cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

queue.c (8119B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 *  linux/drivers/acorn/scsi/queue.c: queue handling primitives
      4 *
      5 *  Copyright (C) 1997-2000 Russell King
      6 *
      7 *  Changelog:
      8 *   15-Sep-1997 RMK	Created.
      9 *   11-Oct-1997 RMK	Corrected problem with queue_remove_exclude
     10 *			not updating internal linked list properly
     11 *			(was causing commands to go missing).
     12 *   30-Aug-2000 RMK	Use Linux list handling and spinlocks
     13 */
     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 DEBUG
     30
     31typedef struct queue_entry {
     32	struct list_head   list;
     33	struct scsi_cmnd   *SCpnt;
     34#ifdef DEBUG
     35	unsigned long	   magic;
     36#endif
     37} QE_t;
     38
     39#ifdef DEBUG
     40#define QUEUE_MAGIC_FREE	0xf7e1c9a3
     41#define QUEUE_MAGIC_USED	0xf7e1cc33
     42
     43#define SET_MAGIC(q,m)	((q)->magic = (m))
     44#define BAD_MAGIC(q,m)	((q)->magic != (m))
     45#else
     46#define SET_MAGIC(q,m)	do { } while (0)
     47#define BAD_MAGIC(q,m)	(0)
     48#endif
     49
     50#include "queue.h"
     51
     52#define NR_QE	32
     53
     54/*
     55 * Function: void queue_initialise (Queue_t *queue)
     56 * Purpose : initialise a queue
     57 * Params  : queue - queue to initialise
     58 */
     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 a
     70	 * host-available list head, and we wouldn't
     71	 * need to keep free lists or allocate this
     72	 * 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 queue
     89 * Params  : queue - queue to free
     90 */
     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 queue
    103 *	     SCpnt - command to add
    104 *	     head  - add command to head of queue
    105 * Returns : 0 on error, !0 on success
    106 */
    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	else
    130		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" list
    144	 */
    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 queue
    158 * Params  : queue   - queue to remove command from
    159 *	     exclude - bit array of target&lun which is busy
    160 * Returns : struct scsi_cmnd if successful (and a reference), or NULL if no command available
    161 */
    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 queue
    185 * Params  : queue   - queue to remove command from
    186 * Returns : struct scsi_cmnd if successful (and a reference), or NULL if no command available
    187 */
    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/tag
    204 * Params  : queue  - queue to remove command from
    205 *	     target - target that we want
    206 *	     lun    - lun on device
    207 *	     tag    - tag on device
    208 * Returns : struct scsi_cmnd if successful, or NULL if no command satisfies requirements
    209 */
    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 target
    234 * Params  : queue  - queue to remove command from
    235 *           target - target device id
    236 * Returns : nothing
    237 */
    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 specified
    255 *	     target/lun.
    256 * Params  : queue  - queue to look in
    257 *	     target - target we want to probe
    258 *	     lun    - lun on target
    259 * Returns : 0 if not found, != 0 if found
    260 */
    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 queues
    283 * Params  : queue - queue to look in
    284 *	     SCpnt - command to find
    285 * Returns : 0 if not found
    286 */
    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");