162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * linux/drivers/acorn/scsi/queue.c: queue handling primitives 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 1997-2000 Russell King 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Changelog: 862306a36Sopenharmony_ci * 15-Sep-1997 RMK Created. 962306a36Sopenharmony_ci * 11-Oct-1997 RMK Corrected problem with queue_remove_exclude 1062306a36Sopenharmony_ci * not updating internal linked list properly 1162306a36Sopenharmony_ci * (was causing commands to go missing). 1262306a36Sopenharmony_ci * 30-Aug-2000 RMK Use Linux list handling and spinlocks 1362306a36Sopenharmony_ci */ 1462306a36Sopenharmony_ci#include <linux/module.h> 1562306a36Sopenharmony_ci#include <linux/blkdev.h> 1662306a36Sopenharmony_ci#include <linux/kernel.h> 1762306a36Sopenharmony_ci#include <linux/string.h> 1862306a36Sopenharmony_ci#include <linux/slab.h> 1962306a36Sopenharmony_ci#include <linux/spinlock.h> 2062306a36Sopenharmony_ci#include <linux/list.h> 2162306a36Sopenharmony_ci#include <linux/init.h> 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci#include <scsi/scsi.h> 2462306a36Sopenharmony_ci#include <scsi/scsi_cmnd.h> 2562306a36Sopenharmony_ci#include <scsi/scsi_device.h> 2662306a36Sopenharmony_ci#include <scsi/scsi_eh.h> 2762306a36Sopenharmony_ci#include <scsi/scsi_tcq.h> 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci#define DEBUG 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_citypedef struct queue_entry { 3262306a36Sopenharmony_ci struct list_head list; 3362306a36Sopenharmony_ci struct scsi_cmnd *SCpnt; 3462306a36Sopenharmony_ci#ifdef DEBUG 3562306a36Sopenharmony_ci unsigned long magic; 3662306a36Sopenharmony_ci#endif 3762306a36Sopenharmony_ci} QE_t; 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ci#ifdef DEBUG 4062306a36Sopenharmony_ci#define QUEUE_MAGIC_FREE 0xf7e1c9a3 4162306a36Sopenharmony_ci#define QUEUE_MAGIC_USED 0xf7e1cc33 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci#define SET_MAGIC(q,m) ((q)->magic = (m)) 4462306a36Sopenharmony_ci#define BAD_MAGIC(q,m) ((q)->magic != (m)) 4562306a36Sopenharmony_ci#else 4662306a36Sopenharmony_ci#define SET_MAGIC(q,m) do { } while (0) 4762306a36Sopenharmony_ci#define BAD_MAGIC(q,m) (0) 4862306a36Sopenharmony_ci#endif 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci#include "queue.h" 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci#define NR_QE 32 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci/* 5562306a36Sopenharmony_ci * Function: void queue_initialise (Queue_t *queue) 5662306a36Sopenharmony_ci * Purpose : initialise a queue 5762306a36Sopenharmony_ci * Params : queue - queue to initialise 5862306a36Sopenharmony_ci */ 5962306a36Sopenharmony_ciint queue_initialise (Queue_t *queue) 6062306a36Sopenharmony_ci{ 6162306a36Sopenharmony_ci unsigned int nqueues = NR_QE; 6262306a36Sopenharmony_ci QE_t *q; 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci spin_lock_init(&queue->queue_lock); 6562306a36Sopenharmony_ci INIT_LIST_HEAD(&queue->head); 6662306a36Sopenharmony_ci INIT_LIST_HEAD(&queue->free); 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci /* 6962306a36Sopenharmony_ci * If life was easier, then SCpnt would have a 7062306a36Sopenharmony_ci * host-available list head, and we wouldn't 7162306a36Sopenharmony_ci * need to keep free lists or allocate this 7262306a36Sopenharmony_ci * memory. 7362306a36Sopenharmony_ci */ 7462306a36Sopenharmony_ci queue->alloc = q = kmalloc_array(nqueues, sizeof(QE_t), GFP_KERNEL); 7562306a36Sopenharmony_ci if (q) { 7662306a36Sopenharmony_ci for (; nqueues; q++, nqueues--) { 7762306a36Sopenharmony_ci SET_MAGIC(q, QUEUE_MAGIC_FREE); 7862306a36Sopenharmony_ci q->SCpnt = NULL; 7962306a36Sopenharmony_ci list_add(&q->list, &queue->free); 8062306a36Sopenharmony_ci } 8162306a36Sopenharmony_ci } 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci return queue->alloc != NULL; 8462306a36Sopenharmony_ci} 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci/* 8762306a36Sopenharmony_ci * Function: void queue_free (Queue_t *queue) 8862306a36Sopenharmony_ci * Purpose : free a queue 8962306a36Sopenharmony_ci * Params : queue - queue to free 9062306a36Sopenharmony_ci */ 9162306a36Sopenharmony_civoid queue_free (Queue_t *queue) 9262306a36Sopenharmony_ci{ 9362306a36Sopenharmony_ci if (!list_empty(&queue->head)) 9462306a36Sopenharmony_ci printk(KERN_WARNING "freeing non-empty queue %p\n", queue); 9562306a36Sopenharmony_ci kfree(queue->alloc); 9662306a36Sopenharmony_ci} 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci/* 10062306a36Sopenharmony_ci * Function: int __queue_add(Queue_t *queue, struct scsi_cmnd *SCpnt, int head) 10162306a36Sopenharmony_ci * Purpose : Add a new command onto a queue, adding REQUEST_SENSE to head. 10262306a36Sopenharmony_ci * Params : queue - destination queue 10362306a36Sopenharmony_ci * SCpnt - command to add 10462306a36Sopenharmony_ci * head - add command to head of queue 10562306a36Sopenharmony_ci * Returns : 0 on error, !0 on success 10662306a36Sopenharmony_ci */ 10762306a36Sopenharmony_ciint __queue_add(Queue_t *queue, struct scsi_cmnd *SCpnt, int head) 10862306a36Sopenharmony_ci{ 10962306a36Sopenharmony_ci unsigned long flags; 11062306a36Sopenharmony_ci struct list_head *l; 11162306a36Sopenharmony_ci QE_t *q; 11262306a36Sopenharmony_ci int ret = 0; 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci spin_lock_irqsave(&queue->queue_lock, flags); 11562306a36Sopenharmony_ci if (list_empty(&queue->free)) 11662306a36Sopenharmony_ci goto empty; 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci l = queue->free.next; 11962306a36Sopenharmony_ci list_del(l); 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci q = list_entry(l, QE_t, list); 12262306a36Sopenharmony_ci BUG_ON(BAD_MAGIC(q, QUEUE_MAGIC_FREE)); 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci SET_MAGIC(q, QUEUE_MAGIC_USED); 12562306a36Sopenharmony_ci q->SCpnt = SCpnt; 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci if (head) 12862306a36Sopenharmony_ci list_add(l, &queue->head); 12962306a36Sopenharmony_ci else 13062306a36Sopenharmony_ci list_add_tail(l, &queue->head); 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci ret = 1; 13362306a36Sopenharmony_ciempty: 13462306a36Sopenharmony_ci spin_unlock_irqrestore(&queue->queue_lock, flags); 13562306a36Sopenharmony_ci return ret; 13662306a36Sopenharmony_ci} 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_cistatic struct scsi_cmnd *__queue_remove(Queue_t *queue, struct list_head *ent) 13962306a36Sopenharmony_ci{ 14062306a36Sopenharmony_ci QE_t *q; 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci /* 14362306a36Sopenharmony_ci * Move the entry from the "used" list onto the "free" list 14462306a36Sopenharmony_ci */ 14562306a36Sopenharmony_ci list_del(ent); 14662306a36Sopenharmony_ci q = list_entry(ent, QE_t, list); 14762306a36Sopenharmony_ci BUG_ON(BAD_MAGIC(q, QUEUE_MAGIC_USED)); 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci SET_MAGIC(q, QUEUE_MAGIC_FREE); 15062306a36Sopenharmony_ci list_add(ent, &queue->free); 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci return q->SCpnt; 15362306a36Sopenharmony_ci} 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci/* 15662306a36Sopenharmony_ci * Function: struct scsi_cmnd *queue_remove_exclude (queue, exclude) 15762306a36Sopenharmony_ci * Purpose : remove a SCSI command from a queue 15862306a36Sopenharmony_ci * Params : queue - queue to remove command from 15962306a36Sopenharmony_ci * exclude - bit array of target&lun which is busy 16062306a36Sopenharmony_ci * Returns : struct scsi_cmnd if successful (and a reference), or NULL if no command available 16162306a36Sopenharmony_ci */ 16262306a36Sopenharmony_cistruct scsi_cmnd *queue_remove_exclude(Queue_t *queue, unsigned long *exclude) 16362306a36Sopenharmony_ci{ 16462306a36Sopenharmony_ci unsigned long flags; 16562306a36Sopenharmony_ci struct list_head *l; 16662306a36Sopenharmony_ci struct scsi_cmnd *SCpnt = NULL; 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci spin_lock_irqsave(&queue->queue_lock, flags); 16962306a36Sopenharmony_ci list_for_each(l, &queue->head) { 17062306a36Sopenharmony_ci QE_t *q = list_entry(l, QE_t, list); 17162306a36Sopenharmony_ci if (!test_bit(q->SCpnt->device->id * 8 + 17262306a36Sopenharmony_ci (u8)(q->SCpnt->device->lun & 0x7), exclude)) { 17362306a36Sopenharmony_ci SCpnt = __queue_remove(queue, l); 17462306a36Sopenharmony_ci break; 17562306a36Sopenharmony_ci } 17662306a36Sopenharmony_ci } 17762306a36Sopenharmony_ci spin_unlock_irqrestore(&queue->queue_lock, flags); 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci return SCpnt; 18062306a36Sopenharmony_ci} 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci/* 18362306a36Sopenharmony_ci * Function: struct scsi_cmnd *queue_remove (queue) 18462306a36Sopenharmony_ci * Purpose : removes first SCSI command from a queue 18562306a36Sopenharmony_ci * Params : queue - queue to remove command from 18662306a36Sopenharmony_ci * Returns : struct scsi_cmnd if successful (and a reference), or NULL if no command available 18762306a36Sopenharmony_ci */ 18862306a36Sopenharmony_cistruct scsi_cmnd *queue_remove(Queue_t *queue) 18962306a36Sopenharmony_ci{ 19062306a36Sopenharmony_ci unsigned long flags; 19162306a36Sopenharmony_ci struct scsi_cmnd *SCpnt = NULL; 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci spin_lock_irqsave(&queue->queue_lock, flags); 19462306a36Sopenharmony_ci if (!list_empty(&queue->head)) 19562306a36Sopenharmony_ci SCpnt = __queue_remove(queue, queue->head.next); 19662306a36Sopenharmony_ci spin_unlock_irqrestore(&queue->queue_lock, flags); 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ci return SCpnt; 19962306a36Sopenharmony_ci} 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ci/* 20262306a36Sopenharmony_ci * Function: struct scsi_cmnd *queue_remove_tgtluntag (queue, target, lun, tag) 20362306a36Sopenharmony_ci * Purpose : remove a SCSI command from the queue for a specified target/lun/tag 20462306a36Sopenharmony_ci * Params : queue - queue to remove command from 20562306a36Sopenharmony_ci * target - target that we want 20662306a36Sopenharmony_ci * lun - lun on device 20762306a36Sopenharmony_ci * tag - tag on device 20862306a36Sopenharmony_ci * Returns : struct scsi_cmnd if successful, or NULL if no command satisfies requirements 20962306a36Sopenharmony_ci */ 21062306a36Sopenharmony_cistruct scsi_cmnd *queue_remove_tgtluntag(Queue_t *queue, int target, int lun, 21162306a36Sopenharmony_ci int tag) 21262306a36Sopenharmony_ci{ 21362306a36Sopenharmony_ci unsigned long flags; 21462306a36Sopenharmony_ci struct list_head *l; 21562306a36Sopenharmony_ci struct scsi_cmnd *SCpnt = NULL; 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci spin_lock_irqsave(&queue->queue_lock, flags); 21862306a36Sopenharmony_ci list_for_each(l, &queue->head) { 21962306a36Sopenharmony_ci QE_t *q = list_entry(l, QE_t, list); 22062306a36Sopenharmony_ci if (q->SCpnt->device->id == target && q->SCpnt->device->lun == lun && 22162306a36Sopenharmony_ci scsi_cmd_to_rq(q->SCpnt)->tag == tag) { 22262306a36Sopenharmony_ci SCpnt = __queue_remove(queue, l); 22362306a36Sopenharmony_ci break; 22462306a36Sopenharmony_ci } 22562306a36Sopenharmony_ci } 22662306a36Sopenharmony_ci spin_unlock_irqrestore(&queue->queue_lock, flags); 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ci return SCpnt; 22962306a36Sopenharmony_ci} 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci/* 23262306a36Sopenharmony_ci * Function: queue_remove_all_target(queue, target) 23362306a36Sopenharmony_ci * Purpose : remove all SCSI commands from the queue for a specified target 23462306a36Sopenharmony_ci * Params : queue - queue to remove command from 23562306a36Sopenharmony_ci * target - target device id 23662306a36Sopenharmony_ci * Returns : nothing 23762306a36Sopenharmony_ci */ 23862306a36Sopenharmony_civoid queue_remove_all_target(Queue_t *queue, int target) 23962306a36Sopenharmony_ci{ 24062306a36Sopenharmony_ci unsigned long flags; 24162306a36Sopenharmony_ci struct list_head *l; 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci spin_lock_irqsave(&queue->queue_lock, flags); 24462306a36Sopenharmony_ci list_for_each(l, &queue->head) { 24562306a36Sopenharmony_ci QE_t *q = list_entry(l, QE_t, list); 24662306a36Sopenharmony_ci if (q->SCpnt->device->id == target) 24762306a36Sopenharmony_ci __queue_remove(queue, l); 24862306a36Sopenharmony_ci } 24962306a36Sopenharmony_ci spin_unlock_irqrestore(&queue->queue_lock, flags); 25062306a36Sopenharmony_ci} 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci/* 25362306a36Sopenharmony_ci * Function: int queue_probetgtlun (queue, target, lun) 25462306a36Sopenharmony_ci * Purpose : check to see if we have a command in the queue for the specified 25562306a36Sopenharmony_ci * target/lun. 25662306a36Sopenharmony_ci * Params : queue - queue to look in 25762306a36Sopenharmony_ci * target - target we want to probe 25862306a36Sopenharmony_ci * lun - lun on target 25962306a36Sopenharmony_ci * Returns : 0 if not found, != 0 if found 26062306a36Sopenharmony_ci */ 26162306a36Sopenharmony_ciint queue_probetgtlun (Queue_t *queue, int target, int lun) 26262306a36Sopenharmony_ci{ 26362306a36Sopenharmony_ci unsigned long flags; 26462306a36Sopenharmony_ci struct list_head *l; 26562306a36Sopenharmony_ci int found = 0; 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci spin_lock_irqsave(&queue->queue_lock, flags); 26862306a36Sopenharmony_ci list_for_each(l, &queue->head) { 26962306a36Sopenharmony_ci QE_t *q = list_entry(l, QE_t, list); 27062306a36Sopenharmony_ci if (q->SCpnt->device->id == target && q->SCpnt->device->lun == lun) { 27162306a36Sopenharmony_ci found = 1; 27262306a36Sopenharmony_ci break; 27362306a36Sopenharmony_ci } 27462306a36Sopenharmony_ci } 27562306a36Sopenharmony_ci spin_unlock_irqrestore(&queue->queue_lock, flags); 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_ci return found; 27862306a36Sopenharmony_ci} 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_ci/* 28162306a36Sopenharmony_ci * Function: int queue_remove_cmd(Queue_t *queue, struct scsi_cmnd *SCpnt) 28262306a36Sopenharmony_ci * Purpose : remove a specific command from the queues 28362306a36Sopenharmony_ci * Params : queue - queue to look in 28462306a36Sopenharmony_ci * SCpnt - command to find 28562306a36Sopenharmony_ci * Returns : 0 if not found 28662306a36Sopenharmony_ci */ 28762306a36Sopenharmony_ciint queue_remove_cmd(Queue_t *queue, struct scsi_cmnd *SCpnt) 28862306a36Sopenharmony_ci{ 28962306a36Sopenharmony_ci unsigned long flags; 29062306a36Sopenharmony_ci struct list_head *l; 29162306a36Sopenharmony_ci int found = 0; 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_ci spin_lock_irqsave(&queue->queue_lock, flags); 29462306a36Sopenharmony_ci list_for_each(l, &queue->head) { 29562306a36Sopenharmony_ci QE_t *q = list_entry(l, QE_t, list); 29662306a36Sopenharmony_ci if (q->SCpnt == SCpnt) { 29762306a36Sopenharmony_ci __queue_remove(queue, l); 29862306a36Sopenharmony_ci found = 1; 29962306a36Sopenharmony_ci break; 30062306a36Sopenharmony_ci } 30162306a36Sopenharmony_ci } 30262306a36Sopenharmony_ci spin_unlock_irqrestore(&queue->queue_lock, flags); 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_ci return found; 30562306a36Sopenharmony_ci} 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_ciEXPORT_SYMBOL(queue_initialise); 30862306a36Sopenharmony_ciEXPORT_SYMBOL(queue_free); 30962306a36Sopenharmony_ciEXPORT_SYMBOL(__queue_add); 31062306a36Sopenharmony_ciEXPORT_SYMBOL(queue_remove); 31162306a36Sopenharmony_ciEXPORT_SYMBOL(queue_remove_exclude); 31262306a36Sopenharmony_ciEXPORT_SYMBOL(queue_remove_tgtluntag); 31362306a36Sopenharmony_ciEXPORT_SYMBOL(queue_remove_cmd); 31462306a36Sopenharmony_ciEXPORT_SYMBOL(queue_remove_all_target); 31562306a36Sopenharmony_ciEXPORT_SYMBOL(queue_probetgtlun); 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_ciMODULE_AUTHOR("Russell King"); 31862306a36Sopenharmony_ciMODULE_DESCRIPTION("SCSI command queueing"); 31962306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 320