linux/drivers/scsi/arm/queue.c
<<
>>
Prefs
   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.h"
  24
  25#define DEBUG
  26
  27typedef struct queue_entry {
  28        struct list_head   list;
  29        struct scsi_cmnd   *SCpnt;
  30#ifdef DEBUG
  31        unsigned long      magic;
  32#endif
  33} QE_t;
  34
  35#ifdef DEBUG
  36#define QUEUE_MAGIC_FREE        0xf7e1c9a3
  37#define QUEUE_MAGIC_USED        0xf7e1cc33
  38
  39#define SET_MAGIC(q,m)  ((q)->magic = (m))
  40#define BAD_MAGIC(q,m)  ((q)->magic != (m))
  41#else
  42#define SET_MAGIC(q,m)  do { } while (0)
  43#define BAD_MAGIC(q,m)  (0)
  44#endif
  45
  46#include "queue.h"
  47
  48#define NR_QE   32
  49
  50/*
  51 * Function: void queue_initialise (Queue_t *queue)
  52 * Purpose : initialise a queue
  53 * Params  : queue - queue to initialise
  54 */
  55int queue_initialise (Queue_t *queue)
  56{
  57        unsigned int nqueues = NR_QE;
  58        QE_t *q;
  59
  60        spin_lock_init(&queue->queue_lock);
  61        INIT_LIST_HEAD(&queue->head);
  62        INIT_LIST_HEAD(&queue->free);
  63
  64        /*
  65         * If life was easier, then SCpnt would have a
  66         * host-available list head, and we wouldn't
  67         * need to keep free lists or allocate this
  68         * memory.
  69         */
  70        queue->alloc = q = kmalloc_array(nqueues, sizeof(QE_t), GFP_KERNEL);
  71        if (q) {
  72                for (; nqueues; q++, nqueues--) {
  73                        SET_MAGIC(q, QUEUE_MAGIC_FREE);
  74                        q->SCpnt = NULL;
  75                        list_add(&q->list, &queue->free);
  76                }
  77        }
  78
  79        return queue->alloc != NULL;
  80}
  81
  82/*
  83 * Function: void queue_free (Queue_t *queue)
  84 * Purpose : free a queue
  85 * Params  : queue - queue to free
  86 */
  87void queue_free (Queue_t *queue)
  88{
  89        if (!list_empty(&queue->head))
  90                printk(KERN_WARNING "freeing non-empty queue %p\n", queue);
  91        kfree(queue->alloc);
  92}
  93     
  94
  95/*
  96 * Function: int __queue_add(Queue_t *queue, struct scsi_cmnd *SCpnt, int head)
  97 * Purpose : Add a new command onto a queue, adding REQUEST_SENSE to head.
  98 * Params  : queue - destination queue
  99 *           SCpnt - command to add
 100 *           head  - add command to head of queue
 101 * Returns : 0 on error, !0 on success
 102 */
 103int __queue_add(Queue_t *queue, struct scsi_cmnd *SCpnt, int head)
 104{
 105        unsigned long flags;
 106        struct list_head *l;
 107        QE_t *q;
 108        int ret = 0;
 109
 110        spin_lock_irqsave(&queue->queue_lock, flags);
 111        if (list_empty(&queue->free))
 112                goto empty;
 113
 114        l = queue->free.next;
 115        list_del(l);
 116
 117        q = list_entry(l, QE_t, list);
 118        BUG_ON(BAD_MAGIC(q, QUEUE_MAGIC_FREE));
 119
 120        SET_MAGIC(q, QUEUE_MAGIC_USED);
 121        q->SCpnt = SCpnt;
 122
 123        if (head)
 124                list_add(l, &queue->head);
 125        else
 126                list_add_tail(l, &queue->head);
 127
 128        ret = 1;
 129empty:
 130        spin_unlock_irqrestore(&queue->queue_lock, flags);
 131        return ret;
 132}
 133
 134static struct scsi_cmnd *__queue_remove(Queue_t *queue, struct list_head *ent)
 135{
 136        QE_t *q;
 137
 138        /*
 139         * Move the entry from the "used" list onto the "free" list
 140         */
 141        list_del(ent);
 142        q = list_entry(ent, QE_t, list);
 143        BUG_ON(BAD_MAGIC(q, QUEUE_MAGIC_USED));
 144
 145        SET_MAGIC(q, QUEUE_MAGIC_FREE);
 146        list_add(ent, &queue->free);
 147
 148        return q->SCpnt;
 149}
 150
 151/*
 152 * Function: struct scsi_cmnd *queue_remove_exclude (queue, exclude)
 153 * Purpose : remove a SCSI command from a queue
 154 * Params  : queue   - queue to remove command from
 155 *           exclude - bit array of target&lun which is busy
 156 * Returns : struct scsi_cmnd if successful (and a reference), or NULL if no command available
 157 */
 158struct scsi_cmnd *queue_remove_exclude(Queue_t *queue, unsigned long *exclude)
 159{
 160        unsigned long flags;
 161        struct list_head *l;
 162        struct scsi_cmnd *SCpnt = NULL;
 163
 164        spin_lock_irqsave(&queue->queue_lock, flags);
 165        list_for_each(l, &queue->head) {
 166                QE_t *q = list_entry(l, QE_t, list);
 167                if (!test_bit(q->SCpnt->device->id * 8 +
 168                              (u8)(q->SCpnt->device->lun & 0x7), exclude)) {
 169                        SCpnt = __queue_remove(queue, l);
 170                        break;
 171                }
 172        }
 173        spin_unlock_irqrestore(&queue->queue_lock, flags);
 174
 175        return SCpnt;
 176}
 177
 178/*
 179 * Function: struct scsi_cmnd *queue_remove (queue)
 180 * Purpose : removes first SCSI command from a queue
 181 * Params  : queue   - queue to remove command from
 182 * Returns : struct scsi_cmnd if successful (and a reference), or NULL if no command available
 183 */
 184struct scsi_cmnd *queue_remove(Queue_t *queue)
 185{
 186        unsigned long flags;
 187        struct scsi_cmnd *SCpnt = NULL;
 188
 189        spin_lock_irqsave(&queue->queue_lock, flags);
 190        if (!list_empty(&queue->head))
 191                SCpnt = __queue_remove(queue, queue->head.next);
 192        spin_unlock_irqrestore(&queue->queue_lock, flags);
 193
 194        return SCpnt;
 195}
 196
 197/*
 198 * Function: struct scsi_cmnd *queue_remove_tgtluntag (queue, target, lun, tag)
 199 * Purpose : remove a SCSI command from the queue for a specified target/lun/tag
 200 * Params  : queue  - queue to remove command from
 201 *           target - target that we want
 202 *           lun    - lun on device
 203 *           tag    - tag on device
 204 * Returns : struct scsi_cmnd if successful, or NULL if no command satisfies requirements
 205 */
 206struct scsi_cmnd *queue_remove_tgtluntag(Queue_t *queue, int target, int lun,
 207                                         int tag)
 208{
 209        unsigned long flags;
 210        struct list_head *l;
 211        struct scsi_cmnd *SCpnt = NULL;
 212
 213        spin_lock_irqsave(&queue->queue_lock, flags);
 214        list_for_each(l, &queue->head) {
 215                QE_t *q = list_entry(l, QE_t, list);
 216                if (q->SCpnt->device->id == target && q->SCpnt->device->lun == lun &&
 217                    q->SCpnt->tag == tag) {
 218                        SCpnt = __queue_remove(queue, l);
 219                        break;
 220                }
 221        }
 222        spin_unlock_irqrestore(&queue->queue_lock, flags);
 223
 224        return SCpnt;
 225}
 226
 227/*
 228 * Function: queue_remove_all_target(queue, target)
 229 * Purpose : remove all SCSI commands from the queue for a specified target
 230 * Params  : queue  - queue to remove command from
 231 *           target - target device id
 232 * Returns : nothing
 233 */
 234void queue_remove_all_target(Queue_t *queue, int target)
 235{
 236        unsigned long flags;
 237        struct list_head *l;
 238
 239        spin_lock_irqsave(&queue->queue_lock, flags);
 240        list_for_each(l, &queue->head) {
 241                QE_t *q = list_entry(l, QE_t, list);
 242                if (q->SCpnt->device->id == target)
 243                        __queue_remove(queue, l);
 244        }
 245        spin_unlock_irqrestore(&queue->queue_lock, flags);
 246}
 247
 248/*
 249 * Function: int queue_probetgtlun (queue, target, lun)
 250 * Purpose : check to see if we have a command in the queue for the specified
 251 *           target/lun.
 252 * Params  : queue  - queue to look in
 253 *           target - target we want to probe
 254 *           lun    - lun on target
 255 * Returns : 0 if not found, != 0 if found
 256 */
 257int queue_probetgtlun (Queue_t *queue, int target, int lun)
 258{
 259        unsigned long flags;
 260        struct list_head *l;
 261        int found = 0;
 262
 263        spin_lock_irqsave(&queue->queue_lock, flags);
 264        list_for_each(l, &queue->head) {
 265                QE_t *q = list_entry(l, QE_t, list);
 266                if (q->SCpnt->device->id == target && q->SCpnt->device->lun == lun) {
 267                        found = 1;
 268                        break;
 269                }
 270        }
 271        spin_unlock_irqrestore(&queue->queue_lock, flags);
 272
 273        return found;
 274}
 275
 276/*
 277 * Function: int queue_remove_cmd(Queue_t *queue, struct scsi_cmnd *SCpnt)
 278 * Purpose : remove a specific command from the queues
 279 * Params  : queue - queue to look in
 280 *           SCpnt - command to find
 281 * Returns : 0 if not found
 282 */
 283int queue_remove_cmd(Queue_t *queue, struct scsi_cmnd *SCpnt)
 284{
 285        unsigned long flags;
 286        struct list_head *l;
 287        int found = 0;
 288
 289        spin_lock_irqsave(&queue->queue_lock, flags);
 290        list_for_each(l, &queue->head) {
 291                QE_t *q = list_entry(l, QE_t, list);
 292                if (q->SCpnt == SCpnt) {
 293                        __queue_remove(queue, l);
 294                        found = 1;
 295                        break;
 296                }
 297        }
 298        spin_unlock_irqrestore(&queue->queue_lock, flags);
 299
 300        return found;
 301}
 302
 303EXPORT_SYMBOL(queue_initialise);
 304EXPORT_SYMBOL(queue_free);
 305EXPORT_SYMBOL(__queue_add);
 306EXPORT_SYMBOL(queue_remove);
 307EXPORT_SYMBOL(queue_remove_exclude);
 308EXPORT_SYMBOL(queue_remove_tgtluntag);
 309EXPORT_SYMBOL(queue_remove_cmd);
 310EXPORT_SYMBOL(queue_remove_all_target);
 311EXPORT_SYMBOL(queue_probetgtlun);
 312
 313MODULE_AUTHOR("Russell King");
 314MODULE_DESCRIPTION("SCSI command queueing");
 315MODULE_LICENSE("GPL");
 316