!C99Shell v. 1.0 pre-release build #13!

Software: Apache/2.0.54 (Unix) mod_perl/1.99_09 Perl/v5.8.0 mod_ssl/2.0.54 OpenSSL/0.9.7l DAV/2 FrontPage/5.0.2.2635 PHP/4.4.0 mod_gzip/2.0.26.1a 

uname -a: Linux snow.he.net 4.4.276-v2-mono-1 #1 SMP Wed Jul 21 11:21:17 PDT 2021 i686 

uid=99(nobody) gid=98(nobody) groups=98(nobody) 

Safe-mode: OFF (not secure)

/usr/src/linux-2.4.18-xfs-1.1/drivers/scsi/   drwxr-xr-x
Free 318.35 GB of 458.09 GB (69.5%)
Home    Back    Forward    UPDIR    Refresh    Search    Buffer    Encoder    Tools    Proc.    FTP brute    Sec.    SQL    PHP-code    Update    Feedback    Self remove    Logout    


Viewing file:     scsi_lib.c (33.95 KB)      -rw-r--r--
Select action/file-type:
(+) | (+) | (+) | Code (+) | Session (+) | (+) | SDB (+) | (+) | (+) | (+) | (+) | (+) |
/*
 *  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 %lu\n",
               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 destroyed\n");
                }
            }
        }
    } 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 %d\n ", 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 = %x\n",
               (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 blocks\n");
                }
                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 blocks\n");
                }
                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)
{
}

:: Command execute ::

Enter:
 
Select:
 

:: Search ::
  - regexp 

:: Upload ::
 
[ Read-Only ]

:: Make Dir ::
 
[ Read-Only ]
:: Make File ::
 
[ Read-Only ]

:: Go Dir ::
 
:: Go File ::
 

--[ c99shell v. 1.0 pre-release build #13 powered by Captain Crunch Security Team | http://ccteam.ru | Generation time: 0.0339 ]--