18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci *  linux/drivers/acorn/scsi/queue.c: queue handling primitives
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci *  Copyright (C) 1997-2000 Russell King
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci *  Changelog:
88c2ecf20Sopenharmony_ci *   15-Sep-1997 RMK	Created.
98c2ecf20Sopenharmony_ci *   11-Oct-1997 RMK	Corrected problem with queue_remove_exclude
108c2ecf20Sopenharmony_ci *			not updating internal linked list properly
118c2ecf20Sopenharmony_ci *			(was causing commands to go missing).
128c2ecf20Sopenharmony_ci *   30-Aug-2000 RMK	Use Linux list handling and spinlocks
138c2ecf20Sopenharmony_ci */
148c2ecf20Sopenharmony_ci#include <linux/module.h>
158c2ecf20Sopenharmony_ci#include <linux/blkdev.h>
168c2ecf20Sopenharmony_ci#include <linux/kernel.h>
178c2ecf20Sopenharmony_ci#include <linux/string.h>
188c2ecf20Sopenharmony_ci#include <linux/slab.h>
198c2ecf20Sopenharmony_ci#include <linux/spinlock.h>
208c2ecf20Sopenharmony_ci#include <linux/list.h>
218c2ecf20Sopenharmony_ci#include <linux/init.h>
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_ci#include "../scsi.h"
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_ci#define DEBUG
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_citypedef struct queue_entry {
288c2ecf20Sopenharmony_ci	struct list_head   list;
298c2ecf20Sopenharmony_ci	struct scsi_cmnd   *SCpnt;
308c2ecf20Sopenharmony_ci#ifdef DEBUG
318c2ecf20Sopenharmony_ci	unsigned long	   magic;
328c2ecf20Sopenharmony_ci#endif
338c2ecf20Sopenharmony_ci} QE_t;
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_ci#ifdef DEBUG
368c2ecf20Sopenharmony_ci#define QUEUE_MAGIC_FREE	0xf7e1c9a3
378c2ecf20Sopenharmony_ci#define QUEUE_MAGIC_USED	0xf7e1cc33
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_ci#define SET_MAGIC(q,m)	((q)->magic = (m))
408c2ecf20Sopenharmony_ci#define BAD_MAGIC(q,m)	((q)->magic != (m))
418c2ecf20Sopenharmony_ci#else
428c2ecf20Sopenharmony_ci#define SET_MAGIC(q,m)	do { } while (0)
438c2ecf20Sopenharmony_ci#define BAD_MAGIC(q,m)	(0)
448c2ecf20Sopenharmony_ci#endif
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_ci#include "queue.h"
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_ci#define NR_QE	32
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_ci/*
518c2ecf20Sopenharmony_ci * Function: void queue_initialise (Queue_t *queue)
528c2ecf20Sopenharmony_ci * Purpose : initialise a queue
538c2ecf20Sopenharmony_ci * Params  : queue - queue to initialise
548c2ecf20Sopenharmony_ci */
558c2ecf20Sopenharmony_ciint queue_initialise (Queue_t *queue)
568c2ecf20Sopenharmony_ci{
578c2ecf20Sopenharmony_ci	unsigned int nqueues = NR_QE;
588c2ecf20Sopenharmony_ci	QE_t *q;
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_ci	spin_lock_init(&queue->queue_lock);
618c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&queue->head);
628c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&queue->free);
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_ci	/*
658c2ecf20Sopenharmony_ci	 * If life was easier, then SCpnt would have a
668c2ecf20Sopenharmony_ci	 * host-available list head, and we wouldn't
678c2ecf20Sopenharmony_ci	 * need to keep free lists or allocate this
688c2ecf20Sopenharmony_ci	 * memory.
698c2ecf20Sopenharmony_ci	 */
708c2ecf20Sopenharmony_ci	queue->alloc = q = kmalloc_array(nqueues, sizeof(QE_t), GFP_KERNEL);
718c2ecf20Sopenharmony_ci	if (q) {
728c2ecf20Sopenharmony_ci		for (; nqueues; q++, nqueues--) {
738c2ecf20Sopenharmony_ci			SET_MAGIC(q, QUEUE_MAGIC_FREE);
748c2ecf20Sopenharmony_ci			q->SCpnt = NULL;
758c2ecf20Sopenharmony_ci			list_add(&q->list, &queue->free);
768c2ecf20Sopenharmony_ci		}
778c2ecf20Sopenharmony_ci	}
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_ci	return queue->alloc != NULL;
808c2ecf20Sopenharmony_ci}
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_ci/*
838c2ecf20Sopenharmony_ci * Function: void queue_free (Queue_t *queue)
848c2ecf20Sopenharmony_ci * Purpose : free a queue
858c2ecf20Sopenharmony_ci * Params  : queue - queue to free
868c2ecf20Sopenharmony_ci */
878c2ecf20Sopenharmony_civoid queue_free (Queue_t *queue)
888c2ecf20Sopenharmony_ci{
898c2ecf20Sopenharmony_ci	if (!list_empty(&queue->head))
908c2ecf20Sopenharmony_ci		printk(KERN_WARNING "freeing non-empty queue %p\n", queue);
918c2ecf20Sopenharmony_ci	kfree(queue->alloc);
928c2ecf20Sopenharmony_ci}
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_ci/*
968c2ecf20Sopenharmony_ci * Function: int __queue_add(Queue_t *queue, struct scsi_cmnd *SCpnt, int head)
978c2ecf20Sopenharmony_ci * Purpose : Add a new command onto a queue, adding REQUEST_SENSE to head.
988c2ecf20Sopenharmony_ci * Params  : queue - destination queue
998c2ecf20Sopenharmony_ci *	     SCpnt - command to add
1008c2ecf20Sopenharmony_ci *	     head  - add command to head of queue
1018c2ecf20Sopenharmony_ci * Returns : 0 on error, !0 on success
1028c2ecf20Sopenharmony_ci */
1038c2ecf20Sopenharmony_ciint __queue_add(Queue_t *queue, struct scsi_cmnd *SCpnt, int head)
1048c2ecf20Sopenharmony_ci{
1058c2ecf20Sopenharmony_ci	unsigned long flags;
1068c2ecf20Sopenharmony_ci	struct list_head *l;
1078c2ecf20Sopenharmony_ci	QE_t *q;
1088c2ecf20Sopenharmony_ci	int ret = 0;
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_ci	spin_lock_irqsave(&queue->queue_lock, flags);
1118c2ecf20Sopenharmony_ci	if (list_empty(&queue->free))
1128c2ecf20Sopenharmony_ci		goto empty;
1138c2ecf20Sopenharmony_ci
1148c2ecf20Sopenharmony_ci	l = queue->free.next;
1158c2ecf20Sopenharmony_ci	list_del(l);
1168c2ecf20Sopenharmony_ci
1178c2ecf20Sopenharmony_ci	q = list_entry(l, QE_t, list);
1188c2ecf20Sopenharmony_ci	BUG_ON(BAD_MAGIC(q, QUEUE_MAGIC_FREE));
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_ci	SET_MAGIC(q, QUEUE_MAGIC_USED);
1218c2ecf20Sopenharmony_ci	q->SCpnt = SCpnt;
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_ci	if (head)
1248c2ecf20Sopenharmony_ci		list_add(l, &queue->head);
1258c2ecf20Sopenharmony_ci	else
1268c2ecf20Sopenharmony_ci		list_add_tail(l, &queue->head);
1278c2ecf20Sopenharmony_ci
1288c2ecf20Sopenharmony_ci	ret = 1;
1298c2ecf20Sopenharmony_ciempty:
1308c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&queue->queue_lock, flags);
1318c2ecf20Sopenharmony_ci	return ret;
1328c2ecf20Sopenharmony_ci}
1338c2ecf20Sopenharmony_ci
1348c2ecf20Sopenharmony_cistatic struct scsi_cmnd *__queue_remove(Queue_t *queue, struct list_head *ent)
1358c2ecf20Sopenharmony_ci{
1368c2ecf20Sopenharmony_ci	QE_t *q;
1378c2ecf20Sopenharmony_ci
1388c2ecf20Sopenharmony_ci	/*
1398c2ecf20Sopenharmony_ci	 * Move the entry from the "used" list onto the "free" list
1408c2ecf20Sopenharmony_ci	 */
1418c2ecf20Sopenharmony_ci	list_del(ent);
1428c2ecf20Sopenharmony_ci	q = list_entry(ent, QE_t, list);
1438c2ecf20Sopenharmony_ci	BUG_ON(BAD_MAGIC(q, QUEUE_MAGIC_USED));
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_ci	SET_MAGIC(q, QUEUE_MAGIC_FREE);
1468c2ecf20Sopenharmony_ci	list_add(ent, &queue->free);
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_ci	return q->SCpnt;
1498c2ecf20Sopenharmony_ci}
1508c2ecf20Sopenharmony_ci
1518c2ecf20Sopenharmony_ci/*
1528c2ecf20Sopenharmony_ci * Function: struct scsi_cmnd *queue_remove_exclude (queue, exclude)
1538c2ecf20Sopenharmony_ci * Purpose : remove a SCSI command from a queue
1548c2ecf20Sopenharmony_ci * Params  : queue   - queue to remove command from
1558c2ecf20Sopenharmony_ci *	     exclude - bit array of target&lun which is busy
1568c2ecf20Sopenharmony_ci * Returns : struct scsi_cmnd if successful (and a reference), or NULL if no command available
1578c2ecf20Sopenharmony_ci */
1588c2ecf20Sopenharmony_cistruct scsi_cmnd *queue_remove_exclude(Queue_t *queue, unsigned long *exclude)
1598c2ecf20Sopenharmony_ci{
1608c2ecf20Sopenharmony_ci	unsigned long flags;
1618c2ecf20Sopenharmony_ci	struct list_head *l;
1628c2ecf20Sopenharmony_ci	struct scsi_cmnd *SCpnt = NULL;
1638c2ecf20Sopenharmony_ci
1648c2ecf20Sopenharmony_ci	spin_lock_irqsave(&queue->queue_lock, flags);
1658c2ecf20Sopenharmony_ci	list_for_each(l, &queue->head) {
1668c2ecf20Sopenharmony_ci		QE_t *q = list_entry(l, QE_t, list);
1678c2ecf20Sopenharmony_ci		if (!test_bit(q->SCpnt->device->id * 8 +
1688c2ecf20Sopenharmony_ci			      (u8)(q->SCpnt->device->lun & 0x7), exclude)) {
1698c2ecf20Sopenharmony_ci			SCpnt = __queue_remove(queue, l);
1708c2ecf20Sopenharmony_ci			break;
1718c2ecf20Sopenharmony_ci		}
1728c2ecf20Sopenharmony_ci	}
1738c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&queue->queue_lock, flags);
1748c2ecf20Sopenharmony_ci
1758c2ecf20Sopenharmony_ci	return SCpnt;
1768c2ecf20Sopenharmony_ci}
1778c2ecf20Sopenharmony_ci
1788c2ecf20Sopenharmony_ci/*
1798c2ecf20Sopenharmony_ci * Function: struct scsi_cmnd *queue_remove (queue)
1808c2ecf20Sopenharmony_ci * Purpose : removes first SCSI command from a queue
1818c2ecf20Sopenharmony_ci * Params  : queue   - queue to remove command from
1828c2ecf20Sopenharmony_ci * Returns : struct scsi_cmnd if successful (and a reference), or NULL if no command available
1838c2ecf20Sopenharmony_ci */
1848c2ecf20Sopenharmony_cistruct scsi_cmnd *queue_remove(Queue_t *queue)
1858c2ecf20Sopenharmony_ci{
1868c2ecf20Sopenharmony_ci	unsigned long flags;
1878c2ecf20Sopenharmony_ci	struct scsi_cmnd *SCpnt = NULL;
1888c2ecf20Sopenharmony_ci
1898c2ecf20Sopenharmony_ci	spin_lock_irqsave(&queue->queue_lock, flags);
1908c2ecf20Sopenharmony_ci	if (!list_empty(&queue->head))
1918c2ecf20Sopenharmony_ci		SCpnt = __queue_remove(queue, queue->head.next);
1928c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&queue->queue_lock, flags);
1938c2ecf20Sopenharmony_ci
1948c2ecf20Sopenharmony_ci	return SCpnt;
1958c2ecf20Sopenharmony_ci}
1968c2ecf20Sopenharmony_ci
1978c2ecf20Sopenharmony_ci/*
1988c2ecf20Sopenharmony_ci * Function: struct scsi_cmnd *queue_remove_tgtluntag (queue, target, lun, tag)
1998c2ecf20Sopenharmony_ci * Purpose : remove a SCSI command from the queue for a specified target/lun/tag
2008c2ecf20Sopenharmony_ci * Params  : queue  - queue to remove command from
2018c2ecf20Sopenharmony_ci *	     target - target that we want
2028c2ecf20Sopenharmony_ci *	     lun    - lun on device
2038c2ecf20Sopenharmony_ci *	     tag    - tag on device
2048c2ecf20Sopenharmony_ci * Returns : struct scsi_cmnd if successful, or NULL if no command satisfies requirements
2058c2ecf20Sopenharmony_ci */
2068c2ecf20Sopenharmony_cistruct scsi_cmnd *queue_remove_tgtluntag(Queue_t *queue, int target, int lun,
2078c2ecf20Sopenharmony_ci					 int tag)
2088c2ecf20Sopenharmony_ci{
2098c2ecf20Sopenharmony_ci	unsigned long flags;
2108c2ecf20Sopenharmony_ci	struct list_head *l;
2118c2ecf20Sopenharmony_ci	struct scsi_cmnd *SCpnt = NULL;
2128c2ecf20Sopenharmony_ci
2138c2ecf20Sopenharmony_ci	spin_lock_irqsave(&queue->queue_lock, flags);
2148c2ecf20Sopenharmony_ci	list_for_each(l, &queue->head) {
2158c2ecf20Sopenharmony_ci		QE_t *q = list_entry(l, QE_t, list);
2168c2ecf20Sopenharmony_ci		if (q->SCpnt->device->id == target && q->SCpnt->device->lun == lun &&
2178c2ecf20Sopenharmony_ci		    q->SCpnt->tag == tag) {
2188c2ecf20Sopenharmony_ci			SCpnt = __queue_remove(queue, l);
2198c2ecf20Sopenharmony_ci			break;
2208c2ecf20Sopenharmony_ci		}
2218c2ecf20Sopenharmony_ci	}
2228c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&queue->queue_lock, flags);
2238c2ecf20Sopenharmony_ci
2248c2ecf20Sopenharmony_ci	return SCpnt;
2258c2ecf20Sopenharmony_ci}
2268c2ecf20Sopenharmony_ci
2278c2ecf20Sopenharmony_ci/*
2288c2ecf20Sopenharmony_ci * Function: queue_remove_all_target(queue, target)
2298c2ecf20Sopenharmony_ci * Purpose : remove all SCSI commands from the queue for a specified target
2308c2ecf20Sopenharmony_ci * Params  : queue  - queue to remove command from
2318c2ecf20Sopenharmony_ci *           target - target device id
2328c2ecf20Sopenharmony_ci * Returns : nothing
2338c2ecf20Sopenharmony_ci */
2348c2ecf20Sopenharmony_civoid queue_remove_all_target(Queue_t *queue, int target)
2358c2ecf20Sopenharmony_ci{
2368c2ecf20Sopenharmony_ci	unsigned long flags;
2378c2ecf20Sopenharmony_ci	struct list_head *l;
2388c2ecf20Sopenharmony_ci
2398c2ecf20Sopenharmony_ci	spin_lock_irqsave(&queue->queue_lock, flags);
2408c2ecf20Sopenharmony_ci	list_for_each(l, &queue->head) {
2418c2ecf20Sopenharmony_ci		QE_t *q = list_entry(l, QE_t, list);
2428c2ecf20Sopenharmony_ci		if (q->SCpnt->device->id == target)
2438c2ecf20Sopenharmony_ci			__queue_remove(queue, l);
2448c2ecf20Sopenharmony_ci	}
2458c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&queue->queue_lock, flags);
2468c2ecf20Sopenharmony_ci}
2478c2ecf20Sopenharmony_ci
2488c2ecf20Sopenharmony_ci/*
2498c2ecf20Sopenharmony_ci * Function: int queue_probetgtlun (queue, target, lun)
2508c2ecf20Sopenharmony_ci * Purpose : check to see if we have a command in the queue for the specified
2518c2ecf20Sopenharmony_ci *	     target/lun.
2528c2ecf20Sopenharmony_ci * Params  : queue  - queue to look in
2538c2ecf20Sopenharmony_ci *	     target - target we want to probe
2548c2ecf20Sopenharmony_ci *	     lun    - lun on target
2558c2ecf20Sopenharmony_ci * Returns : 0 if not found, != 0 if found
2568c2ecf20Sopenharmony_ci */
2578c2ecf20Sopenharmony_ciint queue_probetgtlun (Queue_t *queue, int target, int lun)
2588c2ecf20Sopenharmony_ci{
2598c2ecf20Sopenharmony_ci	unsigned long flags;
2608c2ecf20Sopenharmony_ci	struct list_head *l;
2618c2ecf20Sopenharmony_ci	int found = 0;
2628c2ecf20Sopenharmony_ci
2638c2ecf20Sopenharmony_ci	spin_lock_irqsave(&queue->queue_lock, flags);
2648c2ecf20Sopenharmony_ci	list_for_each(l, &queue->head) {
2658c2ecf20Sopenharmony_ci		QE_t *q = list_entry(l, QE_t, list);
2668c2ecf20Sopenharmony_ci		if (q->SCpnt->device->id == target && q->SCpnt->device->lun == lun) {
2678c2ecf20Sopenharmony_ci			found = 1;
2688c2ecf20Sopenharmony_ci			break;
2698c2ecf20Sopenharmony_ci		}
2708c2ecf20Sopenharmony_ci	}
2718c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&queue->queue_lock, flags);
2728c2ecf20Sopenharmony_ci
2738c2ecf20Sopenharmony_ci	return found;
2748c2ecf20Sopenharmony_ci}
2758c2ecf20Sopenharmony_ci
2768c2ecf20Sopenharmony_ci/*
2778c2ecf20Sopenharmony_ci * Function: int queue_remove_cmd(Queue_t *queue, struct scsi_cmnd *SCpnt)
2788c2ecf20Sopenharmony_ci * Purpose : remove a specific command from the queues
2798c2ecf20Sopenharmony_ci * Params  : queue - queue to look in
2808c2ecf20Sopenharmony_ci *	     SCpnt - command to find
2818c2ecf20Sopenharmony_ci * Returns : 0 if not found
2828c2ecf20Sopenharmony_ci */
2838c2ecf20Sopenharmony_ciint queue_remove_cmd(Queue_t *queue, struct scsi_cmnd *SCpnt)
2848c2ecf20Sopenharmony_ci{
2858c2ecf20Sopenharmony_ci	unsigned long flags;
2868c2ecf20Sopenharmony_ci	struct list_head *l;
2878c2ecf20Sopenharmony_ci	int found = 0;
2888c2ecf20Sopenharmony_ci
2898c2ecf20Sopenharmony_ci	spin_lock_irqsave(&queue->queue_lock, flags);
2908c2ecf20Sopenharmony_ci	list_for_each(l, &queue->head) {
2918c2ecf20Sopenharmony_ci		QE_t *q = list_entry(l, QE_t, list);
2928c2ecf20Sopenharmony_ci		if (q->SCpnt == SCpnt) {
2938c2ecf20Sopenharmony_ci			__queue_remove(queue, l);
2948c2ecf20Sopenharmony_ci			found = 1;
2958c2ecf20Sopenharmony_ci			break;
2968c2ecf20Sopenharmony_ci		}
2978c2ecf20Sopenharmony_ci	}
2988c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&queue->queue_lock, flags);
2998c2ecf20Sopenharmony_ci
3008c2ecf20Sopenharmony_ci	return found;
3018c2ecf20Sopenharmony_ci}
3028c2ecf20Sopenharmony_ci
3038c2ecf20Sopenharmony_ciEXPORT_SYMBOL(queue_initialise);
3048c2ecf20Sopenharmony_ciEXPORT_SYMBOL(queue_free);
3058c2ecf20Sopenharmony_ciEXPORT_SYMBOL(__queue_add);
3068c2ecf20Sopenharmony_ciEXPORT_SYMBOL(queue_remove);
3078c2ecf20Sopenharmony_ciEXPORT_SYMBOL(queue_remove_exclude);
3088c2ecf20Sopenharmony_ciEXPORT_SYMBOL(queue_remove_tgtluntag);
3098c2ecf20Sopenharmony_ciEXPORT_SYMBOL(queue_remove_cmd);
3108c2ecf20Sopenharmony_ciEXPORT_SYMBOL(queue_remove_all_target);
3118c2ecf20Sopenharmony_ciEXPORT_SYMBOL(queue_probetgtlun);
3128c2ecf20Sopenharmony_ci
3138c2ecf20Sopenharmony_ciMODULE_AUTHOR("Russell King");
3148c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("SCSI command queueing");
3158c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
316