@@ -1949,6 +1949,19 @@ union megasas_frame {
u8 raw_bytes[64];
};
+#define MEGASAS_RECENT_IO_BITS 7
+#define MEGASAS_RECENT_IO (1 << MEGASAS_RECENT_IO_BITS)
+struct megasas_seq_io_tracker {
+ /* Used to track sequential IO so it can be skipped */
+ struct hlist_node hash;
+ struct list_head lru;
+
+ unsigned long jiffies;
+ unsigned int sequential;
+ sector_t last;
+};
+
+
/**
* struct MR_PRIV_DEVICE - sdev private hostdata
* @is_tm_capable: firmware managed tm capable flag
@@ -1959,6 +1972,13 @@ struct MR_PRIV_DEVICE {
bool tm_busy;
atomic_t r1_ldio_hint;
u8 interface_type;
+ /* For tracking sequential IO */
+ struct megasas_seq_io_tracker io[MEGASAS_RECENT_IO];
+ struct hlist_head io_hash[MEGASAS_RECENT_IO + 1];
+ struct list_head io_lru;
+ spinlock_t io_lock;
+ unsigned int sequential_cutoff;
+ unsigned int sequential_io;
};
struct megasas_cmd;
@@ -2548,6 +2568,7 @@ struct megasas_instance {
u32 fw_support_ieee;
atomic_t fw_outstanding;
+ atomic_t total_seq_io;
atomic_t ldio_outstanding;
atomic_t fw_reset_no_pci_access;
atomic_t ieee_sgl;
@@ -50,6 +50,7 @@
#include <linux/blkdev.h>
#include <linux/mutex.h>
#include <linux/poll.h>
+#include <linux/hash.h>
#include <scsi/scsi.h>
#include <scsi/scsi_cmnd.h>
@@ -1868,6 +1869,94 @@ out_return_cmd:
return SCSI_MLQUEUE_HOST_BUSY;
}
+static void add_sequential(struct MR_PRIV_DEVICE *mr_device_priv_data)
+{
+ mr_device_priv_data->sequential_io = 0;
+}
+static struct hlist_head *megasas_iohash(struct MR_PRIV_DEVICE *data,
+ uint64_t k)
+{
+ return &data->io_hash[hash_64(k, MEGASAS_RECENT_IO_BITS)];
+}
+
+ /**
+ * megasas_detect_seq_stream - Detect sequential IO per pattern
using hash table per scsi device.
+ * Each scsi device will have hash
table in private data.
+ * This function will iterate/update
hash table and find out if
+ * recent BIO is a sequence of any
BIO looking at io tracker history.
+ *
+ * This function is a referenced from
<bcache>
+ * @drivers/md/bcache/request.c
check_should_bypass().
+ *
+ * Current megaraid_sas driver use similar
logic without hash table.
+ * It is array based searched in for loop.
Not efficient compare.
+ *
+ *
http://marc.info/?l=linux-scsi&m=148245616108288&w=2
+ *
+ * @instance: Adapter soft state
+ * @scmd: SCSI command to be queued
+ */
+static void
+megasas_detect_seq_stream(struct megasas_instance *instance,
+ struct scsi_cmnd *scmd)
+{
+
+ struct MR_PRIV_DEVICE *mr_device_priv_data;
+ struct megasas_seq_io_tracker *i;
+ unsigned int sectors;
+ struct bio *bio;
+ unsigned long flags;
+
+ mr_device_priv_data = scmd->device->hostdata;
+
+ if (!mr_device_priv_data ||
!mr_device_priv_data->sequential_cutoff)
+ return;
+
+ if (scmd->request &&
+ !scmd->request->bio)
+ return;
+
+ bio = scmd->request->bio;
+
+ spin_lock_irqsave(&mr_device_priv_data->io_lock, flags);
+
+ hlist_for_each_entry(i, megasas_iohash(mr_device_priv_data,
bio->bi_iter.bi_sector), hash)
+ if (i->last == bio->bi_iter.bi_sector &&
+ time_before(jiffies, i->jiffies))
+ goto found;
+
+ i = list_first_entry(&mr_device_priv_data->io_lru,
+ struct megasas_seq_io_tracker, lru);
+
+ /* For every random IO pattern, code will hit here as there is no
relavent
+ * BIO in IO tracker with previous BIO sector and current BIO
sector match
+ */
+ add_sequential(mr_device_priv_data);
+ i->sequential = 0;
+found:
+ if (i->sequential + bio->bi_iter.bi_size > i->sequential)
+ i->sequential += bio->bi_iter.bi_size;
+
+ i->last = bio_end_sector(bio);
+ i->jiffies = jiffies + msecs_to_jiffies(5000);
+ /* megaraid driver/firmware need this information. Pass this down
to fimrware */
+ mr_device_priv_data->sequential_io = i->sequential;
+
+ hlist_del(&i->hash);
+ hlist_add_head(&i->hash,
+ megasas_iohash(mr_device_priv_data, i->last));
+ list_move_tail(&i->lru, &mr_device_priv_data->io_lru);