320 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * linux/drivers/acorn/scsi/queue.c: queue handling primitives4 *5 * Copyright (C) 1997-2000 Russell King6 *7 * Changelog:8 * 15-Sep-1997 RMK Created.9 * 11-Oct-1997 RMK Corrected problem with queue_remove_exclude10 * not updating internal linked list properly11 * (was causing commands to go missing).12 * 30-Aug-2000 RMK Use Linux list handling and spinlocks13 */14#include <linux/module.h>15#include <linux/blkdev.h>16#include <linux/kernel.h>17#include <linux/string.h>18#include <linux/slab.h>19#include <linux/spinlock.h>20#include <linux/list.h>21#include <linux/init.h>22 23#include <scsi/scsi.h>24#include <scsi/scsi_cmnd.h>25#include <scsi/scsi_device.h>26#include <scsi/scsi_eh.h>27#include <scsi/scsi_tcq.h>28 29#define DEBUG30 31typedef struct queue_entry {32 struct list_head list;33 struct scsi_cmnd *SCpnt;34#ifdef DEBUG35 unsigned long magic;36#endif37} QE_t;38 39#ifdef DEBUG40#define QUEUE_MAGIC_FREE 0xf7e1c9a341#define QUEUE_MAGIC_USED 0xf7e1cc3342 43#define SET_MAGIC(q,m) ((q)->magic = (m))44#define BAD_MAGIC(q,m) ((q)->magic != (m))45#else46#define SET_MAGIC(q,m) do { } while (0)47#define BAD_MAGIC(q,m) (0)48#endif49 50#include "queue.h"51 52#define NR_QE 3253 54/*55 * Function: void queue_initialise (Queue_t *queue)56 * Purpose : initialise a queue57 * Params : queue - queue to initialise58 */59int queue_initialise (Queue_t *queue)60{61 unsigned int nqueues = NR_QE;62 QE_t *q;63 64 spin_lock_init(&queue->queue_lock);65 INIT_LIST_HEAD(&queue->head);66 INIT_LIST_HEAD(&queue->free);67 68 /*69 * If life was easier, then SCpnt would have a70 * host-available list head, and we wouldn't71 * need to keep free lists or allocate this72 * memory.73 */74 queue->alloc = q = kmalloc_array(nqueues, sizeof(QE_t), GFP_KERNEL);75 if (q) {76 for (; nqueues; q++, nqueues--) {77 SET_MAGIC(q, QUEUE_MAGIC_FREE);78 q->SCpnt = NULL;79 list_add(&q->list, &queue->free);80 }81 }82 83 return queue->alloc != NULL;84}85 86/*87 * Function: void queue_free (Queue_t *queue)88 * Purpose : free a queue89 * Params : queue - queue to free90 */91void queue_free (Queue_t *queue)92{93 if (!list_empty(&queue->head))94 printk(KERN_WARNING "freeing non-empty queue %p\n", queue);95 kfree(queue->alloc);96}97 98 99/*100 * Function: int __queue_add(Queue_t *queue, struct scsi_cmnd *SCpnt, int head)101 * Purpose : Add a new command onto a queue, adding REQUEST_SENSE to head.102 * Params : queue - destination queue103 * SCpnt - command to add104 * head - add command to head of queue105 * Returns : 0 on error, !0 on success106 */107int __queue_add(Queue_t *queue, struct scsi_cmnd *SCpnt, int head)108{109 unsigned long flags;110 struct list_head *l;111 QE_t *q;112 int ret = 0;113 114 spin_lock_irqsave(&queue->queue_lock, flags);115 if (list_empty(&queue->free))116 goto empty;117 118 l = queue->free.next;119 list_del(l);120 121 q = list_entry(l, QE_t, list);122 BUG_ON(BAD_MAGIC(q, QUEUE_MAGIC_FREE));123 124 SET_MAGIC(q, QUEUE_MAGIC_USED);125 q->SCpnt = SCpnt;126 127 if (head)128 list_add(l, &queue->head);129 else130 list_add_tail(l, &queue->head);131 132 ret = 1;133empty:134 spin_unlock_irqrestore(&queue->queue_lock, flags);135 return ret;136}137 138static struct scsi_cmnd *__queue_remove(Queue_t *queue, struct list_head *ent)139{140 QE_t *q;141 142 /*143 * Move the entry from the "used" list onto the "free" list144 */145 list_del(ent);146 q = list_entry(ent, QE_t, list);147 BUG_ON(BAD_MAGIC(q, QUEUE_MAGIC_USED));148 149 SET_MAGIC(q, QUEUE_MAGIC_FREE);150 list_add(ent, &queue->free);151 152 return q->SCpnt;153}154 155/*156 * Function: struct scsi_cmnd *queue_remove_exclude (queue, exclude)157 * Purpose : remove a SCSI command from a queue158 * Params : queue - queue to remove command from159 * exclude - bit array of target&lun which is busy160 * Returns : struct scsi_cmnd if successful (and a reference), or NULL if no command available161 */162struct scsi_cmnd *queue_remove_exclude(Queue_t *queue, unsigned long *exclude)163{164 unsigned long flags;165 struct list_head *l;166 struct scsi_cmnd *SCpnt = NULL;167 168 spin_lock_irqsave(&queue->queue_lock, flags);169 list_for_each(l, &queue->head) {170 QE_t *q = list_entry(l, QE_t, list);171 if (!test_bit(q->SCpnt->device->id * 8 +172 (u8)(q->SCpnt->device->lun & 0x7), exclude)) {173 SCpnt = __queue_remove(queue, l);174 break;175 }176 }177 spin_unlock_irqrestore(&queue->queue_lock, flags);178 179 return SCpnt;180}181 182/*183 * Function: struct scsi_cmnd *queue_remove (queue)184 * Purpose : removes first SCSI command from a queue185 * Params : queue - queue to remove command from186 * Returns : struct scsi_cmnd if successful (and a reference), or NULL if no command available187 */188struct scsi_cmnd *queue_remove(Queue_t *queue)189{190 unsigned long flags;191 struct scsi_cmnd *SCpnt = NULL;192 193 spin_lock_irqsave(&queue->queue_lock, flags);194 if (!list_empty(&queue->head))195 SCpnt = __queue_remove(queue, queue->head.next);196 spin_unlock_irqrestore(&queue->queue_lock, flags);197 198 return SCpnt;199}200 201/*202 * Function: struct scsi_cmnd *queue_remove_tgtluntag (queue, target, lun, tag)203 * Purpose : remove a SCSI command from the queue for a specified target/lun/tag204 * Params : queue - queue to remove command from205 * target - target that we want206 * lun - lun on device207 * tag - tag on device208 * Returns : struct scsi_cmnd if successful, or NULL if no command satisfies requirements209 */210struct scsi_cmnd *queue_remove_tgtluntag(Queue_t *queue, int target, int lun,211 int tag)212{213 unsigned long flags;214 struct list_head *l;215 struct scsi_cmnd *SCpnt = NULL;216 217 spin_lock_irqsave(&queue->queue_lock, flags);218 list_for_each(l, &queue->head) {219 QE_t *q = list_entry(l, QE_t, list);220 if (q->SCpnt->device->id == target && q->SCpnt->device->lun == lun &&221 scsi_cmd_to_rq(q->SCpnt)->tag == tag) {222 SCpnt = __queue_remove(queue, l);223 break;224 }225 }226 spin_unlock_irqrestore(&queue->queue_lock, flags);227 228 return SCpnt;229}230 231/*232 * Function: queue_remove_all_target(queue, target)233 * Purpose : remove all SCSI commands from the queue for a specified target234 * Params : queue - queue to remove command from235 * target - target device id236 * Returns : nothing237 */238void queue_remove_all_target(Queue_t *queue, int target)239{240 unsigned long flags;241 struct list_head *l;242 243 spin_lock_irqsave(&queue->queue_lock, flags);244 list_for_each(l, &queue->head) {245 QE_t *q = list_entry(l, QE_t, list);246 if (q->SCpnt->device->id == target)247 __queue_remove(queue, l);248 }249 spin_unlock_irqrestore(&queue->queue_lock, flags);250}251 252/*253 * Function: int queue_probetgtlun (queue, target, lun)254 * Purpose : check to see if we have a command in the queue for the specified255 * target/lun.256 * Params : queue - queue to look in257 * target - target we want to probe258 * lun - lun on target259 * Returns : 0 if not found, != 0 if found260 */261int queue_probetgtlun (Queue_t *queue, int target, int lun)262{263 unsigned long flags;264 struct list_head *l;265 int found = 0;266 267 spin_lock_irqsave(&queue->queue_lock, flags);268 list_for_each(l, &queue->head) {269 QE_t *q = list_entry(l, QE_t, list);270 if (q->SCpnt->device->id == target && q->SCpnt->device->lun == lun) {271 found = 1;272 break;273 }274 }275 spin_unlock_irqrestore(&queue->queue_lock, flags);276 277 return found;278}279 280/*281 * Function: int queue_remove_cmd(Queue_t *queue, struct scsi_cmnd *SCpnt)282 * Purpose : remove a specific command from the queues283 * Params : queue - queue to look in284 * SCpnt - command to find285 * Returns : 0 if not found286 */287int queue_remove_cmd(Queue_t *queue, struct scsi_cmnd *SCpnt)288{289 unsigned long flags;290 struct list_head *l;291 int found = 0;292 293 spin_lock_irqsave(&queue->queue_lock, flags);294 list_for_each(l, &queue->head) {295 QE_t *q = list_entry(l, QE_t, list);296 if (q->SCpnt == SCpnt) {297 __queue_remove(queue, l);298 found = 1;299 break;300 }301 }302 spin_unlock_irqrestore(&queue->queue_lock, flags);303 304 return found;305}306 307EXPORT_SYMBOL(queue_initialise);308EXPORT_SYMBOL(queue_free);309EXPORT_SYMBOL(__queue_add);310EXPORT_SYMBOL(queue_remove);311EXPORT_SYMBOL(queue_remove_exclude);312EXPORT_SYMBOL(queue_remove_tgtluntag);313EXPORT_SYMBOL(queue_remove_cmd);314EXPORT_SYMBOL(queue_remove_all_target);315EXPORT_SYMBOL(queue_probetgtlun);316 317MODULE_AUTHOR("Russell King");318MODULE_DESCRIPTION("SCSI command queueing");319MODULE_LICENSE("GPL");320