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