📄 scsi.c
字号:
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); for (HBA_ptr = scsi_hostlist; HBA_ptr; HBA_ptr = HBA_ptr->next) { if (HBA_ptr->host_no == host) { break; } } err = -ENODEV; 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; } } if (scd == NULL) goto out; /* there is no such device attached */ err = -EBUSY; if (scd->access_count) goto out; SDTpnt = scsi_devicelist; while (SDTpnt != NULL) { if (SDTpnt->detach) (*SDTpnt->detach) (scd); SDTpnt = SDTpnt->next; } if (scd->attached == 0) { /* * Nobody is using this device any more. * Free all of the command structures. */ if (HBA_ptr->hostt->revoke) HBA_ptr->hostt->revoke(scd); devfs_unregister (scd->de); scsi_release_commandblocks(scd); /* Now we can remove the device structure */ if (scd->next != NULL) scd->next->prev = scd->prev; if (scd->prev != NULL) scd->prev->next = scd->next; if (HBA_ptr->host_queue == scd) { HBA_ptr->host_queue = scd->next; } blk_cleanup_queue(&scd->request_queue); kfree((char *) scd); } else { goto out; } err = 0; }out: free_page((unsigned long) buffer); return err;}#endif/* * This entry point should be called by a driver if it is trying * to add a low level scsi driver to the system. */static int scsi_register_host(Scsi_Host_Template * tpnt){ int pcount; struct Scsi_Host *shpnt; Scsi_Device *SDpnt; struct Scsi_Device_Template *sdtpnt; const char *name; unsigned long flags; int out_of_space = 0; if (tpnt->next || !tpnt->detect) return 1; /* Must be already loaded, or * no detect routine available */ /* If max_sectors isn't set, default to max */ if (!tpnt->max_sectors) tpnt->max_sectors = MAX_SECTORS; pcount = next_scsi_host; MOD_INC_USE_COUNT; /* The detect routine must carefully spinunlock/spinlock if it enables interrupts, since all interrupt handlers do spinlock as well. All lame drivers are going to fail due to the following spinlock. For the time beeing let's use it only for drivers using the new scsi code. NOTE: the detect routine could redefine the value tpnt->use_new_eh_code. (DB, 13 May 1998) */ if (tpnt->use_new_eh_code) { spin_lock_irqsave(&io_request_lock, flags); tpnt->present = tpnt->detect(tpnt); spin_unlock_irqrestore(&io_request_lock, flags); } else tpnt->present = tpnt->detect(tpnt); if (tpnt->present) { if (pcount == next_scsi_host) { if (tpnt->present > 1) { printk(KERN_ERR "scsi: Failure to register low-level scsi driver"); scsi_unregister_host(tpnt); return 1; } /* * The low-level driver failed to register a driver. * We can do this now. */ if(scsi_register(tpnt, 0)==NULL) { printk(KERN_ERR "scsi: register failed.\n"); scsi_unregister_host(tpnt); return 1; } } tpnt->next = scsi_hosts; /* Add to the linked list */ scsi_hosts = tpnt; /* Add the new driver to /proc/scsi */#ifdef CONFIG_PROC_FS build_proc_dir_entries(tpnt);#endif /* * Add the kernel threads for each host adapter that will * handle error correction. */ for (shpnt = scsi_hostlist; shpnt; shpnt = shpnt->next) { if (shpnt->hostt == tpnt && shpnt->hostt->use_new_eh_code) { DECLARE_MUTEX_LOCKED(sem); shpnt->eh_notify = &sem; kernel_thread((int (*)(void *)) scsi_error_handler, (void *) shpnt, 0); /* * Now wait for the kernel error thread to initialize itself * as it might be needed when we scan the bus. */ down(&sem); shpnt->eh_notify = NULL; } } for (shpnt = scsi_hostlist; shpnt; shpnt = shpnt->next) { if (shpnt->hostt == tpnt) { if (tpnt->info) { name = tpnt->info(shpnt); } else { name = tpnt->name; } printk(KERN_INFO "scsi%d : %s\n", /* And print a little message */ shpnt->host_no, name); } } /* The next step is to call scan_scsis here. This generates the * Scsi_Devices entries */ for (shpnt = scsi_hostlist; shpnt; shpnt = shpnt->next) { if (shpnt->hostt == tpnt) { scan_scsis(shpnt, 0, 0, 0, 0); if (shpnt->select_queue_depths != NULL) { (shpnt->select_queue_depths) (shpnt, shpnt->host_queue); } } } for (sdtpnt = scsi_devicelist; sdtpnt; sdtpnt = sdtpnt->next) { if (sdtpnt->init && sdtpnt->dev_noticed) (*sdtpnt->init) (); } /* * Next we create the Scsi_Cmnd structures for this host */ for (shpnt = scsi_hostlist; shpnt; shpnt = shpnt->next) { for (SDpnt = shpnt->host_queue; SDpnt; SDpnt = SDpnt->next) if (SDpnt->host->hostt == tpnt) { for (sdtpnt = scsi_devicelist; sdtpnt; sdtpnt = sdtpnt->next) if (sdtpnt->attach) (*sdtpnt->attach) (SDpnt); if (SDpnt->attached) { scsi_build_commandblocks(SDpnt); if (0 == SDpnt->has_cmdblocks) out_of_space = 1; } } } /* * Now that we have all of the devices, resize the DMA pool, * as required. */ if (!out_of_space) scsi_resize_dma_pool(); /* This does any final handling that is required. */ for (sdtpnt = scsi_devicelist; sdtpnt; sdtpnt = sdtpnt->next) { if (sdtpnt->finish && sdtpnt->nr_dev) { (*sdtpnt->finish) (); } } }#if defined(USE_STATIC_SCSI_MEMORY) printk("SCSI memory: total %ldKb, used %ldKb, free %ldKb.\n", (scsi_memory_upper_value - scsi_memory_lower_value) / 1024, (scsi_init_memory_start - scsi_memory_lower_value) / 1024, (scsi_memory_upper_value - scsi_init_memory_start) / 1024);#endif if (out_of_space) { scsi_unregister_host(tpnt); /* easiest way to clean up?? */ return 1; } else return 0;}/* * Similarly, this entry point should be called by a loadable module if it * is trying to remove a low level scsi driver from the system. */static int scsi_unregister_host(Scsi_Host_Template * tpnt){ int online_status; int pcount0, pcount; Scsi_Cmnd *SCpnt; Scsi_Device *SDpnt; Scsi_Device *SDpnt1; struct Scsi_Device_Template *sdtpnt; struct Scsi_Host *sh1; struct Scsi_Host *shpnt; char name[10]; /* host_no>=10^9? I don't think so. */ /* get the big kernel lock, so we don't race with open() */ lock_kernel(); /* * First verify that this host adapter is completely free with no pending * commands */ for (shpnt = scsi_hostlist; shpnt; shpnt = shpnt->next) { for (SDpnt = shpnt->host_queue; SDpnt; SDpnt = SDpnt->next) { if (SDpnt->host->hostt == tpnt && SDpnt->host->hostt->module && GET_USE_COUNT(SDpnt->host->hostt->module)) goto err_out; /* * FIXME(eric) - We need to find a way to notify the * low level driver that we are shutting down - via the * special device entry that still needs to get added. * * Is detach interface below good enough for this? */ } } /* * FIXME(eric) put a spinlock on this. We force all of the devices offline * to help prevent race conditions where other hosts/processors could try and * get in and queue a command. */ for (shpnt = scsi_hostlist; shpnt; shpnt = shpnt->next) { for (SDpnt = shpnt->host_queue; SDpnt; SDpnt = SDpnt->next) { if (SDpnt->host->hostt == tpnt) SDpnt->online = FALSE; } } for (shpnt = scsi_hostlist; shpnt; shpnt = shpnt->next) { if (shpnt->hostt != tpnt) { continue; } for (SDpnt = shpnt->host_queue; SDpnt; SDpnt = SDpnt->next) { /* * Loop over all of the commands associated with the device. If any of * them are busy, then set the state back to inactive and bail. */ for (SCpnt = SDpnt->device_queue; SCpnt; SCpnt = SCpnt->next) { online_status = SDpnt->online; SDpnt->online = FALSE; if (SCpnt->request.rq_status != RQ_INACTIVE) { printk(KERN_ERR "SCSI device not inactive - rq_status=%d, target=%d, pid=%ld, state=%d, owner=%d.\n", SCpnt->request.rq_status, SCpnt->target, SCpnt->pid, SCpnt->state, SCpnt->owner); for (SDpnt1 = shpnt->host_queue; SDpnt1; SDpnt1 = SDpnt1->next) { for (SCpnt = SDpnt1->device_queue; SCpnt; SCpnt = SCpnt->next) if (SCpnt->request.rq_status == RQ_SCSI_DISCONNECTING) SCpnt->request.rq_status = RQ_INACTIVE; } SDpnt->online = online_status; printk(KERN_ERR "Device busy???\n"); goto err_out; } /* * No, this device is really free. Mark it as such, and * continue on. */ SCpnt->state = SCSI_STATE_DISCONNECTING; SCpnt->request.rq_status = RQ_SCSI_DISCONNECTING; /* Mark as busy */ } } } /* Next we detach the high level drivers from the Scsi_Device structures */ for (shpnt = scsi_hostlist; shpnt; shpnt = shpnt->next) { if (shpnt->hostt != tpnt) { continue; } for (SDpnt = shpnt->host_queue; SDpnt; SDpnt = SDpnt->next) { for (sdtpnt = scsi_devicelist; sdtpnt; sdtpnt = sdtpnt->next) if (sdtpnt->detach) (*sdtpnt->detach) (SDpnt); /* If something still attached, punt */ if (SDpnt->attached) { printk(KERN_ERR "Attached usage count = %d\n", SDpnt->attached); goto err_out; } devfs_unregister (SDpnt->de); } } /* * Next, kill the kernel error recovery thread for this host. */ for (shpnt = scsi_hostlist; shpnt; shpnt = shpnt->next) { if (shpnt->hostt == tpnt && shpnt->hostt->use_new_eh_code && shpnt->ehandler != NULL) { DECLARE_MUTEX_LOCKED(sem); shpnt->eh_notify = &sem; send_sig(SIGHUP, shpnt->ehandler, 1); down(&sem); shpnt->eh_notify = NULL; } } /* Next we free up the Scsi_Cmnd structures for this host */ for (shpnt = scsi_hostlist; shpnt; shpnt = shpnt->next) { if (shpnt->hostt != tpnt) { continue; } for (SDpnt = shpnt->host_queue; SDpnt; SDpnt = shpnt->host_queue) { scsi_release_commandblocks(SDpnt); blk_cleanup_queue(&SDpnt->request_queue); /* Next free up the Scsi_Device structures for this host */ shpnt->host_queue = SDpnt->next; kfree((char *) SDpnt); } } /* Next we go through and remove the instances of the individual hosts * that were detected */ pcount0 = next_scsi_host; for (shpnt = scsi_hostlist; shpnt; shpnt = sh1) { sh1 = shpnt->next; if (shpnt->hostt != tpnt) continue; pcount = next_scsi_host; /* Remove the /proc/scsi directory entry */ sprintf(name,"%d",shpnt->host_no); remove_proc_entry(name
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -