brintos

brintos / linux-shallow public Read only

0
0
Text · 7.9 KiB · 978df23 Raw
320 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 *  linux/drivers/acorn/scsi/queue.c: queue handling primitives4 *5 *  Copyright (C) 1997-2000 Russell King6 *7 *  Changelog:8 *   15-Sep-1997 RMK	Created.9 *   11-Oct-1997 RMK	Corrected problem with queue_remove_exclude10 *			not updating internal linked list properly11 *			(was causing commands to go missing).12 *   30-Aug-2000 RMK	Use Linux list handling and spinlocks13 */14#include <linux/module.h>15#include <linux/blkdev.h>16#include <linux/kernel.h>17#include <linux/string.h>18#include <linux/slab.h>19#include <linux/spinlock.h>20#include <linux/list.h>21#include <linux/init.h>22 23#include <scsi/scsi.h>24#include <scsi/scsi_cmnd.h>25#include <scsi/scsi_device.h>26#include <scsi/scsi_eh.h>27#include <scsi/scsi_tcq.h>28 29#define DEBUG30 31typedef struct queue_entry {32	struct list_head   list;33	struct scsi_cmnd   *SCpnt;34#ifdef DEBUG35	unsigned long	   magic;36#endif37} QE_t;38 39#ifdef DEBUG40#define QUEUE_MAGIC_FREE	0xf7e1c9a341#define QUEUE_MAGIC_USED	0xf7e1cc3342 43#define SET_MAGIC(q,m)	((q)->magic = (m))44#define BAD_MAGIC(q,m)	((q)->magic != (m))45#else46#define SET_MAGIC(q,m)	do { } while (0)47#define BAD_MAGIC(q,m)	(0)48#endif49 50#include "queue.h"51 52#define NR_QE	3253 54/*55 * Function: void queue_initialise (Queue_t *queue)56 * Purpose : initialise a queue57 * Params  : queue - queue to initialise58 */59int queue_initialise (Queue_t *queue)60{61	unsigned int nqueues = NR_QE;62	QE_t *q;63 64	spin_lock_init(&queue->queue_lock);65	INIT_LIST_HEAD(&queue->head);66	INIT_LIST_HEAD(&queue->free);67 68	/*69	 * If life was easier, then SCpnt would have a70	 * host-available list head, and we wouldn't71	 * need to keep free lists or allocate this72	 * memory.73	 */74	queue->alloc = q = kmalloc_array(nqueues, sizeof(QE_t), GFP_KERNEL);75	if (q) {76		for (; nqueues; q++, nqueues--) {77			SET_MAGIC(q, QUEUE_MAGIC_FREE);78			q->SCpnt = NULL;79			list_add(&q->list, &queue->free);80		}81	}82 83	return queue->alloc != NULL;84}85 86/*87 * Function: void queue_free (Queue_t *queue)88 * Purpose : free a queue89 * Params  : queue - queue to free90 */91void queue_free (Queue_t *queue)92{93	if (!list_empty(&queue->head))94		printk(KERN_WARNING "freeing non-empty queue %p\n", queue);95	kfree(queue->alloc);96}97     98 99/*100 * Function: int __queue_add(Queue_t *queue, struct scsi_cmnd *SCpnt, int head)101 * Purpose : Add a new command onto a queue, adding REQUEST_SENSE to head.102 * Params  : queue - destination queue103 *	     SCpnt - command to add104 *	     head  - add command to head of queue105 * Returns : 0 on error, !0 on success106 */107int __queue_add(Queue_t *queue, struct 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	BUG_ON(BAD_MAGIC(q, QUEUE_MAGIC_FREE));123 124	SET_MAGIC(q, QUEUE_MAGIC_USED);125	q->SCpnt = SCpnt;126 127	if (head)128		list_add(l, &queue->head);129	else130		list_add_tail(l, &queue->head);131 132	ret = 1;133empty:134	spin_unlock_irqrestore(&queue->queue_lock, flags);135	return ret;136}137 138static struct scsi_cmnd *__queue_remove(Queue_t *queue, struct list_head *ent)139{140	QE_t *q;141 142	/*143	 * Move the entry from the "used" list onto the "free" list144	 */145	list_del(ent);146	q = list_entry(ent, QE_t, list);147	BUG_ON(BAD_MAGIC(q, QUEUE_MAGIC_USED));148 149	SET_MAGIC(q, QUEUE_MAGIC_FREE);150	list_add(ent, &queue->free);151 152	return q->SCpnt;153}154 155/*156 * Function: struct scsi_cmnd *queue_remove_exclude (queue, exclude)157 * Purpose : remove a SCSI command from a queue158 * Params  : queue   - queue to remove command from159 *	     exclude - bit array of target&lun which is busy160 * Returns : struct scsi_cmnd if successful (and a reference), or NULL if no command available161 */162struct scsi_cmnd *queue_remove_exclude(Queue_t *queue, unsigned long *exclude)163{164	unsigned long flags;165	struct list_head *l;166	struct scsi_cmnd *SCpnt = NULL;167 168	spin_lock_irqsave(&queue->queue_lock, flags);169	list_for_each(l, &queue->head) {170		QE_t *q = list_entry(l, QE_t, list);171		if (!test_bit(q->SCpnt->device->id * 8 +172			      (u8)(q->SCpnt->device->lun & 0x7), exclude)) {173			SCpnt = __queue_remove(queue, l);174			break;175		}176	}177	spin_unlock_irqrestore(&queue->queue_lock, flags);178 179	return SCpnt;180}181 182/*183 * Function: struct scsi_cmnd *queue_remove (queue)184 * Purpose : removes first SCSI command from a queue185 * Params  : queue   - queue to remove command from186 * Returns : struct scsi_cmnd if successful (and a reference), or NULL if no command available187 */188struct scsi_cmnd *queue_remove(Queue_t *queue)189{190	unsigned long flags;191	struct scsi_cmnd *SCpnt = NULL;192 193	spin_lock_irqsave(&queue->queue_lock, flags);194	if (!list_empty(&queue->head))195		SCpnt = __queue_remove(queue, queue->head.next);196	spin_unlock_irqrestore(&queue->queue_lock, flags);197 198	return SCpnt;199}200 201/*202 * Function: struct scsi_cmnd *queue_remove_tgtluntag (queue, target, lun, tag)203 * Purpose : remove a SCSI command from the queue for a specified target/lun/tag204 * Params  : queue  - queue to remove command from205 *	     target - target that we want206 *	     lun    - lun on device207 *	     tag    - tag on device208 * Returns : struct scsi_cmnd if successful, or NULL if no command satisfies requirements209 */210struct scsi_cmnd *queue_remove_tgtluntag(Queue_t *queue, int target, int lun,211					 int tag)212{213	unsigned long flags;214	struct list_head *l;215	struct 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		    scsi_cmd_to_rq(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 target234 * Params  : queue  - queue to remove command from235 *           target - target device id236 * Returns : nothing237 */238void 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 specified255 *	     target/lun.256 * Params  : queue  - queue to look in257 *	     target - target we want to probe258 *	     lun    - lun on target259 * Returns : 0 if not found, != 0 if found260 */261int 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, struct scsi_cmnd *SCpnt)282 * Purpose : remove a specific command from the queues283 * Params  : queue - queue to look in284 *	     SCpnt - command to find285 * Returns : 0 if not found286 */287int queue_remove_cmd(Queue_t *queue, struct 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 307EXPORT_SYMBOL(queue_initialise);308EXPORT_SYMBOL(queue_free);309EXPORT_SYMBOL(__queue_add);310EXPORT_SYMBOL(queue_remove);311EXPORT_SYMBOL(queue_remove_exclude);312EXPORT_SYMBOL(queue_remove_tgtluntag);313EXPORT_SYMBOL(queue_remove_cmd);314EXPORT_SYMBOL(queue_remove_all_target);315EXPORT_SYMBOL(queue_probetgtlun);316 317MODULE_AUTHOR("Russell King");318MODULE_DESCRIPTION("SCSI command queueing");319MODULE_LICENSE("GPL");320