block: support different tag allocation policy
authorShaohua Li <shli@fb.com>
Fri, 16 Jan 2015 01:32:25 +0000 (17:32 -0800)
committerJens Axboe <axboe@fb.com>
Fri, 23 Jan 2015 21:15:46 +0000 (14:15 -0700)
The libata tag allocation is using a round-robin policy. Next patch will
make libata use block generic tag allocation, so let's add a policy to
tag allocation.

Currently two policies: FIFO (default) and round-robin.

Cc: Jens Axboe <axboe@fb.com>
Cc: Tejun Heo <tj@kernel.org>
Cc: Christoph Hellwig <hch@infradead.org>
Signed-off-by: Shaohua Li <shli@fb.com>
Signed-off-by: Jens Axboe <axboe@fb.com>
block/blk-tag.c
drivers/block/osdblk.c
drivers/scsi/scsi_scan.c
include/linux/blkdev.h
include/scsi/scsi_host.h
include/scsi/scsi_tcq.h

index a185b86741e5ff80dccbc5223eb570f764199d86..f0344e6939d537ecaac05c21a0542c46d37d2808 100644 (file)
@@ -119,7 +119,7 @@ fail:
 }
 
 static struct blk_queue_tag *__blk_queue_init_tags(struct request_queue *q,
-                                                  int depth)
+                                               int depth, int alloc_policy)
 {
        struct blk_queue_tag *tags;
 
@@ -131,6 +131,8 @@ static struct blk_queue_tag *__blk_queue_init_tags(struct request_queue *q,
                goto fail;
 
        atomic_set(&tags->refcnt, 1);
+       tags->alloc_policy = alloc_policy;
+       tags->next_tag = 0;
        return tags;
 fail:
        kfree(tags);
@@ -140,10 +142,11 @@ fail:
 /**
  * blk_init_tags - initialize the tag info for an external tag map
  * @depth:     the maximum queue depth supported
+ * @alloc_policy: tag allocation policy
  **/
-struct blk_queue_tag *blk_init_tags(int depth)
+struct blk_queue_tag *blk_init_tags(int depth, int alloc_policy)
 {
-       return __blk_queue_init_tags(NULL, depth);
+       return __blk_queue_init_tags(NULL, depth, alloc_policy);
 }
 EXPORT_SYMBOL(blk_init_tags);
 
@@ -152,19 +155,20 @@ EXPORT_SYMBOL(blk_init_tags);
  * @q:  the request queue for the device
  * @depth:  the maximum queue depth supported
  * @tags: the tag to use
+ * @alloc_policy: tag allocation policy
  *
  * Queue lock must be held here if the function is called to resize an
  * existing map.
  **/
 int blk_queue_init_tags(struct request_queue *q, int depth,
-                       struct blk_queue_tag *tags)
+                       struct blk_queue_tag *tags, int alloc_policy)
 {
        int rc;
 
        BUG_ON(tags && q->queue_tags && tags != q->queue_tags);
 
        if (!tags && !q->queue_tags) {
-               tags = __blk_queue_init_tags(q, depth);
+               tags = __blk_queue_init_tags(q, depth, alloc_policy);
 
                if (!tags)
                        return -ENOMEM;
@@ -344,9 +348,21 @@ int blk_queue_start_tag(struct request_queue *q, struct request *rq)
        }
 
        do {
-               tag = find_first_zero_bit(bqt->tag_map, max_depth);
-               if (tag >= max_depth)
-                       return 1;
+               if (bqt->alloc_policy == BLK_TAG_ALLOC_FIFO) {
+                       tag = find_first_zero_bit(bqt->tag_map, max_depth);
+                       if (tag >= max_depth)
+                               return 1;
+               } else {
+                       int start = bqt->next_tag;
+                       int size = min_t(int, bqt->max_depth, max_depth + start);
+                       tag = find_next_zero_bit(bqt->tag_map, size, start);
+                       if (tag >= size && start + size > bqt->max_depth) {
+                               size = start + size - bqt->max_depth;
+                               tag = find_first_zero_bit(bqt->tag_map, size);
+                       }
+                       if (tag >= size)
+                               return 1;
+               }
 
        } while (test_and_set_bit_lock(tag, bqt->tag_map));
        /*
@@ -354,6 +370,7 @@ int blk_queue_start_tag(struct request_queue *q, struct request *rq)
         * See blk_queue_end_tag for details.
         */
 
+       bqt->next_tag = (tag + 1) % bqt->max_depth;
        rq->cmd_flags |= REQ_QUEUED;
        rq->tag = tag;
        bqt->tag_index[tag] = rq;
index 79aa179305b5e6fc5a84c5a7e80506626cb26e38..e229425962074cf358ee234b853af452435495ea 100644 (file)
@@ -423,7 +423,7 @@ static int osdblk_init_disk(struct osdblk_device *osdev)
        }
 
        /* switch queue to TCQ mode; allocate tag map */
-       rc = blk_queue_init_tags(q, OSDBLK_MAX_REQ, NULL);
+       rc = blk_queue_init_tags(q, OSDBLK_MAX_REQ, NULL, BLK_TAG_ALLOC_FIFO);
        if (rc) {
                blk_cleanup_queue(q);
                put_disk(disk);
index 983aed10ff2f8d97c9d34254768ecd3ec819ab9b..921a8c897eb2bdb6644bf3cc7666c096f3638968 100644 (file)
@@ -290,7 +290,8 @@ static struct scsi_device *scsi_alloc_sdev(struct scsi_target *starget,
        if (!shost_use_blk_mq(sdev->host) &&
            (shost->bqt || shost->hostt->use_blk_tags)) {
                blk_queue_init_tags(sdev->request_queue,
-                                   sdev->host->cmd_per_lun, shost->bqt);
+                                   sdev->host->cmd_per_lun, shost->bqt,
+                                   shost->hostt->tag_alloc_policy);
        }
        scsi_change_queue_depth(sdev, sdev->host->cmd_per_lun);
 
index 4c4b732d75566eb6b3e99b69a4d46415bf7c0219..6f388fd1c11c1c6775e935b0dd9816a8a1d0e34a 100644 (file)
@@ -272,7 +272,11 @@ struct blk_queue_tag {
        int max_depth;                  /* what we will send to device */
        int real_max_depth;             /* what the array can hold */
        atomic_t refcnt;                /* map can be shared */
+       int alloc_policy;               /* tag allocation policy */
+       int next_tag;                   /* next tag */
 };
+#define BLK_TAG_ALLOC_FIFO 0 /* allocate starting from 0 */
+#define BLK_TAG_ALLOC_RR 1 /* allocate starting from last allocated tag */
 
 #define BLK_SCSI_MAX_CMDS      (256)
 #define BLK_SCSI_CMD_PER_LONG  (BLK_SCSI_MAX_CMDS / (sizeof(long) * 8))
@@ -1139,11 +1143,11 @@ static inline bool blk_needs_flush_plug(struct task_struct *tsk)
 extern int blk_queue_start_tag(struct request_queue *, struct request *);
 extern struct request *blk_queue_find_tag(struct request_queue *, int);
 extern void blk_queue_end_tag(struct request_queue *, struct request *);
-extern int blk_queue_init_tags(struct request_queue *, int, struct blk_queue_tag *);
+extern int blk_queue_init_tags(struct request_queue *, int, struct blk_queue_tag *, int);
 extern void blk_queue_free_tags(struct request_queue *);
 extern int blk_queue_resize_tags(struct request_queue *, int);
 extern void blk_queue_invalidate_tags(struct request_queue *);
-extern struct blk_queue_tag *blk_init_tags(int);
+extern struct blk_queue_tag *blk_init_tags(int, int);
 extern void blk_free_tags(struct blk_queue_tag *);
 
 static inline struct request *blk_map_queue_find_tag(struct blk_queue_tag *bqt,
index 019e66858ce64a3cc2e6e990cd26e52669e8a2bb..e113c757d555ef097954a4b41137f4d4b5a8f4e1 100644 (file)
@@ -402,6 +402,9 @@ struct scsi_host_template {
         */
        unsigned char present;
 
+       /* If use block layer to manage tags, this is tag allocation policy */
+       int tag_alloc_policy;
+
        /*
         * Let the block layer assigns tags to all commands.
         */
index 9708b28bd2aa1bf1ca53a044eefef563b4a5b8fc..b27977e8aaed9b12bf6497fbdf01879a47b26cbc 100644 (file)
@@ -66,7 +66,8 @@ static inline int scsi_init_shared_tag_map(struct Scsi_Host *shost, int depth)
         * devices on the shared host (for libata)
         */
        if (!shost->bqt) {
-               shost->bqt = blk_init_tags(depth);
+               shost->bqt = blk_init_tags(depth,
+                       shost->hostt->tag_alloc_policy);
                if (!shost->bqt)
                        return -ENOMEM;
        }