📄 scsi.c
字号:
}/* * Function: scsi_finish_command * * Purpose: Pass command off to upper layer for finishing of I/O * request, waking processes that are waiting on results, * etc. */void scsi_finish_command(Scsi_Cmnd * SCpnt){ struct Scsi_Host *host; Scsi_Device *device; Scsi_Request * SRpnt; unsigned long flags; ASSERT_LOCK(&io_request_lock, 0); host = SCpnt->host; device = SCpnt->device; /* * We need to protect the decrement, as otherwise a race condition * would exist. Fiddling with SCpnt isn't a problem as the * design only allows a single SCpnt to be active in only * one execution context, but the device and host structures are * shared. */ spin_lock_irqsave(&io_request_lock, flags); host->host_busy--; /* Indicate that we are free */ device->device_busy--; /* Decrement device usage counter. */ spin_unlock_irqrestore(&io_request_lock, flags); /* * Clear the flags which say that the device/host is no longer * capable of accepting new commands. These are set in scsi_queue.c * for both the queue full condition on a device, and for a * host full condition on the host. */ host->host_blocked = FALSE; device->device_blocked = FALSE; /* * If we have valid sense information, then some kind of recovery * must have taken place. Make a note of this. */ if (scsi_sense_valid(SCpnt)) { SCpnt->result |= (DRIVER_SENSE << 24); } SCSI_LOG_MLCOMPLETE(3, printk("Notifying upper driver of completion for device %d %x\n", SCpnt->device->id, SCpnt->result)); SCpnt->owner = SCSI_OWNER_HIGHLEVEL; SCpnt->state = SCSI_STATE_FINISHED; /* We can get here with use_sg=0, causing a panic in the upper level (DB) */ SCpnt->use_sg = SCpnt->old_use_sg; /* * If there is an associated request structure, copy the data over before we call the * completion function. */ SRpnt = SCpnt->sc_request; if( SRpnt != NULL ) { SRpnt->sr_result = SRpnt->sr_command->result; if( SRpnt->sr_result != 0 ) { memcpy(SRpnt->sr_sense_buffer, SRpnt->sr_command->sense_buffer, sizeof(SRpnt->sr_sense_buffer)); } } SCpnt->done(SCpnt);}static int scsi_register_host(Scsi_Host_Template *);static void scsi_unregister_host(Scsi_Host_Template *);/* * Function: scsi_release_commandblocks() * * Purpose: Release command blocks associated with a device. * * Arguments: SDpnt - device * * Returns: Nothing * * Lock status: No locking assumed or required. * * Notes: */void scsi_release_commandblocks(Scsi_Device * SDpnt){ Scsi_Cmnd *SCpnt, *SCnext; unsigned long flags; spin_lock_irqsave(&device_request_lock, flags); for (SCpnt = SDpnt->device_queue; SCpnt; SCpnt = SCnext) { SDpnt->device_queue = SCnext = SCpnt->next; kfree((char *) SCpnt); } SDpnt->has_cmdblocks = 0; SDpnt->queue_depth = 0; spin_unlock_irqrestore(&device_request_lock, flags);}/* * Function: scsi_build_commandblocks() * * Purpose: Allocate command blocks associated with a device. * * Arguments: SDpnt - device * * Returns: Nothing * * Lock status: No locking assumed or required. * * Notes: */void scsi_build_commandblocks(Scsi_Device * SDpnt){ unsigned long flags; struct Scsi_Host *host = SDpnt->host; int j; Scsi_Cmnd *SCpnt; spin_lock_irqsave(&device_request_lock, flags); if (SDpnt->queue_depth == 0) { SDpnt->queue_depth = host->cmd_per_lun; if (SDpnt->queue_depth == 0) SDpnt->queue_depth = 1; /* live to fight another day */ } SDpnt->device_queue = NULL; for (j = 0; j < SDpnt->queue_depth; j++) { SCpnt = (Scsi_Cmnd *) kmalloc(sizeof(Scsi_Cmnd), GFP_ATOMIC | (host->unchecked_isa_dma ? GFP_DMA : 0)); if (NULL == SCpnt) break; /* If not, the next line will oops ... */ memset(SCpnt, 0, sizeof(Scsi_Cmnd)); SCpnt->host = host; SCpnt->device = SDpnt; SCpnt->target = SDpnt->id; SCpnt->lun = SDpnt->lun; SCpnt->channel = SDpnt->channel; SCpnt->request.rq_status = RQ_INACTIVE; SCpnt->use_sg = 0; SCpnt->old_use_sg = 0; SCpnt->old_cmd_len = 0; SCpnt->underflow = 0; SCpnt->old_underflow = 0; SCpnt->transfersize = 0; SCpnt->resid = 0; SCpnt->serial_number = 0; SCpnt->serial_number_at_timeout = 0; SCpnt->host_scribble = NULL; SCpnt->next = SDpnt->device_queue; SDpnt->device_queue = SCpnt; SCpnt->state = SCSI_STATE_UNUSED; SCpnt->owner = SCSI_OWNER_NOBODY; } if (j < SDpnt->queue_depth) { /* low on space (D.Gilbert 990424) */ printk(KERN_WARNING "scsi_build_commandblocks: want=%d, space for=%d blocks\n", SDpnt->queue_depth, j); SDpnt->queue_depth = j; SDpnt->has_cmdblocks = (0 != j); } else { SDpnt->has_cmdblocks = 1; } spin_unlock_irqrestore(&device_request_lock, flags);}static int proc_scsi_gen_write(struct file * file, const char * buf, unsigned long length, void *data);void __init scsi_host_no_insert(char *str, int n){ Scsi_Host_Name *shn, *shn2; int len; len = strlen(str); if (len && (shn = (Scsi_Host_Name *) kmalloc(sizeof(Scsi_Host_Name), GFP_ATOMIC))) { if ((shn->name = kmalloc(len+1, GFP_ATOMIC))) { strncpy(shn->name, str, len); shn->name[len] = 0; shn->host_no = n; shn->host_registered = 0; shn->loaded_as_module = 1; /* numbers shouldn't be freed in any case */ shn->next = NULL; if (scsi_host_no_list) { for (shn2 = scsi_host_no_list;shn2->next;shn2 = shn2->next) ; shn2->next = shn; } else scsi_host_no_list = shn; max_scsi_hosts = n+1; } else kfree((char *) shn); }}#ifdef CONFIG_PROC_FSstatic int scsi_proc_info(char *buffer, char **start, off_t offset, int length){ Scsi_Device *scd; struct Scsi_Host *HBA_ptr; int size, len = 0; off_t begin = 0; off_t pos = 0; /* * First, see if there are any attached devices or not. */ for (HBA_ptr = scsi_hostlist; HBA_ptr; HBA_ptr = HBA_ptr->next) { if (HBA_ptr->host_queue != NULL) { break; } } size = sprintf(buffer + len, "Attached devices: %s\n", (HBA_ptr) ? "" : "none"); len += size; pos = begin + len; for (HBA_ptr = scsi_hostlist; HBA_ptr; HBA_ptr = HBA_ptr->next) {#if 0 size += sprintf(buffer + len, "scsi%2d: %s\n", (int) HBA_ptr->host_no, HBA_ptr->hostt->procname); len += size; pos = begin + len;#endif for (scd = HBA_ptr->host_queue; scd; scd = scd->next) { proc_print_scsidevice(scd, buffer, &size, len); len += size; pos = begin + len; if (pos < offset) { len = 0; begin = pos; } if (pos > offset + length) goto stop_output; } }stop_output: *start = buffer + (offset - begin); /* Start of wanted data */ len -= (offset - begin); /* Start slop */ if (len > length) len = length; /* Ending slop */ return (len);}static int proc_scsi_gen_write(struct file * file, const char * buf, unsigned long length, void *data){ struct Scsi_Device_Template *SDTpnt; Scsi_Device *scd; struct Scsi_Host *HBA_ptr; char *p; int host, channel, id, lun; char * buffer; int err; if (!buf || length>PAGE_SIZE) return -EINVAL; if (!(buffer = (char *) __get_free_page(GFP_KERNEL))) return -ENOMEM; copy_from_user(buffer, buf, length); err = -EINVAL; if (length < 11 || strncmp("scsi", buffer, 4)) goto out; /* * Usage: echo "scsi dump #N" > /proc/scsi/scsi * to dump status of all scsi commands. The number is used to specify the level * of detail in the dump. */ if (!strncmp("dump", buffer + 5, 4)) { unsigned int level; p = buffer + 10; if (*p == '\0') goto out; level = simple_strtoul(p, NULL, 0); scsi_dump_status(level); } /* * Usage: echo "scsi log token #N" > /proc/scsi/scsi * where token is one of [error,scan,mlqueue,mlcomplete,llqueue, * llcomplete,hlqueue,hlcomplete] */#ifdef CONFIG_SCSI_LOGGING /* { */ if (!strncmp("log", buffer + 5, 3)) { char *token; unsigned int level; p = buffer + 9; token = p; while (*p != ' ' && *p != '\t' && *p != '\0') { p++; } if (*p == '\0') { if (strncmp(token, "all", 3) == 0) { /* * Turn on absolutely everything. */ scsi_logging_level = ~0; } else if (strncmp(token, "none", 4) == 0) { /* * Turn off absolutely everything. */ scsi_logging_level = 0; } else { goto out; } } else { *p++ = '\0'; level = simple_strtoul(p, NULL, 0); /* * Now figure out what to do with it. */ if (strcmp(token, "error") == 0) { SCSI_SET_ERROR_RECOVERY_LOGGING(level); } else if (strcmp(token, "timeout") == 0) { SCSI_SET_TIMEOUT_LOGGING(level); } else if (strcmp(token, "scan") == 0) { SCSI_SET_SCAN_BUS_LOGGING(level); } else if (strcmp(token, "mlqueue") == 0) { SCSI_SET_MLQUEUE_LOGGING(level); } else if (strcmp(token, "mlcomplete") == 0) { SCSI_SET_MLCOMPLETE_LOGGING(level); } else if (strcmp(token, "llqueue") == 0) { SCSI_SET_LLQUEUE_LOGGING(level); } else if (strcmp(token, "llcomplete") == 0) { SCSI_SET_LLCOMPLETE_LOGGING(level); } else if (strcmp(token, "hlqueue") == 0) { SCSI_SET_HLQUEUE_LOGGING(level); } else if (strcmp(token, "hlcomplete") == 0) { SCSI_SET_HLCOMPLETE_LOGGING(level); } else if (strcmp(token, "ioctl") == 0) { SCSI_SET_IOCTL_LOGGING(level); } else { goto out; } } printk(KERN_INFO "scsi logging level set to 0x%8.8x\n", scsi_logging_level); }#endif /* CONFIG_SCSI_LOGGING */ /* } */ /* * Usage: echo "scsi add-single-device 0 1 2 3" >/proc/scsi/scsi * with "0 1 2 3" replaced by your "Host Channel Id Lun". * Consider this feature BETA. * CAUTION: This is not for hotplugging your peripherals. As * SCSI was not designed for this you could damage your * hardware ! * However perhaps it is legal to switch on an * already connected device. It is perhaps not * guaranteed this device doesn't corrupt an ongoing data transfer. */ if (!strncmp("add-single-device", buffer + 5, 17)) { p = buffer + 23; host = simple_strtoul(p, &p, 0); channel = simple_strtoul(p + 1, &p, 0); id = simple_strtoul(p + 1, &p, 0); lun = simple_strtoul(p + 1, &p, 0); printk(KERN_INFO "scsi singledevice %d %d %d %d\n", host, channel, id, lun); for (HBA_ptr = scsi_hostlist; HBA_ptr; HBA_ptr = HBA_ptr->next) { if (HBA_ptr->host_no == host) { break; } } err = -ENXIO; if (!HBA_ptr) goto out; for (scd = HBA_ptr->host_queue; scd; scd = scd->next) { if ((scd->channel == channel && scd->id == id && scd->lun == lun)) { break; } } err = -ENOSYS; if (scd) goto out; /* We do not yet support unplugging */ scan_scsis(HBA_ptr, 1, channel, id, lun); /* FIXME (DB) This assumes that the queue_depth routines can be used in this context as well, while they were all designed to be called only once after the detect routine. (DB) */ /* queue_depth routine moved to inside scan_scsis(,1,,,) so it is called before build_commandblocks() */ err = length; goto out; } /* * Usage: echo "scsi remove-single-device 0 1 2 3" >/proc/scsi/scsi * with "0 1 2 3" replaced by your "Host Channel Id Lun". * * Consider this feature pre-BETA. * * CAUTION: This is not for hotplugging your peripherals. As * SCSI was not designed for this you could damage your * hardware and thoroughly confuse the SCSI subsystem. * */ else if (!strncmp("remove-single-device", buffer + 5, 20)) { p = buffer + 26; host = simple_strtoul(p, &p, 0); channel = simple_strtoul(p + 1, &p, 0); id = simple_strtoul(p + 1, &p, 0); lun = simple_strtoul(p + 1, &p, 0);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -