/* * scsi_queue.c Copyright (C) 1997 Eric Youngdale * * generic mid-level SCSI queueing. * * The point of this is that we need to track when hosts are unable to * accept a command because they are busy. In addition, we track devices * that cannot accept a command because of a QUEUE_FULL condition. In both * of these cases, we enter the command in the queue. At some later point, * we attempt to remove commands from the queue and retry them. */ #define __NO_VERSION__ #include #include #include #include #include #include #include #include #include #include #include #include #define __KERNEL_SYSCALLS__ #include #include #include #include #include "scsi.h" #include "hosts.h" #include "constants.h" /* * TODO: * 1) Prevent multiple traversals of list to look for commands to * queue. * 2) Protect against multiple insertions of list at the same time. * DONE: * 1) Set state of scsi command to a new state value for ml queue. * 2) Insert into queue when host rejects command. * 3) Make sure status code is properly passed from low-level queue func * so that internal_cmnd properly returns the right value. * 4) Insert into queue when QUEUE_FULL. * 5) Cull queue in bottom half handler. * 6) Check usage count prior to queue insertion. Requeue if usage * count is 0. * 7) Don't send down any more commands if the host/device is busy. */ static const char RCSid[] = "$Header: /cvs/work/hardhat2_1_ar7/linux-2.4.17_mvl21/drivers/scsi/scsi_queue.c,v 1.1.1.1 2003/06/23 22:18:34 jharrell Exp $"; /* * Function: scsi_mlqueue_insert() * * Purpose: Insert a command in the midlevel queue. * * Arguments: cmd - command that we are adding to queue. * reason - why we are inserting command to queue. * * Lock status: Assumed that lock is not held upon entry. * * Returns: Nothing. * * Notes: We do this for one of two cases. Either the host is busy * and it cannot accept any more commands for the time being, * or the device returned QUEUE_FULL and can accept no more * commands. * Notes: This could be called either from an interrupt context or a * normal process context. */ int scsi_mlqueue_insert(Scsi_Cmnd * cmd, int reason) { struct Scsi_Host *host; unsigned long flags; SCSI_LOG_MLQUEUE(1, printk("Inserting command %p into mlqueue\n", cmd)); /* * We are inserting the command into the ml queue. First, we * cancel the timer, so it doesn't time out. */ scsi_delete_timer(cmd); host = cmd->host; /* * Next, set the appropriate busy bit for the device/host. */ if (reason == SCSI_MLQUEUE_HOST_BUSY) { /* * Protect against race conditions. If the host isn't busy, * assume that something actually completed, and that we should * be able to queue a command now. Note that there is an implicit * assumption that every host can always queue at least one command. * If a host is inactive and cannot queue any commands, I don't see * how things could possibly work anyways. */ if (host->host_busy == 0) { if (scsi_retry_command(cmd) == 0) { return 0; } } host->host_blocked = TRUE; } else { /* * Protect against race conditions. If the device isn't busy, * assume that something actually completed, and that we should * be able to queue a command now. Note that there is an implicit * assumption that every host can always queue at least one command. * If a host is inactive and cannot queue any commands, I don't see * how things could possibly work anyways. */ if (cmd->device->device_busy == 0) { if (scsi_retry_command(cmd) == 0) { return 0; } } cmd->device->device_blocked = TRUE; } /* * Register the fact that we own the thing for now. */ cmd->state = SCSI_STATE_MLQUEUE; cmd->owner = SCSI_OWNER_MIDLEVEL; cmd->bh_next = NULL; /* * Decrement the counters, since these commands are no longer * active on the host/device. */ spin_lock_irqsave(&io_request_lock, flags); cmd->host->host_busy--; cmd->device->device_busy--; spin_unlock_irqrestore(&io_request_lock, flags); /* * Insert this command at the head of the queue for it's device. * It will go before all other commands that are already in the queue. */ scsi_insert_special_cmd(cmd, 1); return 0; }