📄 scsi_lib.c
字号:
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){}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -