Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  *  linux/drivers/acorn/scsi/queue.c: queue handling primitives
0004  *
0005  *  Copyright (C) 1997-2000 Russell King
0006  *
0007  *  Changelog:
0008  *   15-Sep-1997 RMK    Created.
0009  *   11-Oct-1997 RMK    Corrected problem with queue_remove_exclude
0010  *          not updating internal linked list properly
0011  *          (was causing commands to go missing).
0012  *   30-Aug-2000 RMK    Use Linux list handling and spinlocks
0013  */
0014 #include <linux/module.h>
0015 #include <linux/blkdev.h>
0016 #include <linux/kernel.h>
0017 #include <linux/string.h>
0018 #include <linux/slab.h>
0019 #include <linux/spinlock.h>
0020 #include <linux/list.h>
0021 #include <linux/init.h>
0022 
0023 #include <scsi/scsi.h>
0024 #include <scsi/scsi_cmnd.h>
0025 #include <scsi/scsi_device.h>
0026 #include <scsi/scsi_eh.h>
0027 #include <scsi/scsi_tcq.h>
0028 
0029 #define DEBUG
0030 
0031 typedef struct queue_entry {
0032     struct list_head   list;
0033     struct scsi_cmnd   *SCpnt;
0034 #ifdef DEBUG
0035     unsigned long      magic;
0036 #endif
0037 } QE_t;
0038 
0039 #ifdef DEBUG
0040 #define QUEUE_MAGIC_FREE    0xf7e1c9a3
0041 #define QUEUE_MAGIC_USED    0xf7e1cc33
0042 
0043 #define SET_MAGIC(q,m)  ((q)->magic = (m))
0044 #define BAD_MAGIC(q,m)  ((q)->magic != (m))
0045 #else
0046 #define SET_MAGIC(q,m)  do { } while (0)
0047 #define BAD_MAGIC(q,m)  (0)
0048 #endif
0049 
0050 #include "queue.h"
0051 
0052 #define NR_QE   32
0053 
0054 /*
0055  * Function: void queue_initialise (Queue_t *queue)
0056  * Purpose : initialise a queue
0057  * Params  : queue - queue to initialise
0058  */
0059 int queue_initialise (Queue_t *queue)
0060 {
0061     unsigned int nqueues = NR_QE;
0062     QE_t *q;
0063 
0064     spin_lock_init(&queue->queue_lock);
0065     INIT_LIST_HEAD(&queue->head);
0066     INIT_LIST_HEAD(&queue->free);
0067 
0068     /*
0069      * If life was easier, then SCpnt would have a
0070      * host-available list head, and we wouldn't
0071      * need to keep free lists or allocate this
0072      * memory.
0073      */
0074     queue->alloc = q = kmalloc_array(nqueues, sizeof(QE_t), GFP_KERNEL);
0075     if (q) {
0076         for (; nqueues; q++, nqueues--) {
0077             SET_MAGIC(q, QUEUE_MAGIC_FREE);
0078             q->SCpnt = NULL;
0079             list_add(&q->list, &queue->free);
0080         }
0081     }
0082 
0083     return queue->alloc != NULL;
0084 }
0085 
0086 /*
0087  * Function: void queue_free (Queue_t *queue)
0088  * Purpose : free a queue
0089  * Params  : queue - queue to free
0090  */
0091 void queue_free (Queue_t *queue)
0092 {
0093     if (!list_empty(&queue->head))
0094         printk(KERN_WARNING "freeing non-empty queue %p\n", queue);
0095     kfree(queue->alloc);
0096 }
0097      
0098 
0099 /*
0100  * Function: int __queue_add(Queue_t *queue, struct scsi_cmnd *SCpnt, int head)
0101  * Purpose : Add a new command onto a queue, adding REQUEST_SENSE to head.
0102  * Params  : queue - destination queue
0103  *       SCpnt - command to add
0104  *       head  - add command to head of queue
0105  * Returns : 0 on error, !0 on success
0106  */
0107 int __queue_add(Queue_t *queue, struct scsi_cmnd *SCpnt, int head)
0108 {
0109     unsigned long flags;
0110     struct list_head *l;
0111     QE_t *q;
0112     int ret = 0;
0113 
0114     spin_lock_irqsave(&queue->queue_lock, flags);
0115     if (list_empty(&queue->free))
0116         goto empty;
0117 
0118     l = queue->free.next;
0119     list_del(l);
0120 
0121     q = list_entry(l, QE_t, list);
0122     BUG_ON(BAD_MAGIC(q, QUEUE_MAGIC_FREE));
0123 
0124     SET_MAGIC(q, QUEUE_MAGIC_USED);
0125     q->SCpnt = SCpnt;
0126 
0127     if (head)
0128         list_add(l, &queue->head);
0129     else
0130         list_add_tail(l, &queue->head);
0131 
0132     ret = 1;
0133 empty:
0134     spin_unlock_irqrestore(&queue->queue_lock, flags);
0135     return ret;
0136 }
0137 
0138 static struct scsi_cmnd *__queue_remove(Queue_t *queue, struct list_head *ent)
0139 {
0140     QE_t *q;
0141 
0142     /*
0143      * Move the entry from the "used" list onto the "free" list
0144      */
0145     list_del(ent);
0146     q = list_entry(ent, QE_t, list);
0147     BUG_ON(BAD_MAGIC(q, QUEUE_MAGIC_USED));
0148 
0149     SET_MAGIC(q, QUEUE_MAGIC_FREE);
0150     list_add(ent, &queue->free);
0151 
0152     return q->SCpnt;
0153 }
0154 
0155 /*
0156  * Function: struct scsi_cmnd *queue_remove_exclude (queue, exclude)
0157  * Purpose : remove a SCSI command from a queue
0158  * Params  : queue   - queue to remove command from
0159  *       exclude - bit array of target&lun which is busy
0160  * Returns : struct scsi_cmnd if successful (and a reference), or NULL if no command available
0161  */
0162 struct scsi_cmnd *queue_remove_exclude(Queue_t *queue, unsigned long *exclude)
0163 {
0164     unsigned long flags;
0165     struct list_head *l;
0166     struct scsi_cmnd *SCpnt = NULL;
0167 
0168     spin_lock_irqsave(&queue->queue_lock, flags);
0169     list_for_each(l, &queue->head) {
0170         QE_t *q = list_entry(l, QE_t, list);
0171         if (!test_bit(q->SCpnt->device->id * 8 +
0172                   (u8)(q->SCpnt->device->lun & 0x7), exclude)) {
0173             SCpnt = __queue_remove(queue, l);
0174             break;
0175         }
0176     }
0177     spin_unlock_irqrestore(&queue->queue_lock, flags);
0178 
0179     return SCpnt;
0180 }
0181 
0182 /*
0183  * Function: struct scsi_cmnd *queue_remove (queue)
0184  * Purpose : removes first SCSI command from a queue
0185  * Params  : queue   - queue to remove command from
0186  * Returns : struct scsi_cmnd if successful (and a reference), or NULL if no command available
0187  */
0188 struct scsi_cmnd *queue_remove(Queue_t *queue)
0189 {
0190     unsigned long flags;
0191     struct scsi_cmnd *SCpnt = NULL;
0192 
0193     spin_lock_irqsave(&queue->queue_lock, flags);
0194     if (!list_empty(&queue->head))
0195         SCpnt = __queue_remove(queue, queue->head.next);
0196     spin_unlock_irqrestore(&queue->queue_lock, flags);
0197 
0198     return SCpnt;
0199 }
0200 
0201 /*
0202  * Function: struct scsi_cmnd *queue_remove_tgtluntag (queue, target, lun, tag)
0203  * Purpose : remove a SCSI command from the queue for a specified target/lun/tag
0204  * Params  : queue  - queue to remove command from
0205  *       target - target that we want
0206  *       lun    - lun on device
0207  *       tag    - tag on device
0208  * Returns : struct scsi_cmnd if successful, or NULL if no command satisfies requirements
0209  */
0210 struct scsi_cmnd *queue_remove_tgtluntag(Queue_t *queue, int target, int lun,
0211                      int tag)
0212 {
0213     unsigned long flags;
0214     struct list_head *l;
0215     struct scsi_cmnd *SCpnt = NULL;
0216 
0217     spin_lock_irqsave(&queue->queue_lock, flags);
0218     list_for_each(l, &queue->head) {
0219         QE_t *q = list_entry(l, QE_t, list);
0220         if (q->SCpnt->device->id == target && q->SCpnt->device->lun == lun &&
0221             scsi_cmd_to_rq(q->SCpnt)->tag == tag) {
0222             SCpnt = __queue_remove(queue, l);
0223             break;
0224         }
0225     }
0226     spin_unlock_irqrestore(&queue->queue_lock, flags);
0227 
0228     return SCpnt;
0229 }
0230 
0231 /*
0232  * Function: queue_remove_all_target(queue, target)
0233  * Purpose : remove all SCSI commands from the queue for a specified target
0234  * Params  : queue  - queue to remove command from
0235  *           target - target device id
0236  * Returns : nothing
0237  */
0238 void queue_remove_all_target(Queue_t *queue, int target)
0239 {
0240     unsigned long flags;
0241     struct list_head *l;
0242 
0243     spin_lock_irqsave(&queue->queue_lock, flags);
0244     list_for_each(l, &queue->head) {
0245         QE_t *q = list_entry(l, QE_t, list);
0246         if (q->SCpnt->device->id == target)
0247             __queue_remove(queue, l);
0248     }
0249     spin_unlock_irqrestore(&queue->queue_lock, flags);
0250 }
0251 
0252 /*
0253  * Function: int queue_probetgtlun (queue, target, lun)
0254  * Purpose : check to see if we have a command in the queue for the specified
0255  *       target/lun.
0256  * Params  : queue  - queue to look in
0257  *       target - target we want to probe
0258  *       lun    - lun on target
0259  * Returns : 0 if not found, != 0 if found
0260  */
0261 int queue_probetgtlun (Queue_t *queue, int target, int lun)
0262 {
0263     unsigned long flags;
0264     struct list_head *l;
0265     int found = 0;
0266 
0267     spin_lock_irqsave(&queue->queue_lock, flags);
0268     list_for_each(l, &queue->head) {
0269         QE_t *q = list_entry(l, QE_t, list);
0270         if (q->SCpnt->device->id == target && q->SCpnt->device->lun == lun) {
0271             found = 1;
0272             break;
0273         }
0274     }
0275     spin_unlock_irqrestore(&queue->queue_lock, flags);
0276 
0277     return found;
0278 }
0279 
0280 /*
0281  * Function: int queue_remove_cmd(Queue_t *queue, struct scsi_cmnd *SCpnt)
0282  * Purpose : remove a specific command from the queues
0283  * Params  : queue - queue to look in
0284  *       SCpnt - command to find
0285  * Returns : 0 if not found
0286  */
0287 int queue_remove_cmd(Queue_t *queue, struct scsi_cmnd *SCpnt)
0288 {
0289     unsigned long flags;
0290     struct list_head *l;
0291     int found = 0;
0292 
0293     spin_lock_irqsave(&queue->queue_lock, flags);
0294     list_for_each(l, &queue->head) {
0295         QE_t *q = list_entry(l, QE_t, list);
0296         if (q->SCpnt == SCpnt) {
0297             __queue_remove(queue, l);
0298             found = 1;
0299             break;
0300         }
0301     }
0302     spin_unlock_irqrestore(&queue->queue_lock, flags);
0303 
0304     return found;
0305 }
0306 
0307 EXPORT_SYMBOL(queue_initialise);
0308 EXPORT_SYMBOL(queue_free);
0309 EXPORT_SYMBOL(__queue_add);
0310 EXPORT_SYMBOL(queue_remove);
0311 EXPORT_SYMBOL(queue_remove_exclude);
0312 EXPORT_SYMBOL(queue_remove_tgtluntag);
0313 EXPORT_SYMBOL(queue_remove_cmd);
0314 EXPORT_SYMBOL(queue_remove_all_target);
0315 EXPORT_SYMBOL(queue_probetgtlun);
0316 
0317 MODULE_AUTHOR("Russell King");
0318 MODULE_DESCRIPTION("SCSI command queueing");
0319 MODULE_LICENSE("GPL");