xref: /openbmc/linux/drivers/scsi/arm/queue.c (revision 4f2c0a4acffbec01079c28f839422e64ddeff004)
1d2912cb1SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
21da177e4SLinus Torvalds /*
31da177e4SLinus Torvalds  *  linux/drivers/acorn/scsi/queue.c: queue handling primitives
41da177e4SLinus Torvalds  *
51da177e4SLinus Torvalds  *  Copyright (C) 1997-2000 Russell King
61da177e4SLinus Torvalds  *
71da177e4SLinus Torvalds  *  Changelog:
81da177e4SLinus Torvalds  *   15-Sep-1997 RMK	Created.
91da177e4SLinus Torvalds  *   11-Oct-1997 RMK	Corrected problem with queue_remove_exclude
101da177e4SLinus Torvalds  *			not updating internal linked list properly
111da177e4SLinus Torvalds  *			(was causing commands to go missing).
121da177e4SLinus Torvalds  *   30-Aug-2000 RMK	Use Linux list handling and spinlocks
131da177e4SLinus Torvalds  */
141da177e4SLinus Torvalds #include <linux/module.h>
151da177e4SLinus Torvalds #include <linux/blkdev.h>
161da177e4SLinus Torvalds #include <linux/kernel.h>
171da177e4SLinus Torvalds #include <linux/string.h>
181da177e4SLinus Torvalds #include <linux/slab.h>
191da177e4SLinus Torvalds #include <linux/spinlock.h>
201da177e4SLinus Torvalds #include <linux/list.h>
211da177e4SLinus Torvalds #include <linux/init.h>
221da177e4SLinus Torvalds 
23*53555fb7SBart Van Assche #include <scsi/scsi.h>
24*53555fb7SBart Van Assche #include <scsi/scsi_cmnd.h>
25*53555fb7SBart Van Assche #include <scsi/scsi_device.h>
26*53555fb7SBart Van Assche #include <scsi/scsi_eh.h>
27*53555fb7SBart Van Assche #include <scsi/scsi_tcq.h>
281da177e4SLinus Torvalds 
291da177e4SLinus Torvalds #define DEBUG
301da177e4SLinus Torvalds 
311da177e4SLinus Torvalds typedef struct queue_entry {
321da177e4SLinus Torvalds 	struct list_head   list;
33ee0ca6baSHenne 	struct scsi_cmnd   *SCpnt;
341da177e4SLinus Torvalds #ifdef DEBUG
351da177e4SLinus Torvalds 	unsigned long	   magic;
361da177e4SLinus Torvalds #endif
371da177e4SLinus Torvalds } QE_t;
381da177e4SLinus Torvalds 
391da177e4SLinus Torvalds #ifdef DEBUG
401da177e4SLinus Torvalds #define QUEUE_MAGIC_FREE	0xf7e1c9a3
411da177e4SLinus Torvalds #define QUEUE_MAGIC_USED	0xf7e1cc33
421da177e4SLinus Torvalds 
431da177e4SLinus Torvalds #define SET_MAGIC(q,m)	((q)->magic = (m))
441da177e4SLinus Torvalds #define BAD_MAGIC(q,m)	((q)->magic != (m))
451da177e4SLinus Torvalds #else
461da177e4SLinus Torvalds #define SET_MAGIC(q,m)	do { } while (0)
471da177e4SLinus Torvalds #define BAD_MAGIC(q,m)	(0)
481da177e4SLinus Torvalds #endif
491da177e4SLinus Torvalds 
501da177e4SLinus Torvalds #include "queue.h"
511da177e4SLinus Torvalds 
521da177e4SLinus Torvalds #define NR_QE	32
531da177e4SLinus Torvalds 
541da177e4SLinus Torvalds /*
551da177e4SLinus Torvalds  * Function: void queue_initialise (Queue_t *queue)
561da177e4SLinus Torvalds  * Purpose : initialise a queue
571da177e4SLinus Torvalds  * Params  : queue - queue to initialise
581da177e4SLinus Torvalds  */
queue_initialise(Queue_t * queue)591da177e4SLinus Torvalds int queue_initialise (Queue_t *queue)
601da177e4SLinus Torvalds {
611da177e4SLinus Torvalds 	unsigned int nqueues = NR_QE;
621da177e4SLinus Torvalds 	QE_t *q;
631da177e4SLinus Torvalds 
641da177e4SLinus Torvalds 	spin_lock_init(&queue->queue_lock);
651da177e4SLinus Torvalds 	INIT_LIST_HEAD(&queue->head);
661da177e4SLinus Torvalds 	INIT_LIST_HEAD(&queue->free);
671da177e4SLinus Torvalds 
681da177e4SLinus Torvalds 	/*
691da177e4SLinus Torvalds 	 * If life was easier, then SCpnt would have a
701da177e4SLinus Torvalds 	 * host-available list head, and we wouldn't
711da177e4SLinus Torvalds 	 * need to keep free lists or allocate this
721da177e4SLinus Torvalds 	 * memory.
731da177e4SLinus Torvalds 	 */
746da2ec56SKees Cook 	queue->alloc = q = kmalloc_array(nqueues, sizeof(QE_t), GFP_KERNEL);
751da177e4SLinus Torvalds 	if (q) {
761da177e4SLinus Torvalds 		for (; nqueues; q++, nqueues--) {
771da177e4SLinus Torvalds 			SET_MAGIC(q, QUEUE_MAGIC_FREE);
781da177e4SLinus Torvalds 			q->SCpnt = NULL;
791da177e4SLinus Torvalds 			list_add(&q->list, &queue->free);
801da177e4SLinus Torvalds 		}
811da177e4SLinus Torvalds 	}
821da177e4SLinus Torvalds 
831da177e4SLinus Torvalds 	return queue->alloc != NULL;
841da177e4SLinus Torvalds }
851da177e4SLinus Torvalds 
861da177e4SLinus Torvalds /*
871da177e4SLinus Torvalds  * Function: void queue_free (Queue_t *queue)
881da177e4SLinus Torvalds  * Purpose : free a queue
891da177e4SLinus Torvalds  * Params  : queue - queue to free
901da177e4SLinus Torvalds  */
queue_free(Queue_t * queue)911da177e4SLinus Torvalds void queue_free (Queue_t *queue)
921da177e4SLinus Torvalds {
931da177e4SLinus Torvalds 	if (!list_empty(&queue->head))
941da177e4SLinus Torvalds 		printk(KERN_WARNING "freeing non-empty queue %p\n", queue);
951da177e4SLinus Torvalds 	kfree(queue->alloc);
961da177e4SLinus Torvalds }
971da177e4SLinus Torvalds 
981da177e4SLinus Torvalds 
991da177e4SLinus Torvalds /*
100ee0ca6baSHenne  * Function: int __queue_add(Queue_t *queue, struct scsi_cmnd *SCpnt, int head)
1011da177e4SLinus Torvalds  * Purpose : Add a new command onto a queue, adding REQUEST_SENSE to head.
1021da177e4SLinus Torvalds  * Params  : queue - destination queue
1031da177e4SLinus Torvalds  *	     SCpnt - command to add
1041da177e4SLinus Torvalds  *	     head  - add command to head of queue
1051da177e4SLinus Torvalds  * Returns : 0 on error, !0 on success
1061da177e4SLinus Torvalds  */
__queue_add(Queue_t * queue,struct scsi_cmnd * SCpnt,int head)107ee0ca6baSHenne int __queue_add(Queue_t *queue, struct scsi_cmnd *SCpnt, int head)
1081da177e4SLinus Torvalds {
1091da177e4SLinus Torvalds 	unsigned long flags;
1101da177e4SLinus Torvalds 	struct list_head *l;
1111da177e4SLinus Torvalds 	QE_t *q;
1121da177e4SLinus Torvalds 	int ret = 0;
1131da177e4SLinus Torvalds 
1141da177e4SLinus Torvalds 	spin_lock_irqsave(&queue->queue_lock, flags);
1151da177e4SLinus Torvalds 	if (list_empty(&queue->free))
1161da177e4SLinus Torvalds 		goto empty;
1171da177e4SLinus Torvalds 
1181da177e4SLinus Torvalds 	l = queue->free.next;
1191da177e4SLinus Torvalds 	list_del(l);
1201da177e4SLinus Torvalds 
1211da177e4SLinus Torvalds 	q = list_entry(l, QE_t, list);
122125e1874SEric Sesterhenn 	BUG_ON(BAD_MAGIC(q, QUEUE_MAGIC_FREE));
1231da177e4SLinus Torvalds 
1241da177e4SLinus Torvalds 	SET_MAGIC(q, QUEUE_MAGIC_USED);
1251da177e4SLinus Torvalds 	q->SCpnt = SCpnt;
1261da177e4SLinus Torvalds 
1271da177e4SLinus Torvalds 	if (head)
1281da177e4SLinus Torvalds 		list_add(l, &queue->head);
1291da177e4SLinus Torvalds 	else
1301da177e4SLinus Torvalds 		list_add_tail(l, &queue->head);
1311da177e4SLinus Torvalds 
1321da177e4SLinus Torvalds 	ret = 1;
1331da177e4SLinus Torvalds empty:
1341da177e4SLinus Torvalds 	spin_unlock_irqrestore(&queue->queue_lock, flags);
1351da177e4SLinus Torvalds 	return ret;
1361da177e4SLinus Torvalds }
1371da177e4SLinus Torvalds 
__queue_remove(Queue_t * queue,struct list_head * ent)138ee0ca6baSHenne static struct scsi_cmnd *__queue_remove(Queue_t *queue, struct list_head *ent)
1391da177e4SLinus Torvalds {
1401da177e4SLinus Torvalds 	QE_t *q;
1411da177e4SLinus Torvalds 
1421da177e4SLinus Torvalds 	/*
1431da177e4SLinus Torvalds 	 * Move the entry from the "used" list onto the "free" list
1441da177e4SLinus Torvalds 	 */
1451da177e4SLinus Torvalds 	list_del(ent);
1461da177e4SLinus Torvalds 	q = list_entry(ent, QE_t, list);
147125e1874SEric Sesterhenn 	BUG_ON(BAD_MAGIC(q, QUEUE_MAGIC_USED));
1481da177e4SLinus Torvalds 
1491da177e4SLinus Torvalds 	SET_MAGIC(q, QUEUE_MAGIC_FREE);
1501da177e4SLinus Torvalds 	list_add(ent, &queue->free);
1511da177e4SLinus Torvalds 
1521da177e4SLinus Torvalds 	return q->SCpnt;
1531da177e4SLinus Torvalds }
1541da177e4SLinus Torvalds 
1551da177e4SLinus Torvalds /*
156ee0ca6baSHenne  * Function: struct scsi_cmnd *queue_remove_exclude (queue, exclude)
1571da177e4SLinus Torvalds  * Purpose : remove a SCSI command from a queue
1581da177e4SLinus Torvalds  * Params  : queue   - queue to remove command from
1591da177e4SLinus Torvalds  *	     exclude - bit array of target&lun which is busy
160ee0ca6baSHenne  * Returns : struct scsi_cmnd if successful (and a reference), or NULL if no command available
1611da177e4SLinus Torvalds  */
queue_remove_exclude(Queue_t * queue,unsigned long * exclude)162ee0ca6baSHenne struct scsi_cmnd *queue_remove_exclude(Queue_t *queue, unsigned long *exclude)
1631da177e4SLinus Torvalds {
1641da177e4SLinus Torvalds 	unsigned long flags;
1651da177e4SLinus Torvalds 	struct list_head *l;
166ee0ca6baSHenne 	struct scsi_cmnd *SCpnt = NULL;
1671da177e4SLinus Torvalds 
1681da177e4SLinus Torvalds 	spin_lock_irqsave(&queue->queue_lock, flags);
1691da177e4SLinus Torvalds 	list_for_each(l, &queue->head) {
1701da177e4SLinus Torvalds 		QE_t *q = list_entry(l, QE_t, list);
1719cb78c16SHannes Reinecke 		if (!test_bit(q->SCpnt->device->id * 8 +
1729cb78c16SHannes Reinecke 			      (u8)(q->SCpnt->device->lun & 0x7), exclude)) {
1731da177e4SLinus Torvalds 			SCpnt = __queue_remove(queue, l);
1741da177e4SLinus Torvalds 			break;
1751da177e4SLinus Torvalds 		}
1761da177e4SLinus Torvalds 	}
1771da177e4SLinus Torvalds 	spin_unlock_irqrestore(&queue->queue_lock, flags);
1781da177e4SLinus Torvalds 
1791da177e4SLinus Torvalds 	return SCpnt;
1801da177e4SLinus Torvalds }
1811da177e4SLinus Torvalds 
1821da177e4SLinus Torvalds /*
183ee0ca6baSHenne  * Function: struct scsi_cmnd *queue_remove (queue)
1841da177e4SLinus Torvalds  * Purpose : removes first SCSI command from a queue
1851da177e4SLinus Torvalds  * Params  : queue   - queue to remove command from
186ee0ca6baSHenne  * Returns : struct scsi_cmnd if successful (and a reference), or NULL if no command available
1871da177e4SLinus Torvalds  */
queue_remove(Queue_t * queue)188ee0ca6baSHenne struct scsi_cmnd *queue_remove(Queue_t *queue)
1891da177e4SLinus Torvalds {
1901da177e4SLinus Torvalds 	unsigned long flags;
191ee0ca6baSHenne 	struct scsi_cmnd *SCpnt = NULL;
1921da177e4SLinus Torvalds 
1931da177e4SLinus Torvalds 	spin_lock_irqsave(&queue->queue_lock, flags);
1941da177e4SLinus Torvalds 	if (!list_empty(&queue->head))
1951da177e4SLinus Torvalds 		SCpnt = __queue_remove(queue, queue->head.next);
1961da177e4SLinus Torvalds 	spin_unlock_irqrestore(&queue->queue_lock, flags);
1971da177e4SLinus Torvalds 
1981da177e4SLinus Torvalds 	return SCpnt;
1991da177e4SLinus Torvalds }
2001da177e4SLinus Torvalds 
2011da177e4SLinus Torvalds /*
202ee0ca6baSHenne  * Function: struct scsi_cmnd *queue_remove_tgtluntag (queue, target, lun, tag)
2031da177e4SLinus Torvalds  * Purpose : remove a SCSI command from the queue for a specified target/lun/tag
2041da177e4SLinus Torvalds  * Params  : queue  - queue to remove command from
2051da177e4SLinus Torvalds  *	     target - target that we want
2061da177e4SLinus Torvalds  *	     lun    - lun on device
2071da177e4SLinus Torvalds  *	     tag    - tag on device
208ee0ca6baSHenne  * Returns : struct scsi_cmnd if successful, or NULL if no command satisfies requirements
2091da177e4SLinus Torvalds  */
queue_remove_tgtluntag(Queue_t * queue,int target,int lun,int tag)210ee0ca6baSHenne struct scsi_cmnd *queue_remove_tgtluntag(Queue_t *queue, int target, int lun,
211ee0ca6baSHenne 					 int tag)
2121da177e4SLinus Torvalds {
2131da177e4SLinus Torvalds 	unsigned long flags;
2141da177e4SLinus Torvalds 	struct list_head *l;
215ee0ca6baSHenne 	struct scsi_cmnd *SCpnt = NULL;
2161da177e4SLinus Torvalds 
2171da177e4SLinus Torvalds 	spin_lock_irqsave(&queue->queue_lock, flags);
2181da177e4SLinus Torvalds 	list_for_each(l, &queue->head) {
2191da177e4SLinus Torvalds 		QE_t *q = list_entry(l, QE_t, list);
2201da177e4SLinus Torvalds 		if (q->SCpnt->device->id == target && q->SCpnt->device->lun == lun &&
221756fb6a8SHannes Reinecke 		    scsi_cmd_to_rq(q->SCpnt)->tag == tag) {
2221da177e4SLinus Torvalds 			SCpnt = __queue_remove(queue, l);
2231da177e4SLinus Torvalds 			break;
2241da177e4SLinus Torvalds 		}
2251da177e4SLinus Torvalds 	}
2261da177e4SLinus Torvalds 	spin_unlock_irqrestore(&queue->queue_lock, flags);
2271da177e4SLinus Torvalds 
2281da177e4SLinus Torvalds 	return SCpnt;
2291da177e4SLinus Torvalds }
2301da177e4SLinus Torvalds 
2311da177e4SLinus Torvalds /*
2321da177e4SLinus Torvalds  * Function: queue_remove_all_target(queue, target)
2331da177e4SLinus Torvalds  * Purpose : remove all SCSI commands from the queue for a specified target
2341da177e4SLinus Torvalds  * Params  : queue  - queue to remove command from
2351da177e4SLinus Torvalds  *           target - target device id
2361da177e4SLinus Torvalds  * Returns : nothing
2371da177e4SLinus Torvalds  */
queue_remove_all_target(Queue_t * queue,int target)2381da177e4SLinus Torvalds void queue_remove_all_target(Queue_t *queue, int target)
2391da177e4SLinus Torvalds {
2401da177e4SLinus Torvalds 	unsigned long flags;
2411da177e4SLinus Torvalds 	struct list_head *l;
2421da177e4SLinus Torvalds 
2431da177e4SLinus Torvalds 	spin_lock_irqsave(&queue->queue_lock, flags);
2441da177e4SLinus Torvalds 	list_for_each(l, &queue->head) {
2451da177e4SLinus Torvalds 		QE_t *q = list_entry(l, QE_t, list);
2461da177e4SLinus Torvalds 		if (q->SCpnt->device->id == target)
2471da177e4SLinus Torvalds 			__queue_remove(queue, l);
2481da177e4SLinus Torvalds 	}
2491da177e4SLinus Torvalds 	spin_unlock_irqrestore(&queue->queue_lock, flags);
2501da177e4SLinus Torvalds }
2511da177e4SLinus Torvalds 
2521da177e4SLinus Torvalds /*
2531da177e4SLinus Torvalds  * Function: int queue_probetgtlun (queue, target, lun)
2541da177e4SLinus Torvalds  * Purpose : check to see if we have a command in the queue for the specified
2551da177e4SLinus Torvalds  *	     target/lun.
2561da177e4SLinus Torvalds  * Params  : queue  - queue to look in
2571da177e4SLinus Torvalds  *	     target - target we want to probe
2581da177e4SLinus Torvalds  *	     lun    - lun on target
2591da177e4SLinus Torvalds  * Returns : 0 if not found, != 0 if found
2601da177e4SLinus Torvalds  */
queue_probetgtlun(Queue_t * queue,int target,int lun)2611da177e4SLinus Torvalds int queue_probetgtlun (Queue_t *queue, int target, int lun)
2621da177e4SLinus Torvalds {
2631da177e4SLinus Torvalds 	unsigned long flags;
2641da177e4SLinus Torvalds 	struct list_head *l;
2651da177e4SLinus Torvalds 	int found = 0;
2661da177e4SLinus Torvalds 
2671da177e4SLinus Torvalds 	spin_lock_irqsave(&queue->queue_lock, flags);
2681da177e4SLinus Torvalds 	list_for_each(l, &queue->head) {
2691da177e4SLinus Torvalds 		QE_t *q = list_entry(l, QE_t, list);
2701da177e4SLinus Torvalds 		if (q->SCpnt->device->id == target && q->SCpnt->device->lun == lun) {
2711da177e4SLinus Torvalds 			found = 1;
2721da177e4SLinus Torvalds 			break;
2731da177e4SLinus Torvalds 		}
2741da177e4SLinus Torvalds 	}
2751da177e4SLinus Torvalds 	spin_unlock_irqrestore(&queue->queue_lock, flags);
2761da177e4SLinus Torvalds 
2771da177e4SLinus Torvalds 	return found;
2781da177e4SLinus Torvalds }
2791da177e4SLinus Torvalds 
2801da177e4SLinus Torvalds /*
281ee0ca6baSHenne  * Function: int queue_remove_cmd(Queue_t *queue, struct scsi_cmnd *SCpnt)
2821da177e4SLinus Torvalds  * Purpose : remove a specific command from the queues
2831da177e4SLinus Torvalds  * Params  : queue - queue to look in
2841da177e4SLinus Torvalds  *	     SCpnt - command to find
2851da177e4SLinus Torvalds  * Returns : 0 if not found
2861da177e4SLinus Torvalds  */
queue_remove_cmd(Queue_t * queue,struct scsi_cmnd * SCpnt)287ee0ca6baSHenne int queue_remove_cmd(Queue_t *queue, struct scsi_cmnd *SCpnt)
2881da177e4SLinus Torvalds {
2891da177e4SLinus Torvalds 	unsigned long flags;
2901da177e4SLinus Torvalds 	struct list_head *l;
2911da177e4SLinus Torvalds 	int found = 0;
2921da177e4SLinus Torvalds 
2931da177e4SLinus Torvalds 	spin_lock_irqsave(&queue->queue_lock, flags);
2941da177e4SLinus Torvalds 	list_for_each(l, &queue->head) {
2951da177e4SLinus Torvalds 		QE_t *q = list_entry(l, QE_t, list);
2961da177e4SLinus Torvalds 		if (q->SCpnt == SCpnt) {
2971da177e4SLinus Torvalds 			__queue_remove(queue, l);
2981da177e4SLinus Torvalds 			found = 1;
2991da177e4SLinus Torvalds 			break;
3001da177e4SLinus Torvalds 		}
3011da177e4SLinus Torvalds 	}
3021da177e4SLinus Torvalds 	spin_unlock_irqrestore(&queue->queue_lock, flags);
3031da177e4SLinus Torvalds 
3041da177e4SLinus Torvalds 	return found;
3051da177e4SLinus Torvalds }
3061da177e4SLinus Torvalds 
3071da177e4SLinus Torvalds EXPORT_SYMBOL(queue_initialise);
3081da177e4SLinus Torvalds EXPORT_SYMBOL(queue_free);
3091da177e4SLinus Torvalds EXPORT_SYMBOL(__queue_add);
3101da177e4SLinus Torvalds EXPORT_SYMBOL(queue_remove);
3111da177e4SLinus Torvalds EXPORT_SYMBOL(queue_remove_exclude);
3121da177e4SLinus Torvalds EXPORT_SYMBOL(queue_remove_tgtluntag);
3131da177e4SLinus Torvalds EXPORT_SYMBOL(queue_remove_cmd);
3141da177e4SLinus Torvalds EXPORT_SYMBOL(queue_remove_all_target);
3151da177e4SLinus Torvalds EXPORT_SYMBOL(queue_probetgtlun);
3161da177e4SLinus Torvalds 
3171da177e4SLinus Torvalds MODULE_AUTHOR("Russell King");
3181da177e4SLinus Torvalds MODULE_DESCRIPTION("SCSI command queueing");
3191da177e4SLinus Torvalds MODULE_LICENSE("GPL");
320