scsi_lib.c
上传用户:lgb322
上传日期:2013-02-24
资源大小:30529k
文件大小:34k
- /*
- * scsi_lib.c Copyright (C) 1999 Eric Youngdale
- *
- * SCSI queueing library.
- * Initial versions: Eric Youngdale (eric@andante.org).
- * Based upon conversations with large numbers
- * of people at Linux Expo.
- */
- /*
- * The fundamental purpose of this file is to contain a library of utility
- * routines that can be used by low-level drivers. Ultimately the idea
- * is that there should be a sufficiently rich number of functions that it
- * would be possible for a driver author to fashion a queueing function for
- * a low-level driver if they wished. Note however that this file also
- * contains the "default" versions of these functions, as we don't want to
- * go through and retrofit queueing functions into all 30 some-odd drivers.
- */
- #define __NO_VERSION__
- #include <linux/module.h>
- #include <linux/sched.h>
- #include <linux/timer.h>
- #include <linux/string.h>
- #include <linux/slab.h>
- #include <linux/ioport.h>
- #include <linux/kernel.h>
- #include <linux/stat.h>
- #include <linux/blk.h>
- #include <linux/interrupt.h>
- #include <linux/delay.h>
- #include <linux/smp_lock.h>
- #include <linux/completion.h>
- #define __KERNEL_SYSCALLS__
- #include <linux/unistd.h>
- #include <asm/system.h>
- #include <asm/irq.h>
- #include <asm/dma.h>
- #include "scsi.h"
- #include "hosts.h"
- #include "constants.h"
- #include <scsi/scsi_ioctl.h>
- /*
- * This entire source file deals with the new queueing code.
- */
- /*
- * Function: __scsi_insert_special()
- *
- * Purpose: worker for scsi_insert_special_*()
- *
- * Arguments: q - request queue where request should be inserted
- * rq - request to be inserted
- * data - private data
- * at_head - insert request at head or tail of queue
- *
- * Lock status: Assumed that io_request_lock is not held upon entry.
- *
- * Returns: Nothing
- */
- static void __scsi_insert_special(request_queue_t *q, struct request *rq,
- void *data, int at_head)
- {
- unsigned long flags;
- ASSERT_LOCK(&io_request_lock, 0);
- rq->cmd = SPECIAL;
- rq->special = data;
- rq->q = NULL;
- rq->nr_segments = 0;
- rq->elevator_sequence = 0;
- /*
- * We have the option of inserting the head or the tail of the queue.
- * Typically we use the tail for new ioctls and so forth. We use the
- * head of the queue for things like a QUEUE_FULL message from a
- * device, or a host that is unable to accept a particular command.
- */
- spin_lock_irqsave(&io_request_lock, flags);
- if (at_head)
- list_add(&rq->queue, &q->queue_head);
- else
- list_add_tail(&rq->queue, &q->queue_head);
- q->request_fn(q);
- spin_unlock_irqrestore(&io_request_lock, flags);
- }
- /*
- * Function: scsi_insert_special_cmd()
- *
- * Purpose: Insert pre-formed command into request queue.
- *
- * Arguments: SCpnt - command that is ready to be queued.
- * at_head - boolean. True if we should insert at head
- * of queue, false if we should insert at tail.
- *
- * Lock status: Assumed that lock is not held upon entry.
- *
- * Returns: Nothing
- *
- * Notes: This function is called from character device and from
- * ioctl types of functions where the caller knows exactly
- * what SCSI command needs to be issued. The idea is that
- * we merely inject the command into the queue (at the head
- * for now), and then call the queue request function to actually
- * process it.
- */
- int scsi_insert_special_cmd(Scsi_Cmnd * SCpnt, int at_head)
- {
- request_queue_t *q = &SCpnt->device->request_queue;
- __scsi_insert_special(q, &SCpnt->request, SCpnt, at_head);
- return 0;
- }
- /*
- * Function: scsi_insert_special_req()
- *
- * Purpose: Insert pre-formed request into request queue.
- *
- * Arguments: SRpnt - request that is ready to be queued.
- * at_head - boolean. True if we should insert at head
- * of queue, false if we should insert at tail.
- *
- * Lock status: Assumed that lock is not held upon entry.
- *
- * Returns: Nothing
- *
- * Notes: This function is called from character device and from
- * ioctl types of functions where the caller knows exactly
- * what SCSI command needs to be issued. The idea is that
- * we merely inject the command into the queue (at the head
- * for now), and then call the queue request function to actually
- * process it.
- */
- int scsi_insert_special_req(Scsi_Request * SRpnt, int at_head)
- {
- request_queue_t *q = &SRpnt->sr_device->request_queue;
- __scsi_insert_special(q, &SRpnt->sr_request, SRpnt, at_head);
- return 0;
- }
- /*
- * Function: scsi_init_cmd_errh()
- *
- * Purpose: Initialize SCpnt fields related to error handling.
- *
- * Arguments: SCpnt - command that is ready to be queued.
- *
- * Returns: Nothing
- *
- * Notes: This function has the job of initializing a number of
- * fields related to error handling. Typically this will
- * be called once for each command, as required.
- */
- int scsi_init_cmd_errh(Scsi_Cmnd * SCpnt)
- {
- ASSERT_LOCK(&io_request_lock, 0);
- SCpnt->owner = SCSI_OWNER_MIDLEVEL;
- SCpnt->reset_chain = NULL;
- SCpnt->serial_number = 0;
- SCpnt->serial_number_at_timeout = 0;
- SCpnt->flags = 0;
- SCpnt->retries = 0;
- SCpnt->abort_reason = 0;
- memset((void *) SCpnt->sense_buffer, 0, sizeof SCpnt->sense_buffer);
- if (SCpnt->cmd_len == 0)
- SCpnt->cmd_len = COMMAND_SIZE(SCpnt->cmnd[0]);
- /*
- * We need saved copies of a number of fields - this is because
- * error handling may need to overwrite these with different values
- * to run different commands, and once error handling is complete,
- * we will need to restore these values prior to running the actual
- * command.
- */
- SCpnt->old_use_sg = SCpnt->use_sg;
- SCpnt->old_cmd_len = SCpnt->cmd_len;
- SCpnt->sc_old_data_direction = SCpnt->sc_data_direction;
- SCpnt->old_underflow = SCpnt->underflow;
- memcpy((void *) SCpnt->data_cmnd,
- (const void *) SCpnt->cmnd, sizeof(SCpnt->cmnd));
- SCpnt->buffer = SCpnt->request_buffer;
- SCpnt->bufflen = SCpnt->request_bufflen;
- SCpnt->reset_chain = NULL;
- SCpnt->internal_timeout = NORMAL_TIMEOUT;
- SCpnt->abort_reason = 0;
- return 1;
- }
- /*
- * Function: scsi_queue_next_request()
- *
- * Purpose: Handle post-processing of completed commands.
- *
- * Arguments: SCpnt - command that may need to be requeued.
- *
- * Returns: Nothing
- *
- * Notes: After command completion, there may be blocks left
- * over which weren't finished by the previous command
- * this can be for a number of reasons - the main one is
- * that a medium error occurred, and the sectors after
- * the bad block need to be re-read.
- *
- * If SCpnt is NULL, it means that the previous command
- * was completely finished, and we should simply start
- * a new command, if possible.
- *
- * This is where a lot of special case code has begun to
- * accumulate. It doesn't really affect readability or
- * anything, but it might be considered architecturally
- * inelegant. If more of these special cases start to
- * accumulate, I am thinking along the lines of implementing
- * an atexit() like technology that gets run when commands
- * complete. I am not convinced that it is worth the
- * added overhead, however. Right now as things stand,
- * there are simple conditional checks, and most hosts
- * would skip past.
- *
- * Another possible solution would be to tailor different
- * handler functions, sort of like what we did in scsi_merge.c.
- * This is probably a better solution, but the number of different
- * permutations grows as 2**N, and if too many more special cases
- * get added, we start to get screwed.
- */
- void scsi_queue_next_request(request_queue_t * q, Scsi_Cmnd * SCpnt)
- {
- int all_clear;
- unsigned long flags;
- Scsi_Device *SDpnt;
- struct Scsi_Host *SHpnt;
- ASSERT_LOCK(&io_request_lock, 0);
- spin_lock_irqsave(&io_request_lock, flags);
- if (SCpnt != NULL) {
- /*
- * For some reason, we are not done with this request.
- * This happens for I/O errors in the middle of the request,
- * in which case we need to request the blocks that come after
- * the bad sector.
- */
- SCpnt->request.special = (void *) SCpnt;
- list_add(&SCpnt->request.queue, &q->queue_head);
- }
- /*
- * Just hit the requeue function for the queue.
- */
- q->request_fn(q);
- SDpnt = (Scsi_Device *) q->queuedata;
- SHpnt = SDpnt->host;
- /*
- * If this is a single-lun device, and we are currently finished
- * with this device, then see if we need to get another device
- * started. FIXME(eric) - if this function gets too cluttered
- * with special case code, then spin off separate versions and
- * use function pointers to pick the right one.
- */
- if (SDpnt->single_lun
- && list_empty(&q->queue_head)
- && SDpnt->device_busy == 0) {
- request_queue_t *q;
- for (SDpnt = SHpnt->host_queue;
- SDpnt;
- SDpnt = SDpnt->next) {
- if (((SHpnt->can_queue > 0)
- && (SHpnt->host_busy >= SHpnt->can_queue))
- || (SHpnt->host_blocked)
- || (SHpnt->host_self_blocked)
- || (SDpnt->device_blocked)) {
- break;
- }
- q = &SDpnt->request_queue;
- q->request_fn(q);
- }
- }
- /*
- * Now see whether there are other devices on the bus which
- * might be starved. If so, hit the request function. If we
- * don't find any, then it is safe to reset the flag. If we
- * find any device that it is starved, it isn't safe to reset the
- * flag as the queue function releases the lock and thus some
- * other device might have become starved along the way.
- */
- all_clear = 1;
- if (SHpnt->some_device_starved) {
- for (SDpnt = SHpnt->host_queue; SDpnt; SDpnt = SDpnt->next) {
- request_queue_t *q;
- if ((SHpnt->can_queue > 0 && (SHpnt->host_busy >= SHpnt->can_queue))
- || (SHpnt->host_blocked)
- || (SHpnt->host_self_blocked)) {
- break;
- }
- if (SDpnt->device_blocked || !SDpnt->starved) {
- continue;
- }
- q = &SDpnt->request_queue;
- q->request_fn(q);
- all_clear = 0;
- }
- if (SDpnt == NULL && all_clear) {
- SHpnt->some_device_starved = 0;
- }
- }
- spin_unlock_irqrestore(&io_request_lock, flags);
- }
- /*
- * Function: scsi_end_request()
- *
- * Purpose: Post-processing of completed commands called from interrupt
- * handler or a bottom-half handler.
- *
- * Arguments: SCpnt - command that is complete.
- * uptodate - 1 if I/O indicates success, 0 for I/O error.
- * sectors - number of sectors we want to mark.
- * requeue - indicates whether we should requeue leftovers.
- * frequeue - indicates that if we release the command block
- * that the queue request function should be called.
- *
- * Lock status: Assumed that lock is not held upon entry.
- *
- * Returns: Nothing
- *
- * Notes: This is called for block device requests in order to
- * mark some number of sectors as complete.
- *
- * We are guaranteeing that the request queue will be goosed
- * at some point during this call.
- */
- static Scsi_Cmnd *__scsi_end_request(Scsi_Cmnd * SCpnt,
- int uptodate,
- int sectors,
- int requeue,
- int frequeue)
- {
- struct request *req;
- struct buffer_head *bh;
- Scsi_Device * SDpnt;
- int nsect;
- ASSERT_LOCK(&io_request_lock, 0);
- req = &SCpnt->request;
- req->errors = 0;
- if (!uptodate) {
- printk(" I/O error: dev %s, sector %lun",
- kdevname(req->rq_dev), req->sector);
- }
- do {
- if ((bh = req->bh) != NULL) {
- nsect = bh->b_size >> 9;
- blk_finished_io(nsect);
- req->bh = bh->b_reqnext;
- bh->b_reqnext = NULL;
- sectors -= nsect;
- bh->b_end_io(bh, uptodate);
- if ((bh = req->bh) != NULL) {
- req->hard_sector += nsect;
- req->hard_nr_sectors -= nsect;
- req->sector += nsect;
- req->nr_sectors -= nsect;
- req->current_nr_sectors = bh->b_size >> 9;
- if (req->nr_sectors < req->current_nr_sectors) {
- req->nr_sectors = req->current_nr_sectors;
- printk("scsi_end_request: buffer-list destroyedn");
- }
- }
- }
- } while (sectors && bh);
- /*
- * If there are blocks left over at the end, set up the command
- * to queue the remainder of them.
- */
- if (req->bh) {
- request_queue_t *q;
- if( !requeue )
- {
- return SCpnt;
- }
- q = &SCpnt->device->request_queue;
- req->buffer = bh->b_data;
- /*
- * Bleah. Leftovers again. Stick the leftovers in
- * the front of the queue, and goose the queue again.
- */
- scsi_queue_next_request(q, SCpnt);
- return SCpnt;
- }
- /*
- * This request is done. If there is someone blocked waiting for this
- * request, wake them up. Typically used to wake up processes trying
- * to swap a page into memory.
- */
- if (req->waiting != NULL) {
- complete(req->waiting);
- }
- add_blkdev_randomness(MAJOR(req->rq_dev));
- SDpnt = SCpnt->device;
- /*
- * This will goose the queue request function at the end, so we don't
- * need to worry about launching another command.
- */
- __scsi_release_command(SCpnt);
- if( frequeue ) {
- request_queue_t *q;
- q = &SDpnt->request_queue;
- scsi_queue_next_request(q, NULL);
- }
- return NULL;
- }
- /*
- * Function: scsi_end_request()
- *
- * Purpose: Post-processing of completed commands called from interrupt
- * handler or a bottom-half handler.
- *
- * Arguments: SCpnt - command that is complete.
- * uptodate - 1 if I/O indicates success, 0 for I/O error.
- * sectors - number of sectors we want to mark.
- *
- * Lock status: Assumed that lock is not held upon entry.
- *
- * Returns: Nothing
- *
- * Notes: This is called for block device requests in order to
- * mark some number of sectors as complete.
- *
- * We are guaranteeing that the request queue will be goosed
- * at some point during this call.
- */
- Scsi_Cmnd *scsi_end_request(Scsi_Cmnd * SCpnt, int uptodate, int sectors)
- {
- return __scsi_end_request(SCpnt, uptodate, sectors, 1, 1);
- }
- /*
- * Function: scsi_release_buffers()
- *
- * Purpose: Completion processing for block device I/O requests.
- *
- * Arguments: SCpnt - command that we are bailing.
- *
- * Lock status: Assumed that no lock is held upon entry.
- *
- * Returns: Nothing
- *
- * Notes: In the event that an upper level driver rejects a
- * command, we must release resources allocated during
- * the __init_io() function. Primarily this would involve
- * the scatter-gather table, and potentially any bounce
- * buffers.
- */
- static void scsi_release_buffers(Scsi_Cmnd * SCpnt)
- {
- ASSERT_LOCK(&io_request_lock, 0);
- /*
- * Free up any indirection buffers we allocated for DMA purposes.
- */
- if (SCpnt->use_sg) {
- struct scatterlist *sgpnt;
- void **bbpnt;
- int i;
- sgpnt = (struct scatterlist *) SCpnt->request_buffer;
- bbpnt = SCpnt->bounce_buffers;
- if (bbpnt) {
- for (i = 0; i < SCpnt->use_sg; i++) {
- if (bbpnt[i])
- scsi_free(sgpnt[i].address, sgpnt[i].length);
- }
- }
- scsi_free(SCpnt->request_buffer, SCpnt->sglist_len);
- } else {
- if (SCpnt->request_buffer != SCpnt->request.buffer) {
- scsi_free(SCpnt->request_buffer, SCpnt->request_bufflen);
- }
- }
- /*
- * Zero these out. They now point to freed memory, and it is
- * dangerous to hang onto the pointers.
- */
- SCpnt->buffer = NULL;
- SCpnt->bufflen = 0;
- SCpnt->request_buffer = NULL;
- SCpnt->request_bufflen = 0;
- }
- /*
- * Function: scsi_io_completion()
- *
- * Purpose: Completion processing for block device I/O requests.
- *
- * Arguments: SCpnt - command that is finished.
- *
- * Lock status: Assumed that no lock is held upon entry.
- *
- * Returns: Nothing
- *
- * Notes: This function is matched in terms of capabilities to
- * the function that created the scatter-gather list.
- * In other words, if there are no bounce buffers
- * (the normal case for most drivers), we don't need
- * the logic to deal with cleaning up afterwards.
- */
- void scsi_io_completion(Scsi_Cmnd * SCpnt, int good_sectors,
- int block_sectors)
- {
- int result = SCpnt->result;
- int this_count = SCpnt->bufflen >> 9;
- request_queue_t *q = &SCpnt->device->request_queue;
- /*
- * We must do one of several things here:
- *
- * Call scsi_end_request. This will finish off the specified
- * number of sectors. If we are done, the command block will
- * be released, and the queue function will be goosed. If we
- * are not done, then scsi_end_request will directly goose
- * the queue.
- *
- * We can just use scsi_queue_next_request() here. This
- * would be used if we just wanted to retry, for example.
- *
- */
- ASSERT_LOCK(&io_request_lock, 0);
- /*
- * Free up any indirection buffers we allocated for DMA purposes.
- * For the case of a READ, we need to copy the data out of the
- * bounce buffer and into the real buffer.
- */
- if (SCpnt->use_sg) {
- struct scatterlist *sgpnt;
- void **bbpnt;
- int i;
- sgpnt = (struct scatterlist *) SCpnt->buffer;
- bbpnt = SCpnt->bounce_buffers;
- if (bbpnt) {
- for (i = 0; i < SCpnt->use_sg; i++) {
- if (bbpnt[i]) {
- if (SCpnt->request.cmd == READ) {
- memcpy(bbpnt[i],
- sgpnt[i].address,
- sgpnt[i].length);
- }
- scsi_free(sgpnt[i].address, sgpnt[i].length);
- }
- }
- }
- scsi_free(SCpnt->buffer, SCpnt->sglist_len);
- } else {
- if (SCpnt->buffer != SCpnt->request.buffer) {
- if (SCpnt->request.cmd == READ) {
- memcpy(SCpnt->request.buffer, SCpnt->buffer,
- SCpnt->bufflen);
- }
- scsi_free(SCpnt->buffer, SCpnt->bufflen);
- }
- }
- /*
- * Zero these out. They now point to freed memory, and it is
- * dangerous to hang onto the pointers.
- */
- SCpnt->buffer = NULL;
- SCpnt->bufflen = 0;
- SCpnt->request_buffer = NULL;
- SCpnt->request_bufflen = 0;
- /*
- * Next deal with any sectors which we were able to correctly
- * handle.
- */
- if (good_sectors > 0) {
- SCSI_LOG_HLCOMPLETE(1, printk("%ld sectors total, %d sectors done.n",
- SCpnt->request.nr_sectors,
- good_sectors));
- SCSI_LOG_HLCOMPLETE(1, printk("use_sg is %dn ", SCpnt->use_sg));
- SCpnt->request.errors = 0;
- /*
- * If multiple sectors are requested in one buffer, then
- * they will have been finished off by the first command.
- * If not, then we have a multi-buffer command.
- *
- * If block_sectors != 0, it means we had a medium error
- * of some sort, and that we want to mark some number of
- * sectors as not uptodate. Thus we want to inhibit
- * requeueing right here - we will requeue down below
- * when we handle the bad sectors.
- */
- SCpnt = __scsi_end_request(SCpnt,
- 1,
- good_sectors,
- result == 0,
- 1);
- /*
- * If the command completed without error, then either finish off the
- * rest of the command, or start a new one.
- */
- if (result == 0 || SCpnt == NULL ) {
- return;
- }
- }
- /*
- * Now, if we were good little boys and girls, Santa left us a request
- * sense buffer. We can extract information from this, so we
- * can choose a block to remap, etc.
- */
- if (driver_byte(result) != 0) {
- if (suggestion(result) == SUGGEST_REMAP) {
- #ifdef REMAP
- /*
- * Not yet implemented. A read will fail after being remapped,
- * a write will call the strategy routine again.
- */
- if (SCpnt->device->remap) {
- result = 0;
- }
- #endif
- }
- if ((SCpnt->sense_buffer[0] & 0x7f) == 0x70) {
- /*
- * If the device is in the process of becoming ready,
- * retry.
- */
- if (SCpnt->sense_buffer[12] == 0x04 &&
- SCpnt->sense_buffer[13] == 0x01) {
- scsi_queue_next_request(q, SCpnt);
- return;
- }
- if ((SCpnt->sense_buffer[2] & 0xf) == UNIT_ATTENTION) {
- if (SCpnt->device->removable) {
- /* detected disc change. set a bit
- * and quietly refuse further access.
- */
- SCpnt->device->changed = 1;
- SCpnt = scsi_end_request(SCpnt, 0, this_count);
- return;
- } else {
- /*
- * Must have been a power glitch, or a
- * bus reset. Could not have been a
- * media change, so we just retry the
- * request and see what happens.
- */
- scsi_queue_next_request(q, SCpnt);
- return;
- }
- }
- }
- /* If we had an ILLEGAL REQUEST returned, then we may have
- * performed an unsupported command. The only thing this should be
- * would be a ten byte read where only a six byte read was supported.
- * Also, on a system where READ CAPACITY failed, we have have read
- * past the end of the disk.
- */
- switch (SCpnt->sense_buffer[2]) {
- case ILLEGAL_REQUEST:
- if (SCpnt->device->ten) {
- SCpnt->device->ten = 0;
- /*
- * This will cause a retry with a 6-byte
- * command.
- */
- scsi_queue_next_request(q, SCpnt);
- result = 0;
- } else {
- SCpnt = scsi_end_request(SCpnt, 0, this_count);
- return;
- }
- break;
- case NOT_READY:
- printk(KERN_INFO "Device %s not ready.n",
- kdevname(SCpnt->request.rq_dev));
- SCpnt = scsi_end_request(SCpnt, 0, this_count);
- return;
- break;
- case MEDIUM_ERROR:
- case VOLUME_OVERFLOW:
- printk("scsi%d: ERROR on channel %d, id %d, lun %d, CDB: ",
- SCpnt->host->host_no, (int) SCpnt->channel,
- (int) SCpnt->target, (int) SCpnt->lun);
- print_command(SCpnt->cmnd);
- print_sense("sd", SCpnt);
- SCpnt = scsi_end_request(SCpnt, 0, block_sectors);
- return;
- default:
- break;
- }
- } /* driver byte != 0 */
- if (host_byte(result) == DID_RESET) {
- /*
- * Third party bus reset or reset for error
- * recovery reasons. Just retry the request
- * and see what happens.
- */
- scsi_queue_next_request(q, SCpnt);
- return;
- }
- if (result) {
- struct Scsi_Device_Template *STpnt;
- STpnt = scsi_get_request_dev(&SCpnt->request);
- printk("SCSI %s error : host %d channel %d id %d lun %d return code = %xn",
- (STpnt ? STpnt->name : "device"),
- SCpnt->device->host->host_no,
- SCpnt->device->channel,
- SCpnt->device->id,
- SCpnt->device->lun, result);
- if (driver_byte(result) & DRIVER_SENSE)
- print_sense("sd", SCpnt);
- /*
- * Mark a single buffer as not uptodate. Queue the remainder.
- * We sometimes get this cruft in the event that a medium error
- * isn't properly reported.
- */
- SCpnt = scsi_end_request(SCpnt, 0, SCpnt->request.current_nr_sectors);
- return;
- }
- }
- /*
- * Function: scsi_get_request_dev()
- *
- * Purpose: Find the upper-level driver that is responsible for this
- * request
- *
- * Arguments: request - I/O request we are preparing to queue.
- *
- * Lock status: No locks assumed to be held, but as it happens the
- * io_request_lock is held when this is called.
- *
- * Returns: Nothing
- *
- * Notes: The requests in the request queue may have originated
- * from any block device driver. We need to find out which
- * one so that we can later form the appropriate command.
- */
- struct Scsi_Device_Template *scsi_get_request_dev(struct request *req)
- {
- struct Scsi_Device_Template *spnt;
- kdev_t dev = req->rq_dev;
- int major = MAJOR(dev);
- ASSERT_LOCK(&io_request_lock, 1);
- for (spnt = scsi_devicelist; spnt; spnt = spnt->next) {
- /*
- * Search for a block device driver that supports this
- * major.
- */
- if (spnt->blk && spnt->major == major) {
- return spnt;
- }
- /*
- * I am still not entirely satisfied with this solution,
- * but it is good enough for now. Disks have a number of
- * major numbers associated with them, the primary
- * 8, which we test above, and a secondary range of 7
- * different consecutive major numbers. If this ever
- * becomes insufficient, then we could add another function
- * to the structure, and generalize this completely.
- */
- if( spnt->min_major != 0
- && spnt->max_major != 0
- && major >= spnt->min_major
- && major <= spnt->max_major )
- {
- return spnt;
- }
- }
- return NULL;
- }
- /*
- * Function: scsi_request_fn()
- *
- * Purpose: Generic version of request function for SCSI hosts.
- *
- * Arguments: q - Pointer to actual queue.
- *
- * Returns: Nothing
- *
- * Lock status: IO request lock assumed to be held when called.
- *
- * Notes: The theory is that this function is something which individual
- * drivers could also supply if they wished to. The problem
- * is that we have 30 some odd low-level drivers in the kernel
- * tree already, and it would be most difficult to retrofit
- * this crap into all of them. Thus this function has the job
- * of acting as a generic queue manager for all of those existing
- * drivers.
- */
- void scsi_request_fn(request_queue_t * q)
- {
- struct request *req;
- Scsi_Cmnd *SCpnt;
- Scsi_Request *SRpnt;
- Scsi_Device *SDpnt;
- struct Scsi_Host *SHpnt;
- struct Scsi_Device_Template *STpnt;
- ASSERT_LOCK(&io_request_lock, 1);
- SDpnt = (Scsi_Device *) q->queuedata;
- if (!SDpnt) {
- panic("Missing device");
- }
- SHpnt = SDpnt->host;
- /*
- * To start with, we keep looping until the queue is empty, or until
- * the host is no longer able to accept any more requests.
- */
- while (1 == 1) {
- /*
- * Check this again - each time we loop through we will have
- * released the lock and grabbed it again, so each time
- * we need to check to see if the queue is plugged or not.
- */
- if (SHpnt->in_recovery || q->plugged)
- return;
- /*
- * If the device cannot accept another request, then quit.
- */
- if (SDpnt->device_blocked) {
- break;
- }
- if ((SHpnt->can_queue > 0 && (SHpnt->host_busy >= SHpnt->can_queue))
- || (SHpnt->host_blocked)
- || (SHpnt->host_self_blocked)) {
- /*
- * If we are unable to process any commands at all for
- * this device, then we consider it to be starved.
- * What this means is that there are no outstanding
- * commands for this device and hence we need a
- * little help getting it started again
- * once the host isn't quite so busy.
- */
- if (SDpnt->device_busy == 0) {
- SDpnt->starved = 1;
- SHpnt->some_device_starved = 1;
- }
- break;
- } else {
- SDpnt->starved = 0;
- }
- /*
- * FIXME(eric)
- * I am not sure where the best place to do this is. We need
- * to hook in a place where we are likely to come if in user
- * space. Technically the error handling thread should be
- * doing this crap, but the error handler isn't used by
- * most hosts.
- */
- if (SDpnt->was_reset) {
- /*
- * We need to relock the door, but we might
- * be in an interrupt handler. Only do this
- * from user space, since we do not want to
- * sleep from an interrupt.
- *
- * FIXME(eric) - have the error handler thread do
- * this work.
- */
- SDpnt->was_reset = 0;
- if (SDpnt->removable && !in_interrupt()) {
- spin_unlock_irq(&io_request_lock);
- scsi_ioctl(SDpnt, SCSI_IOCTL_DOORLOCK, 0);
- spin_lock_irq(&io_request_lock);
- continue;
- }
- }
- /*
- * If we couldn't find a request that could be queued, then we
- * can also quit.
- */
- if (list_empty(&q->queue_head))
- break;
- /*
- * Loop through all of the requests in this queue, and find
- * one that is queueable.
- */
- req = blkdev_entry_next_request(&q->queue_head);
- /*
- * Find the actual device driver associated with this command.
- * The SPECIAL requests are things like character device or
- * ioctls, which did not originate from ll_rw_blk. Note that
- * the special field is also used to indicate the SCpnt for
- * the remainder of a partially fulfilled request that can
- * come up when there is a medium error. We have to treat
- * these two cases differently. We differentiate by looking
- * at request.cmd, as this tells us the real story.
- */
- if (req->cmd == SPECIAL) {
- STpnt = NULL;
- SCpnt = (Scsi_Cmnd *) req->special;
- SRpnt = (Scsi_Request *) req->special;
- if( SRpnt->sr_magic == SCSI_REQ_MAGIC ) {
- SCpnt = scsi_allocate_device(SRpnt->sr_device,
- FALSE, FALSE);
- if( !SCpnt ) {
- break;
- }
- scsi_init_cmd_from_req(SCpnt, SRpnt);
- }
- } else {
- SRpnt = NULL;
- STpnt = scsi_get_request_dev(req);
- if (!STpnt) {
- panic("Unable to find device associated with request");
- }
- /*
- * Now try and find a command block that we can use.
- */
- if( req->special != NULL ) {
- SCpnt = (Scsi_Cmnd *) req->special;
- /*
- * We need to recount the number of
- * scatter-gather segments here - the
- * normal case code assumes this to be
- * correct, as it would be a performance
- * lose to always recount. Handling
- * errors is always unusual, of course.
- */
- recount_segments(SCpnt);
- } else {
- SCpnt = scsi_allocate_device(SDpnt, FALSE, FALSE);
- }
- /*
- * If so, we are ready to do something. Bump the count
- * while the queue is locked and then break out of the
- * loop. Otherwise loop around and try another request.
- */
- if (!SCpnt) {
- break;
- }
- }
- /*
- * Now bump the usage count for both the host and the
- * device.
- */
- SHpnt->host_busy++;
- SDpnt->device_busy++;
- /*
- * Finally, before we release the lock, we copy the
- * request to the command block, and remove the
- * request from the request list. Note that we always
- * operate on the queue head - there is absolutely no
- * reason to search the list, because all of the commands
- * in this queue are for the same device.
- */
- blkdev_dequeue_request(req);
- if (req != &SCpnt->request && req != &SRpnt->sr_request ) {
- memcpy(&SCpnt->request, req, sizeof(struct request));
- /*
- * We have copied the data out of the request block -
- * it is now in a field in SCpnt. Release the request
- * block.
- */
- blkdev_release_request(req);
- }
- /*
- * Now it is finally safe to release the lock. We are
- * not going to noodle the request list until this
- * request has been queued and we loop back to queue
- * another.
- */
- req = NULL;
- spin_unlock_irq(&io_request_lock);
- if (SCpnt->request.cmd != SPECIAL) {
- /*
- * This will do a couple of things:
- * 1) Fill in the actual SCSI command.
- * 2) Fill in any other upper-level specific fields
- * (timeout).
- *
- * If this returns 0, it means that the request failed
- * (reading past end of disk, reading offline device,
- * etc). This won't actually talk to the device, but
- * some kinds of consistency checking may cause the
- * request to be rejected immediately.
- */
- if (STpnt == NULL) {
- STpnt = scsi_get_request_dev(req);
- }
- /*
- * This sets up the scatter-gather table (allocating if
- * required). Hosts that need bounce buffers will also
- * get those allocated here.
- */
- if (!SDpnt->scsi_init_io_fn(SCpnt)) {
- SCpnt = __scsi_end_request(SCpnt, 0,
- SCpnt->request.nr_sectors, 0, 0);
- if( SCpnt != NULL )
- {
- panic("Should not have leftover blocksn");
- }
- spin_lock_irq(&io_request_lock);
- SHpnt->host_busy--;
- SDpnt->device_busy--;
- continue;
- }
- /*
- * Initialize the actual SCSI command for this request.
- */
- if (!STpnt->init_command(SCpnt)) {
- scsi_release_buffers(SCpnt);
- SCpnt = __scsi_end_request(SCpnt, 0,
- SCpnt->request.nr_sectors, 0, 0);
- if( SCpnt != NULL )
- {
- panic("Should not have leftover blocksn");
- }
- spin_lock_irq(&io_request_lock);
- SHpnt->host_busy--;
- SDpnt->device_busy--;
- continue;
- }
- }
- /*
- * Finally, initialize any error handling parameters, and set up
- * the timers for timeouts.
- */
- scsi_init_cmd_errh(SCpnt);
- /*
- * Dispatch the command to the low-level driver.
- */
- scsi_dispatch_cmd(SCpnt);
- /*
- * Now we need to grab the lock again. We are about to mess
- * with the request queue and try to find another command.
- */
- spin_lock_irq(&io_request_lock);
- }
- }
- /*
- * Function: scsi_block_requests()
- *
- * Purpose: Utility function used by low-level drivers to prevent further
- * commands from being queued to the device.
- *
- * Arguments: SHpnt - Host in question
- *
- * Returns: Nothing
- *
- * Lock status: No locks are assumed held.
- *
- * Notes: There is no timer nor any other means by which the requests
- * get unblocked other than the low-level driver calling
- * scsi_unblock_requests().
- */
- void scsi_block_requests(struct Scsi_Host * SHpnt)
- {
- SHpnt->host_self_blocked = TRUE;
- }
- /*
- * Function: scsi_unblock_requests()
- *
- * Purpose: Utility function used by low-level drivers to allow further
- * commands from being queued to the device.
- *
- * Arguments: SHpnt - Host in question
- *
- * Returns: Nothing
- *
- * Lock status: No locks are assumed held.
- *
- * Notes: There is no timer nor any other means by which the requests
- * get unblocked other than the low-level driver calling
- * scsi_unblock_requests().
- *
- * This is done as an API function so that changes to the
- * internals of the scsi mid-layer won't require wholesale
- * changes to drivers that use this feature.
- */
- void scsi_unblock_requests(struct Scsi_Host * SHpnt)
- {
- Scsi_Device *SDloop;
- SHpnt->host_self_blocked = FALSE;
- /* Now that we are unblocked, try to start the queues. */
- for (SDloop = SHpnt->host_queue; SDloop; SDloop = SDloop->next)
- scsi_queue_next_request(&SDloop->request_queue, NULL);
- }
- /*
- * Function: scsi_report_bus_reset()
- *
- * Purpose: Utility function used by low-level drivers to report that
- * they have observed a bus reset on the bus being handled.
- *
- * Arguments: SHpnt - Host in question
- * channel - channel on which reset was observed.
- *
- * Returns: Nothing
- *
- * Lock status: No locks are assumed held.
- *
- * Notes: This only needs to be called if the reset is one which
- * originates from an unknown location. Resets originated
- * by the mid-level itself don't need to call this, but there
- * should be no harm.
- *
- * The main purpose of this is to make sure that a CHECK_CONDITION
- * is properly treated.
- */
- void scsi_report_bus_reset(struct Scsi_Host * SHpnt, int channel)
- {
- Scsi_Device *SDloop;
- for (SDloop = SHpnt->host_queue; SDloop; SDloop = SDloop->next) {
- if (channel == SDloop->channel) {
- SDloop->was_reset = 1;
- SDloop->expecting_cc_ua = 1;
- }
- }
- }
- /*
- * FIXME(eric) - these are empty stubs for the moment. I need to re-implement
- * host blocking from scratch. The theory is that hosts that wish to block
- * will register/deregister using these functions instead of the old way
- * of setting the wish_block flag.
- *
- * The details of the implementation remain to be settled, however the
- * stubs are here now so that the actual drivers will properly compile.
- */
- void scsi_register_blocked_host(struct Scsi_Host * SHpnt)
- {
- }
- void scsi_deregister_blocked_host(struct Scsi_Host * SHpnt)
- {
- }