⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 scsi.c

📁 讲述linux的初始化过程
💻 C
📖 第 1 页 / 共 5 页
字号:
 *              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_do_cmd()\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->owner = SCSI_OWNER_MIDLEVEL;	SCSI_LOG_MLQUEUE(4,			 {			 int i;			 int target = SCpnt->target;			 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 < 10; ++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 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.					 */					scsi_finish_command(SCpnt);				}			}		}		/* for(; SCpnt...) */	}			/* while(1==1) */}/* * Function:    scsi_retry_command * * Purpose:     Send a command back to the low level to be retried. * * Notes:       This command is always executed in the context of the *              bottom half handler, or the error handler thread. Low *              level drivers should not become re-entrant as a result of *              this. */int scsi_retry_command(Scsi_Cmnd * SCpnt){	memcpy((void *) SCpnt->cmnd, (void *) SCpnt->data_cmnd,	       sizeof(SCpnt->data_cmnd));	SCpnt->request_buffer = SCpnt->buffer;	SCpnt->request_bufflen = SCpnt->bufflen;	SCpnt->use_sg = SCpnt->old_use_sg;	SCpnt->cmd_len = SCpnt->old_cmd_len;	SCpnt->sc_data_direction = SCpnt->sc_old_data_direction;	SCpnt->underflow = SCpnt->old_underflow;        /*         * Zero the sense information from the last time we tried         * this command.         */	memset((void *) SCpnt->sense_buffer, 0, sizeof SCpnt->sense_buffer);	return scsi_dispatch_cmd(SCpnt);

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -