diff options
Diffstat (limited to 'drivers/scsi/arm/queue.c')
-rw-r--r-- | drivers/scsi/arm/queue.c | 319 |
1 files changed, 319 insertions, 0 deletions
diff --git a/drivers/scsi/arm/queue.c b/drivers/scsi/arm/queue.c new file mode 100644 index 000000000000..e6d159270d29 --- /dev/null +++ b/drivers/scsi/arm/queue.c | |||
@@ -0,0 +1,319 @@ | |||
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/blkdev.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.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 | */ | ||
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 | */ | ||
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 | */ | ||
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 | |||
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 | */ | ||
164 | Scsi_Cmnd *queue_remove_exclude(Queue_t *queue, unsigned long *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->device->id * 8 + q->SCpnt->device->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 | */ | ||
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 | */ | ||
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->device->id == target && q->SCpnt->device->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 | */ | ||
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->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 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 | */ | ||
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->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, 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 | */ | ||
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"); | ||