xref: /OK3568_Linux_fs/kernel/drivers/scsi/arm/queue.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0-only
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun  *  linux/drivers/acorn/scsi/queue.c: queue handling primitives
4*4882a593Smuzhiyun  *
5*4882a593Smuzhiyun  *  Copyright (C) 1997-2000 Russell King
6*4882a593Smuzhiyun  *
7*4882a593Smuzhiyun  *  Changelog:
8*4882a593Smuzhiyun  *   15-Sep-1997 RMK	Created.
9*4882a593Smuzhiyun  *   11-Oct-1997 RMK	Corrected problem with queue_remove_exclude
10*4882a593Smuzhiyun  *			not updating internal linked list properly
11*4882a593Smuzhiyun  *			(was causing commands to go missing).
12*4882a593Smuzhiyun  *   30-Aug-2000 RMK	Use Linux list handling and spinlocks
13*4882a593Smuzhiyun  */
14*4882a593Smuzhiyun #include <linux/module.h>
15*4882a593Smuzhiyun #include <linux/blkdev.h>
16*4882a593Smuzhiyun #include <linux/kernel.h>
17*4882a593Smuzhiyun #include <linux/string.h>
18*4882a593Smuzhiyun #include <linux/slab.h>
19*4882a593Smuzhiyun #include <linux/spinlock.h>
20*4882a593Smuzhiyun #include <linux/list.h>
21*4882a593Smuzhiyun #include <linux/init.h>
22*4882a593Smuzhiyun 
23*4882a593Smuzhiyun #include "../scsi.h"
24*4882a593Smuzhiyun 
25*4882a593Smuzhiyun #define DEBUG
26*4882a593Smuzhiyun 
27*4882a593Smuzhiyun typedef struct queue_entry {
28*4882a593Smuzhiyun 	struct list_head   list;
29*4882a593Smuzhiyun 	struct scsi_cmnd   *SCpnt;
30*4882a593Smuzhiyun #ifdef DEBUG
31*4882a593Smuzhiyun 	unsigned long	   magic;
32*4882a593Smuzhiyun #endif
33*4882a593Smuzhiyun } QE_t;
34*4882a593Smuzhiyun 
35*4882a593Smuzhiyun #ifdef DEBUG
36*4882a593Smuzhiyun #define QUEUE_MAGIC_FREE	0xf7e1c9a3
37*4882a593Smuzhiyun #define QUEUE_MAGIC_USED	0xf7e1cc33
38*4882a593Smuzhiyun 
39*4882a593Smuzhiyun #define SET_MAGIC(q,m)	((q)->magic = (m))
40*4882a593Smuzhiyun #define BAD_MAGIC(q,m)	((q)->magic != (m))
41*4882a593Smuzhiyun #else
42*4882a593Smuzhiyun #define SET_MAGIC(q,m)	do { } while (0)
43*4882a593Smuzhiyun #define BAD_MAGIC(q,m)	(0)
44*4882a593Smuzhiyun #endif
45*4882a593Smuzhiyun 
46*4882a593Smuzhiyun #include "queue.h"
47*4882a593Smuzhiyun 
48*4882a593Smuzhiyun #define NR_QE	32
49*4882a593Smuzhiyun 
50*4882a593Smuzhiyun /*
51*4882a593Smuzhiyun  * Function: void queue_initialise (Queue_t *queue)
52*4882a593Smuzhiyun  * Purpose : initialise a queue
53*4882a593Smuzhiyun  * Params  : queue - queue to initialise
54*4882a593Smuzhiyun  */
queue_initialise(Queue_t * queue)55*4882a593Smuzhiyun int queue_initialise (Queue_t *queue)
56*4882a593Smuzhiyun {
57*4882a593Smuzhiyun 	unsigned int nqueues = NR_QE;
58*4882a593Smuzhiyun 	QE_t *q;
59*4882a593Smuzhiyun 
60*4882a593Smuzhiyun 	spin_lock_init(&queue->queue_lock);
61*4882a593Smuzhiyun 	INIT_LIST_HEAD(&queue->head);
62*4882a593Smuzhiyun 	INIT_LIST_HEAD(&queue->free);
63*4882a593Smuzhiyun 
64*4882a593Smuzhiyun 	/*
65*4882a593Smuzhiyun 	 * If life was easier, then SCpnt would have a
66*4882a593Smuzhiyun 	 * host-available list head, and we wouldn't
67*4882a593Smuzhiyun 	 * need to keep free lists or allocate this
68*4882a593Smuzhiyun 	 * memory.
69*4882a593Smuzhiyun 	 */
70*4882a593Smuzhiyun 	queue->alloc = q = kmalloc_array(nqueues, sizeof(QE_t), GFP_KERNEL);
71*4882a593Smuzhiyun 	if (q) {
72*4882a593Smuzhiyun 		for (; nqueues; q++, nqueues--) {
73*4882a593Smuzhiyun 			SET_MAGIC(q, QUEUE_MAGIC_FREE);
74*4882a593Smuzhiyun 			q->SCpnt = NULL;
75*4882a593Smuzhiyun 			list_add(&q->list, &queue->free);
76*4882a593Smuzhiyun 		}
77*4882a593Smuzhiyun 	}
78*4882a593Smuzhiyun 
79*4882a593Smuzhiyun 	return queue->alloc != NULL;
80*4882a593Smuzhiyun }
81*4882a593Smuzhiyun 
82*4882a593Smuzhiyun /*
83*4882a593Smuzhiyun  * Function: void queue_free (Queue_t *queue)
84*4882a593Smuzhiyun  * Purpose : free a queue
85*4882a593Smuzhiyun  * Params  : queue - queue to free
86*4882a593Smuzhiyun  */
queue_free(Queue_t * queue)87*4882a593Smuzhiyun void queue_free (Queue_t *queue)
88*4882a593Smuzhiyun {
89*4882a593Smuzhiyun 	if (!list_empty(&queue->head))
90*4882a593Smuzhiyun 		printk(KERN_WARNING "freeing non-empty queue %p\n", queue);
91*4882a593Smuzhiyun 	kfree(queue->alloc);
92*4882a593Smuzhiyun }
93*4882a593Smuzhiyun 
94*4882a593Smuzhiyun 
95*4882a593Smuzhiyun /*
96*4882a593Smuzhiyun  * Function: int __queue_add(Queue_t *queue, struct scsi_cmnd *SCpnt, int head)
97*4882a593Smuzhiyun  * Purpose : Add a new command onto a queue, adding REQUEST_SENSE to head.
98*4882a593Smuzhiyun  * Params  : queue - destination queue
99*4882a593Smuzhiyun  *	     SCpnt - command to add
100*4882a593Smuzhiyun  *	     head  - add command to head of queue
101*4882a593Smuzhiyun  * Returns : 0 on error, !0 on success
102*4882a593Smuzhiyun  */
__queue_add(Queue_t * queue,struct scsi_cmnd * SCpnt,int head)103*4882a593Smuzhiyun int __queue_add(Queue_t *queue, struct scsi_cmnd *SCpnt, int head)
104*4882a593Smuzhiyun {
105*4882a593Smuzhiyun 	unsigned long flags;
106*4882a593Smuzhiyun 	struct list_head *l;
107*4882a593Smuzhiyun 	QE_t *q;
108*4882a593Smuzhiyun 	int ret = 0;
109*4882a593Smuzhiyun 
110*4882a593Smuzhiyun 	spin_lock_irqsave(&queue->queue_lock, flags);
111*4882a593Smuzhiyun 	if (list_empty(&queue->free))
112*4882a593Smuzhiyun 		goto empty;
113*4882a593Smuzhiyun 
114*4882a593Smuzhiyun 	l = queue->free.next;
115*4882a593Smuzhiyun 	list_del(l);
116*4882a593Smuzhiyun 
117*4882a593Smuzhiyun 	q = list_entry(l, QE_t, list);
118*4882a593Smuzhiyun 	BUG_ON(BAD_MAGIC(q, QUEUE_MAGIC_FREE));
119*4882a593Smuzhiyun 
120*4882a593Smuzhiyun 	SET_MAGIC(q, QUEUE_MAGIC_USED);
121*4882a593Smuzhiyun 	q->SCpnt = SCpnt;
122*4882a593Smuzhiyun 
123*4882a593Smuzhiyun 	if (head)
124*4882a593Smuzhiyun 		list_add(l, &queue->head);
125*4882a593Smuzhiyun 	else
126*4882a593Smuzhiyun 		list_add_tail(l, &queue->head);
127*4882a593Smuzhiyun 
128*4882a593Smuzhiyun 	ret = 1;
129*4882a593Smuzhiyun empty:
130*4882a593Smuzhiyun 	spin_unlock_irqrestore(&queue->queue_lock, flags);
131*4882a593Smuzhiyun 	return ret;
132*4882a593Smuzhiyun }
133*4882a593Smuzhiyun 
__queue_remove(Queue_t * queue,struct list_head * ent)134*4882a593Smuzhiyun static struct scsi_cmnd *__queue_remove(Queue_t *queue, struct list_head *ent)
135*4882a593Smuzhiyun {
136*4882a593Smuzhiyun 	QE_t *q;
137*4882a593Smuzhiyun 
138*4882a593Smuzhiyun 	/*
139*4882a593Smuzhiyun 	 * Move the entry from the "used" list onto the "free" list
140*4882a593Smuzhiyun 	 */
141*4882a593Smuzhiyun 	list_del(ent);
142*4882a593Smuzhiyun 	q = list_entry(ent, QE_t, list);
143*4882a593Smuzhiyun 	BUG_ON(BAD_MAGIC(q, QUEUE_MAGIC_USED));
144*4882a593Smuzhiyun 
145*4882a593Smuzhiyun 	SET_MAGIC(q, QUEUE_MAGIC_FREE);
146*4882a593Smuzhiyun 	list_add(ent, &queue->free);
147*4882a593Smuzhiyun 
148*4882a593Smuzhiyun 	return q->SCpnt;
149*4882a593Smuzhiyun }
150*4882a593Smuzhiyun 
151*4882a593Smuzhiyun /*
152*4882a593Smuzhiyun  * Function: struct scsi_cmnd *queue_remove_exclude (queue, exclude)
153*4882a593Smuzhiyun  * Purpose : remove a SCSI command from a queue
154*4882a593Smuzhiyun  * Params  : queue   - queue to remove command from
155*4882a593Smuzhiyun  *	     exclude - bit array of target&lun which is busy
156*4882a593Smuzhiyun  * Returns : struct scsi_cmnd if successful (and a reference), or NULL if no command available
157*4882a593Smuzhiyun  */
queue_remove_exclude(Queue_t * queue,unsigned long * exclude)158*4882a593Smuzhiyun struct scsi_cmnd *queue_remove_exclude(Queue_t *queue, unsigned long *exclude)
159*4882a593Smuzhiyun {
160*4882a593Smuzhiyun 	unsigned long flags;
161*4882a593Smuzhiyun 	struct list_head *l;
162*4882a593Smuzhiyun 	struct scsi_cmnd *SCpnt = NULL;
163*4882a593Smuzhiyun 
164*4882a593Smuzhiyun 	spin_lock_irqsave(&queue->queue_lock, flags);
165*4882a593Smuzhiyun 	list_for_each(l, &queue->head) {
166*4882a593Smuzhiyun 		QE_t *q = list_entry(l, QE_t, list);
167*4882a593Smuzhiyun 		if (!test_bit(q->SCpnt->device->id * 8 +
168*4882a593Smuzhiyun 			      (u8)(q->SCpnt->device->lun & 0x7), exclude)) {
169*4882a593Smuzhiyun 			SCpnt = __queue_remove(queue, l);
170*4882a593Smuzhiyun 			break;
171*4882a593Smuzhiyun 		}
172*4882a593Smuzhiyun 	}
173*4882a593Smuzhiyun 	spin_unlock_irqrestore(&queue->queue_lock, flags);
174*4882a593Smuzhiyun 
175*4882a593Smuzhiyun 	return SCpnt;
176*4882a593Smuzhiyun }
177*4882a593Smuzhiyun 
178*4882a593Smuzhiyun /*
179*4882a593Smuzhiyun  * Function: struct scsi_cmnd *queue_remove (queue)
180*4882a593Smuzhiyun  * Purpose : removes first SCSI command from a queue
181*4882a593Smuzhiyun  * Params  : queue   - queue to remove command from
182*4882a593Smuzhiyun  * Returns : struct scsi_cmnd if successful (and a reference), or NULL if no command available
183*4882a593Smuzhiyun  */
queue_remove(Queue_t * queue)184*4882a593Smuzhiyun struct scsi_cmnd *queue_remove(Queue_t *queue)
185*4882a593Smuzhiyun {
186*4882a593Smuzhiyun 	unsigned long flags;
187*4882a593Smuzhiyun 	struct scsi_cmnd *SCpnt = NULL;
188*4882a593Smuzhiyun 
189*4882a593Smuzhiyun 	spin_lock_irqsave(&queue->queue_lock, flags);
190*4882a593Smuzhiyun 	if (!list_empty(&queue->head))
191*4882a593Smuzhiyun 		SCpnt = __queue_remove(queue, queue->head.next);
192*4882a593Smuzhiyun 	spin_unlock_irqrestore(&queue->queue_lock, flags);
193*4882a593Smuzhiyun 
194*4882a593Smuzhiyun 	return SCpnt;
195*4882a593Smuzhiyun }
196*4882a593Smuzhiyun 
197*4882a593Smuzhiyun /*
198*4882a593Smuzhiyun  * Function: struct scsi_cmnd *queue_remove_tgtluntag (queue, target, lun, tag)
199*4882a593Smuzhiyun  * Purpose : remove a SCSI command from the queue for a specified target/lun/tag
200*4882a593Smuzhiyun  * Params  : queue  - queue to remove command from
201*4882a593Smuzhiyun  *	     target - target that we want
202*4882a593Smuzhiyun  *	     lun    - lun on device
203*4882a593Smuzhiyun  *	     tag    - tag on device
204*4882a593Smuzhiyun  * Returns : struct scsi_cmnd if successful, or NULL if no command satisfies requirements
205*4882a593Smuzhiyun  */
queue_remove_tgtluntag(Queue_t * queue,int target,int lun,int tag)206*4882a593Smuzhiyun struct scsi_cmnd *queue_remove_tgtluntag(Queue_t *queue, int target, int lun,
207*4882a593Smuzhiyun 					 int tag)
208*4882a593Smuzhiyun {
209*4882a593Smuzhiyun 	unsigned long flags;
210*4882a593Smuzhiyun 	struct list_head *l;
211*4882a593Smuzhiyun 	struct scsi_cmnd *SCpnt = NULL;
212*4882a593Smuzhiyun 
213*4882a593Smuzhiyun 	spin_lock_irqsave(&queue->queue_lock, flags);
214*4882a593Smuzhiyun 	list_for_each(l, &queue->head) {
215*4882a593Smuzhiyun 		QE_t *q = list_entry(l, QE_t, list);
216*4882a593Smuzhiyun 		if (q->SCpnt->device->id == target && q->SCpnt->device->lun == lun &&
217*4882a593Smuzhiyun 		    q->SCpnt->tag == tag) {
218*4882a593Smuzhiyun 			SCpnt = __queue_remove(queue, l);
219*4882a593Smuzhiyun 			break;
220*4882a593Smuzhiyun 		}
221*4882a593Smuzhiyun 	}
222*4882a593Smuzhiyun 	spin_unlock_irqrestore(&queue->queue_lock, flags);
223*4882a593Smuzhiyun 
224*4882a593Smuzhiyun 	return SCpnt;
225*4882a593Smuzhiyun }
226*4882a593Smuzhiyun 
227*4882a593Smuzhiyun /*
228*4882a593Smuzhiyun  * Function: queue_remove_all_target(queue, target)
229*4882a593Smuzhiyun  * Purpose : remove all SCSI commands from the queue for a specified target
230*4882a593Smuzhiyun  * Params  : queue  - queue to remove command from
231*4882a593Smuzhiyun  *           target - target device id
232*4882a593Smuzhiyun  * Returns : nothing
233*4882a593Smuzhiyun  */
queue_remove_all_target(Queue_t * queue,int target)234*4882a593Smuzhiyun void queue_remove_all_target(Queue_t *queue, int target)
235*4882a593Smuzhiyun {
236*4882a593Smuzhiyun 	unsigned long flags;
237*4882a593Smuzhiyun 	struct list_head *l;
238*4882a593Smuzhiyun 
239*4882a593Smuzhiyun 	spin_lock_irqsave(&queue->queue_lock, flags);
240*4882a593Smuzhiyun 	list_for_each(l, &queue->head) {
241*4882a593Smuzhiyun 		QE_t *q = list_entry(l, QE_t, list);
242*4882a593Smuzhiyun 		if (q->SCpnt->device->id == target)
243*4882a593Smuzhiyun 			__queue_remove(queue, l);
244*4882a593Smuzhiyun 	}
245*4882a593Smuzhiyun 	spin_unlock_irqrestore(&queue->queue_lock, flags);
246*4882a593Smuzhiyun }
247*4882a593Smuzhiyun 
248*4882a593Smuzhiyun /*
249*4882a593Smuzhiyun  * Function: int queue_probetgtlun (queue, target, lun)
250*4882a593Smuzhiyun  * Purpose : check to see if we have a command in the queue for the specified
251*4882a593Smuzhiyun  *	     target/lun.
252*4882a593Smuzhiyun  * Params  : queue  - queue to look in
253*4882a593Smuzhiyun  *	     target - target we want to probe
254*4882a593Smuzhiyun  *	     lun    - lun on target
255*4882a593Smuzhiyun  * Returns : 0 if not found, != 0 if found
256*4882a593Smuzhiyun  */
queue_probetgtlun(Queue_t * queue,int target,int lun)257*4882a593Smuzhiyun int queue_probetgtlun (Queue_t *queue, int target, int lun)
258*4882a593Smuzhiyun {
259*4882a593Smuzhiyun 	unsigned long flags;
260*4882a593Smuzhiyun 	struct list_head *l;
261*4882a593Smuzhiyun 	int found = 0;
262*4882a593Smuzhiyun 
263*4882a593Smuzhiyun 	spin_lock_irqsave(&queue->queue_lock, flags);
264*4882a593Smuzhiyun 	list_for_each(l, &queue->head) {
265*4882a593Smuzhiyun 		QE_t *q = list_entry(l, QE_t, list);
266*4882a593Smuzhiyun 		if (q->SCpnt->device->id == target && q->SCpnt->device->lun == lun) {
267*4882a593Smuzhiyun 			found = 1;
268*4882a593Smuzhiyun 			break;
269*4882a593Smuzhiyun 		}
270*4882a593Smuzhiyun 	}
271*4882a593Smuzhiyun 	spin_unlock_irqrestore(&queue->queue_lock, flags);
272*4882a593Smuzhiyun 
273*4882a593Smuzhiyun 	return found;
274*4882a593Smuzhiyun }
275*4882a593Smuzhiyun 
276*4882a593Smuzhiyun /*
277*4882a593Smuzhiyun  * Function: int queue_remove_cmd(Queue_t *queue, struct scsi_cmnd *SCpnt)
278*4882a593Smuzhiyun  * Purpose : remove a specific command from the queues
279*4882a593Smuzhiyun  * Params  : queue - queue to look in
280*4882a593Smuzhiyun  *	     SCpnt - command to find
281*4882a593Smuzhiyun  * Returns : 0 if not found
282*4882a593Smuzhiyun  */
queue_remove_cmd(Queue_t * queue,struct scsi_cmnd * SCpnt)283*4882a593Smuzhiyun int queue_remove_cmd(Queue_t *queue, struct scsi_cmnd *SCpnt)
284*4882a593Smuzhiyun {
285*4882a593Smuzhiyun 	unsigned long flags;
286*4882a593Smuzhiyun 	struct list_head *l;
287*4882a593Smuzhiyun 	int found = 0;
288*4882a593Smuzhiyun 
289*4882a593Smuzhiyun 	spin_lock_irqsave(&queue->queue_lock, flags);
290*4882a593Smuzhiyun 	list_for_each(l, &queue->head) {
291*4882a593Smuzhiyun 		QE_t *q = list_entry(l, QE_t, list);
292*4882a593Smuzhiyun 		if (q->SCpnt == SCpnt) {
293*4882a593Smuzhiyun 			__queue_remove(queue, l);
294*4882a593Smuzhiyun 			found = 1;
295*4882a593Smuzhiyun 			break;
296*4882a593Smuzhiyun 		}
297*4882a593Smuzhiyun 	}
298*4882a593Smuzhiyun 	spin_unlock_irqrestore(&queue->queue_lock, flags);
299*4882a593Smuzhiyun 
300*4882a593Smuzhiyun 	return found;
301*4882a593Smuzhiyun }
302*4882a593Smuzhiyun 
303*4882a593Smuzhiyun EXPORT_SYMBOL(queue_initialise);
304*4882a593Smuzhiyun EXPORT_SYMBOL(queue_free);
305*4882a593Smuzhiyun EXPORT_SYMBOL(__queue_add);
306*4882a593Smuzhiyun EXPORT_SYMBOL(queue_remove);
307*4882a593Smuzhiyun EXPORT_SYMBOL(queue_remove_exclude);
308*4882a593Smuzhiyun EXPORT_SYMBOL(queue_remove_tgtluntag);
309*4882a593Smuzhiyun EXPORT_SYMBOL(queue_remove_cmd);
310*4882a593Smuzhiyun EXPORT_SYMBOL(queue_remove_all_target);
311*4882a593Smuzhiyun EXPORT_SYMBOL(queue_probetgtlun);
312*4882a593Smuzhiyun 
313*4882a593Smuzhiyun MODULE_AUTHOR("Russell King");
314*4882a593Smuzhiyun MODULE_DESCRIPTION("SCSI command queueing");
315*4882a593Smuzhiyun MODULE_LICENSE("GPL");
316