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