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