📄 scsi.c
字号:
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 */ pcount = next_scsi_host; /* 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 MOD_INC_USE_COUNT; 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. * * Note - there is a fatal flaw in the deregister module function. * There is no way to return a code that says 'I cannot be unloaded now'. * The system relies entirely upon usage counts that are maintained, * and the assumption is that if the usage count is 0, then the module * can be unloaded. */static void 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. */ /* * 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)) return; /* * 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"); return; } /* * 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); return; } 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, tpnt->proc_dir); if (tpnt->release) (*tpnt->release) (shpnt); else { /* This is the default case for the release function. * It should do the right thing for most correctly * written host adapters. */ if (shpnt->irq) free_irq(shpnt->irq, NULL); if (shpnt->dma_channel != 0xff) free_dma(shpnt->dma_channel); if (shpnt->io_port && shpnt->n_io_port) release_region(shpnt->io_port, shpnt->n_io_port); } if (pcount == next_scsi_host) scsi_unregister(shpnt); tpnt->present--; } /* * If there are absolutely no more hosts left, it is safe * to completely nuke the DMA pool. The resize operation will * do the right thing and free everything. */ if (!scsi_hosts) scsi_resize_dma_pool(); if (pcount0 != next_scsi_host) printk(KERN_INFO "scsi : %d host%s left.\n", next_scsi_host, (next_scsi_host == 1) ? "" : "s");#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 /* * Remove it from the linked list and /proc if all * hosts were successfully removed (ie preset == 0) */ if (!tpnt->present) { Scsi_Ho
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -