📄 scsi.c
字号:
sizeof(SRpnt->sr_cmnd)); SRpnt->sr_bufflen = bufflen; SRpnt->sr_buffer = buffer; SRpnt->sr_allowed = retries; SRpnt->sr_done = done; SRpnt->sr_timeout_per_command = timeout; if (SRpnt->sr_cmd_len == 0) SRpnt->sr_cmd_len = COMMAND_SIZE(SRpnt->sr_cmnd[0]); /* * At this point, we merely set up the command, stick it in the normal * request queue, and return. Eventually that request will come to the * top of the list, and will be dispatched. */ scsi_insert_special_req(SRpnt, 0); SCSI_LOG_MLQUEUE(3, printk("Leaving scsi_do_req()\n"));} /* * Function: scsi_init_cmd_from_req * * Purpose: Queue a SCSI command * Purpose: Initialize a Scsi_Cmnd from a Scsi_Request * * Arguments: SCpnt - command descriptor. * SRpnt - Request from the queue. * * Lock status: None needed. * * Returns: Nothing. * * Notes: Mainly transfer data from the request structure to the * command structure. The request structure is allocated * using the normal memory allocator, and requests can pile * up to more or less any depth. The command structure represents * a consumable resource, as these are allocated into a pool * when the SCSI subsystem initializes. The preallocation is * required so that in low-memory situations a disk I/O request * won't cause the memory manager to try and write out a page. * The request structure is generally used by ioctls and character * devices. */void scsi_init_cmd_from_req(Scsi_Cmnd * SCpnt, Scsi_Request * SRpnt){ struct Scsi_Host *host = SCpnt->host; ASSERT_LOCK(&io_request_lock, 0); SCpnt->owner = SCSI_OWNER_MIDLEVEL; SRpnt->sr_command = SCpnt; if (!host) { panic("Invalid or not present host.\n"); } SCpnt->cmd_len = SRpnt->sr_cmd_len; SCpnt->use_sg = SRpnt->sr_use_sg; memcpy((void *) &SCpnt->request, (const void *) &SRpnt->sr_request, sizeof(SRpnt->sr_request)); memcpy((void *) SCpnt->data_cmnd, (const void *) SRpnt->sr_cmnd, sizeof(SCpnt->data_cmnd)); SCpnt->reset_chain = NULL; SCpnt->serial_number = 0; SCpnt->serial_number_at_timeout = 0; SCpnt->bufflen = SRpnt->sr_bufflen; SCpnt->buffer = SRpnt->sr_buffer; SCpnt->flags = 0; SCpnt->retries = 0; SCpnt->allowed = SRpnt->sr_allowed; SCpnt->done = SRpnt->sr_done; SCpnt->timeout_per_command = SRpnt->sr_timeout_per_command; SCpnt->sc_data_direction = SRpnt->sr_data_direction; SCpnt->sglist_len = SRpnt->sr_sglist_len; SCpnt->underflow = SRpnt->sr_underflow; SCpnt->sc_request = SRpnt; memcpy((void *) SCpnt->cmnd, (const void *) SRpnt->sr_cmnd, sizeof(SCpnt->cmnd)); /* Zero the sense buffer. Some host adapters automatically request * sense on error. 0 is not a valid sense code. */ memset((void *) SCpnt->sense_buffer, 0, sizeof SCpnt->sense_buffer); SCpnt->request_buffer = SRpnt->sr_buffer; SCpnt->request_bufflen = SRpnt->sr_bufflen; SCpnt->old_use_sg = SCpnt->use_sg; if (SCpnt->cmd_len == 0) SCpnt->cmd_len = COMMAND_SIZE(SCpnt->cmnd[0]); SCpnt->old_cmd_len = SCpnt->cmd_len; SCpnt->sc_old_data_direction = SCpnt->sc_data_direction; SCpnt->old_underflow = SCpnt->underflow; /* Start the timer ticking. */ SCpnt->internal_timeout = NORMAL_TIMEOUT; SCpnt->abort_reason = 0; SCpnt->result = 0; SCSI_LOG_MLQUEUE(3, printk("Leaving scsi_init_cmd_from_req()\n"));}/* * Function: scsi_do_cmd * * Purpose: Queue a SCSI command * * Arguments: SCpnt - command descriptor. * cmnd - actual SCSI command to be performed. * buffer - data buffer. * bufflen - size of data buffer. * done - completion function to be run. * timeout - how long to let it run before timeout. * retries - number of retries we allow. * * Lock status: With the new queueing code, this is SMP-safe, and no locks * need be held upon entry. The old queueing code the lock was * assumed to be held upon entry. * * Returns: Nothing. * * Notes: Prior to the new queue code, this function was not SMP-safe. * Also, this function is now only used for queueing requests * for things like ioctls and character device requests - this * is because we essentially just inject a request into the * queue for the device. Normal block device handling manipulates * the queue directly. */void scsi_do_cmd(Scsi_Cmnd * SCpnt, const void *cmnd, void *buffer, unsigned bufflen, void (*done) (Scsi_Cmnd *), int timeout, int retries){ struct Scsi_Host *host = SCpnt->host; ASSERT_LOCK(&io_request_lock, 0); SCpnt->pid = scsi_pid++; SCpnt->owner = SCSI_OWNER_MIDLEVEL; SCSI_LOG_MLQUEUE(4, { int i; int target = SCpnt->target; int size = COMMAND_SIZE(((const unsigned char *)cmnd)[0]); printk("scsi_do_cmd (host = %d, channel = %d target = %d, " "buffer =%p, bufflen = %d, done = %p, timeout = %d, " "retries = %d)\n" "command : ", host->host_no, SCpnt->channel, target, buffer, bufflen, done, timeout, retries); for (i = 0; i < size; ++i) printk("%02x ", ((unsigned char *) cmnd)[i]); printk("\n"); }); if (!host) { panic("Invalid or not present host.\n"); } /* * We must prevent reentrancy to the lowlevel host driver. This prevents * it - we enter a loop until the host we want to talk to is not busy. * Race conditions are prevented, as interrupts are disabled in between the * time we check for the host being not busy, and the time we mark it busy * ourselves. */ /* * Our own function scsi_done (which marks the host as not busy, disables * the timeout counter, etc) will be called by us or by the * scsi_hosts[host].queuecommand() function needs to also call * the completion function for the high level driver. */ memcpy((void *) SCpnt->data_cmnd, (const void *) cmnd, sizeof(SCpnt->data_cmnd)); SCpnt->reset_chain = NULL; SCpnt->serial_number = 0; SCpnt->serial_number_at_timeout = 0; SCpnt->bufflen = bufflen; SCpnt->buffer = buffer; SCpnt->flags = 0; SCpnt->retries = 0; SCpnt->allowed = retries; SCpnt->done = done; SCpnt->timeout_per_command = timeout; memcpy((void *) SCpnt->cmnd, (const void *) cmnd, sizeof(SCpnt->cmnd)); /* Zero the sense buffer. Some host adapters automatically request * sense on error. 0 is not a valid sense code. */ memset((void *) SCpnt->sense_buffer, 0, sizeof SCpnt->sense_buffer); SCpnt->request_buffer = buffer; SCpnt->request_bufflen = bufflen; SCpnt->old_use_sg = SCpnt->use_sg; if (SCpnt->cmd_len == 0) SCpnt->cmd_len = COMMAND_SIZE(SCpnt->cmnd[0]); SCpnt->old_cmd_len = SCpnt->cmd_len; SCpnt->sc_old_data_direction = SCpnt->sc_data_direction; SCpnt->old_underflow = SCpnt->underflow; /* Start the timer ticking. */ SCpnt->internal_timeout = NORMAL_TIMEOUT; SCpnt->abort_reason = 0; SCpnt->result = 0; /* * At this point, we merely set up the command, stick it in the normal * request queue, and return. Eventually that request will come to the * top of the list, and will be dispatched. */ scsi_insert_special_cmd(SCpnt, 0); SCSI_LOG_MLQUEUE(3, printk("Leaving scsi_do_cmd()\n"));}/* * This function is the mid-level interrupt routine, which decides how * to handle error conditions. Each invocation of this function must * do one and *only* one of the following: * * 1) Insert command in BH queue. * 2) Activate error handler for host. * * FIXME(eric) - I am concerned about stack overflow (still). An * interrupt could come while we are processing the bottom queue, * which would cause another command to be stuffed onto the bottom * queue, and it would in turn be processed as that interrupt handler * is returning. Given a sufficiently steady rate of returning * commands, this could cause the stack to overflow. I am not sure * what is the most appropriate solution here - we should probably * keep a depth count, and not process any commands while we still * have a bottom handler active higher in the stack. * * There is currently code in the bottom half handler to monitor * recursion in the bottom handler and report if it ever happens. If * this becomes a problem, it won't be hard to engineer something to * deal with it so that only the outer layer ever does any real * processing. */void scsi_done(Scsi_Cmnd * SCpnt){ unsigned long flags; int tstatus; /* * We don't have to worry about this one timing out any more. */ tstatus = scsi_delete_timer(SCpnt); /* * If we are unable to remove the timer, it means that the command * has already timed out. In this case, we have no choice but to * let the timeout function run, as we have no idea where in fact * that function could really be. It might be on another processor, * etc, etc. */ if (!tstatus) { SCpnt->done_late = 1; return; } /* Set the serial numbers back to zero */ SCpnt->serial_number = 0; /* * First, see whether this command already timed out. If so, we ignore * the response. We treat it as if the command never finished. * * Since serial_number is now 0, the error handler cound detect this * situation and avoid to call the low level driver abort routine. * (DB) * * FIXME(eric) - I believe that this test is now redundant, due to * the test of the return status of del_timer(). */ if (SCpnt->state == SCSI_STATE_TIMEOUT) { SCSI_LOG_MLCOMPLETE(1, printk("Ignoring completion of %p due to timeout status", SCpnt)); return; } spin_lock_irqsave(&scsi_bhqueue_lock, flags); SCpnt->serial_number_at_timeout = 0; SCpnt->state = SCSI_STATE_BHQUEUE; SCpnt->owner = SCSI_OWNER_BH_HANDLER; SCpnt->bh_next = NULL; /* * Next, put this command in the BH queue. * * We need a spinlock here, or compare and exchange if we can reorder incoming * Scsi_Cmnds, as it happens pretty often scsi_done is called multiple times * before bh is serviced. -jj * * We already have the io_request_lock here, since we are called from the * interrupt handler or the error handler. (DB) * * This may be true at the moment, but I would like to wean all of the low * level drivers away from using io_request_lock. Technically they should * all use their own locking. I am adding a small spinlock to protect * this datastructure to make it safe for that day. (ERY) */ if (!scsi_bh_queue_head) { scsi_bh_queue_head = SCpnt; scsi_bh_queue_tail = SCpnt; } else { scsi_bh_queue_tail->bh_next = SCpnt; scsi_bh_queue_tail = SCpnt; } spin_unlock_irqrestore(&scsi_bhqueue_lock, flags); /* * Mark the bottom half handler to be run. */ mark_bh(SCSI_BH);}/* * Procedure: scsi_bottom_half_handler * * Purpose: Called after we have finished processing interrupts, it * performs post-interrupt handling for commands that may * have completed. * * Notes: This is called with all interrupts enabled. This should reduce * interrupt latency, stack depth, and reentrancy of the low-level * drivers. * * The io_request_lock is required in all the routine. There was a subtle * race condition when scsi_done is called after a command has already * timed out but before the time out is processed by the error handler. * (DB) * * I believe I have corrected this. We simply monitor the return status of * del_timer() - if this comes back as 0, it means that the timer has fired * and that a timeout is in progress. I have modified scsi_done() such * that in this instance the command is never inserted in the bottom * half queue. Thus the only time we hold the lock here is when * we wish to atomically remove the contents of the queue. */void scsi_bottom_half_handler(void){ Scsi_Cmnd *SCpnt; Scsi_Cmnd *SCnext; unsigned long flags; while (1 == 1) { spin_lock_irqsave(&scsi_bhqueue_lock, flags); SCpnt = scsi_bh_queue_head; scsi_bh_queue_head = NULL; spin_unlock_irqrestore(&scsi_bhqueue_lock, flags); if (SCpnt == NULL) { return; } SCnext = SCpnt->bh_next; for (; SCpnt; SCpnt = SCnext) { SCnext = SCpnt->bh_next; switch (scsi_decide_disposition(SCpnt)) { case SUCCESS: /* * Add to BH queue. */ SCSI_LOG_MLCOMPLETE(3, printk("Command finished %d %d 0x%x\n", SCpnt->host->host_busy, SCpnt->host->host_failed, SCpnt->result)); scsi_finish_command(SCpnt); break; case NEEDS_RETRY: /* * We only come in here if we want to retry a command. The * test to see whether the command should be retried should be * keeping track of the number of tries, so we don't end up looping, * of course. */ SCSI_LOG_MLCOMPLETE(3, printk("Command needs retry %d %d 0x%x\n", SCpnt->host->host_busy, SCpnt->host->host_failed, SCpnt->result)); scsi_retry_command(SCpnt); break; case ADD_TO_MLQUEUE: /* * This typically happens for a QUEUE_FULL message - * typically only when the queue depth is only * approximate for a given device. Adding a command * to the queue for the device will prevent further commands * from being sent to the device, so we shouldn't end up * with tons of things being sent down that shouldn't be. */ SCSI_LOG_MLCOMPLETE(3, printk("Command rejected as device queue full, put on ml queue %p\n", SCpnt)); scsi_mlqueue_insert(SCpnt, SCSI_MLQUEUE_DEVICE_BUSY); break; default: /* * Here we have a fatal error of some sort. Turn it over to * the error handler. */ SCSI_LOG_MLCOMPLETE(3, printk("Command failed %p %x active=%d busy=%d failed=%d\n", SCpnt, SCpnt->result, atomic_read(&SCpnt->host->host_active), SCpnt->host->host_busy, SCpnt->host->host_failed)); /* * Dump the sense information too. */ if ((status_byte(SCpnt->result) & CHECK_CONDITION) != 0) { SCSI_LOG_MLCOMPLETE(3, print_sense("bh", SCpnt)); } if (SCpnt->host->eh_wait != NULL) { SCpnt->host->host_failed++; SCpnt->owner = SCSI_OWNER_ERROR_HANDLER; SCpnt->state = SCSI_STATE_FAILED; SCpnt->host->in_recovery = 1; /* * If the host is having troubles, then look to see if this was the last * command that might have failed. If so, wake up the error handler. */ if (SCpnt->host->host_busy == SCpnt->host->host_failed) { SCSI_LOG_ERROR_RECOVERY(5, printk("Waking error handler thread (%d)\n", atomic_read(&SCpnt->host->eh_wait->count))); up(SCpnt->host->eh_wait); } } else { /* * We only get here if the error recovery thread has died.
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -