1 /*
2  *  linux/drivers/acorn/scsi/queue.c: queue handling primitives
3  *
4  *  Copyright (C) 1997-2000 Russell King
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License version 2 as
8  * published by the Free Software Foundation.
9  *
10  *  Changelog:
11  *   15-Sep-1997 RMK	Created.
12  *   11-Oct-1997 RMK	Corrected problem with queue_remove_exclude
13  *			not updating internal linked list properly
14  *			(was causing commands to go missing).
15  *   30-Aug-2000 RMK	Use Linux list handling and spinlocks
16  */
17 #include <linux/module.h>
18 #include <linux/blk.h>
19 #include <linux/kernel.h>
20 #include <linux/string.h>
21 #include <linux/slab.h>
22 #include <linux/spinlock.h>
23 #include <linux/list.h>
24 #include <linux/init.h>
25 
26 #include "../../scsi/scsi.h"
27 
28 #define DEBUG
29 
30 typedef struct queue_entry {
31 	struct list_head   list;
32 	Scsi_Cmnd	   *SCpnt;
33 #ifdef DEBUG
34 	unsigned long	   magic;
35 #endif
36 } QE_t;
37 
38 #ifdef DEBUG
39 #define QUEUE_MAGIC_FREE	0xf7e1c9a3
40 #define QUEUE_MAGIC_USED	0xf7e1cc33
41 
42 #define SET_MAGIC(q,m)	((q)->magic = (m))
43 #define BAD_MAGIC(q,m)	((q)->magic != (m))
44 #else
45 #define SET_MAGIC(q,m)	do { } while (0)
46 #define BAD_MAGIC(q,m)	(0)
47 #endif
48 
49 #include "queue.h"
50 
51 #define NR_QE	32
52 
53 /*
54  * Function: void queue_initialise (Queue_t *queue)
55  * Purpose : initialise a queue
56  * Params  : queue - queue to initialise
57  */
queue_initialise(Queue_t * queue)58 int queue_initialise (Queue_t *queue)
59 {
60 	unsigned int nqueues = NR_QE;
61 	QE_t *q;
62 
63 	spin_lock_init(&queue->queue_lock);
64 	INIT_LIST_HEAD(&queue->head);
65 	INIT_LIST_HEAD(&queue->free);
66 
67 	/*
68 	 * If life was easier, then SCpnt would have a
69 	 * host-available list head, and we wouldn't
70 	 * need to keep free lists or allocate this
71 	 * memory.
72 	 */
73 	queue->alloc = q = kmalloc(sizeof(QE_t) * nqueues, GFP_KERNEL);
74 	if (q) {
75 		for (; nqueues; q++, nqueues--) {
76 			SET_MAGIC(q, QUEUE_MAGIC_FREE);
77 			q->SCpnt = NULL;
78 			list_add(&q->list, &queue->free);
79 		}
80 	}
81 
82 	return queue->alloc != NULL;
83 }
84 
85 /*
86  * Function: void queue_free (Queue_t *queue)
87  * Purpose : free a queue
88  * Params  : queue - queue to free
89  */
queue_free(Queue_t * queue)90 void queue_free (Queue_t *queue)
91 {
92 	if (!list_empty(&queue->head))
93 		printk(KERN_WARNING "freeing non-empty queue %p\n", queue);
94 	if (queue->alloc)
95 		kfree(queue->alloc);
96 }
97 
98 
99 /*
100  * Function: int queue_add_cmd(Queue_t *queue, Scsi_Cmnd *SCpnt, int head)
101  * Purpose : Add a new command onto a queue, adding REQUEST_SENSE to head.
102  * Params  : queue - destination queue
103  *	     SCpnt - command to add
104  *	     head  - add command to head of queue
105  * Returns : 0 on error, !0 on success
106  */
__queue_add(Queue_t * queue,Scsi_Cmnd * SCpnt,int head)107 int __queue_add(Queue_t *queue, 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 	if (BAD_MAGIC(q, QUEUE_MAGIC_FREE))
123 		BUG();
124 
125 	SET_MAGIC(q, QUEUE_MAGIC_USED);
126 	q->SCpnt = SCpnt;
127 
128 	if (head)
129 		list_add(l, &queue->head);
130 	else
131 		list_add_tail(l, &queue->head);
132 
133 	ret = 1;
134 empty:
135 	spin_unlock_irqrestore(&queue->queue_lock, flags);
136 	return ret;
137 }
138 
__queue_remove(Queue_t * queue,struct list_head * ent)139 static Scsi_Cmnd *__queue_remove(Queue_t *queue, struct list_head *ent)
140 {
141 	QE_t *q;
142 
143 	/*
144 	 * Move the entry from the "used" list onto the "free" list
145 	 */
146 	list_del(ent);
147 	q = list_entry(ent, QE_t, list);
148 	if (BAD_MAGIC(q, QUEUE_MAGIC_USED))
149 		BUG();
150 
151 	SET_MAGIC(q, QUEUE_MAGIC_FREE);
152 	list_add(ent, &queue->free);
153 
154 	return q->SCpnt;
155 }
156 
157 /*
158  * Function: Scsi_Cmnd *queue_remove_exclude (queue, exclude)
159  * Purpose : remove a SCSI command from a queue
160  * Params  : queue   - queue to remove command from
161  *	     exclude - bit array of target&lun which is busy
162  * Returns : Scsi_Cmnd if successful (and a reference), or NULL if no command available
163  */
queue_remove_exclude(Queue_t * queue,void * exclude)164 Scsi_Cmnd *queue_remove_exclude(Queue_t *queue, void *exclude)
165 {
166 	unsigned long flags;
167 	struct list_head *l;
168 	Scsi_Cmnd *SCpnt = NULL;
169 
170 	spin_lock_irqsave(&queue->queue_lock, flags);
171 	list_for_each(l, &queue->head) {
172 		QE_t *q = list_entry(l, QE_t, list);
173 		if (!test_bit(q->SCpnt->target * 8 + q->SCpnt->lun, exclude)) {
174 			SCpnt = __queue_remove(queue, l);
175 			break;
176 		}
177 	}
178 	spin_unlock_irqrestore(&queue->queue_lock, flags);
179 
180 	return SCpnt;
181 }
182 
183 /*
184  * Function: Scsi_Cmnd *queue_remove (queue)
185  * Purpose : removes first SCSI command from a queue
186  * Params  : queue   - queue to remove command from
187  * Returns : Scsi_Cmnd if successful (and a reference), or NULL if no command available
188  */
queue_remove(Queue_t * queue)189 Scsi_Cmnd *queue_remove(Queue_t *queue)
190 {
191 	unsigned long flags;
192 	Scsi_Cmnd *SCpnt = NULL;
193 
194 	spin_lock_irqsave(&queue->queue_lock, flags);
195 	if (!list_empty(&queue->head))
196 		SCpnt = __queue_remove(queue, queue->head.next);
197 	spin_unlock_irqrestore(&queue->queue_lock, flags);
198 
199 	return SCpnt;
200 }
201 
202 /*
203  * Function: Scsi_Cmnd *queue_remove_tgtluntag (queue, target, lun, tag)
204  * Purpose : remove a SCSI command from the queue for a specified target/lun/tag
205  * Params  : queue  - queue to remove command from
206  *	     target - target that we want
207  *	     lun    - lun on device
208  *	     tag    - tag on device
209  * Returns : Scsi_Cmnd if successful, or NULL if no command satisfies requirements
210  */
queue_remove_tgtluntag(Queue_t * queue,int target,int lun,int tag)211 Scsi_Cmnd *queue_remove_tgtluntag (Queue_t *queue, int target, int lun, int tag)
212 {
213 	unsigned long flags;
214 	struct list_head *l;
215 	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->target == target && q->SCpnt->lun == lun &&
221 		    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 target
234  * Params  : queue  - queue to remove command from
235  *           target - target device id
236  * Returns : nothing
237  */
queue_remove_all_target(Queue_t * queue,int target)238 void 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->target == 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 specified
255  *	     target/lun.
256  * Params  : queue  - queue to look in
257  *	     target - target we want to probe
258  *	     lun    - lun on target
259  * Returns : 0 if not found, != 0 if found
260  */
queue_probetgtlun(Queue_t * queue,int target,int lun)261 int 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->target == target && q->SCpnt->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, Scsi_Cmnd *SCpnt)
282  * Purpose : remove a specific command from the queues
283  * Params  : queue - queue to look in
284  *	     SCpnt - command to find
285  * Returns : 0 if not found
286  */
queue_remove_cmd(Queue_t * queue,Scsi_Cmnd * SCpnt)287 int queue_remove_cmd(Queue_t *queue, 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 
307 EXPORT_SYMBOL(queue_initialise);
308 EXPORT_SYMBOL(queue_free);
309 EXPORT_SYMBOL(__queue_add);
310 EXPORT_SYMBOL(queue_remove);
311 EXPORT_SYMBOL(queue_remove_exclude);
312 EXPORT_SYMBOL(queue_remove_tgtluntag);
313 EXPORT_SYMBOL(queue_remove_cmd);
314 EXPORT_SYMBOL(queue_remove_all_target);
315 EXPORT_SYMBOL(queue_probetgtlun);
316 
317 MODULE_AUTHOR("Russell King");
318 MODULE_DESCRIPTION("SCSI command queueing");
319 MODULE_LICENSE("GPL");
320