📄 cciss_scsi.c
字号:
static voidcciss_update_non_disk_devices(int cntl_num, int hostno){ /* the idea here is we could get notified from /proc that some devices have changed, so we do a report physical luns cmd, and adjust our list of devices accordingly. (We can't rely on the scsi-mid layer just doing inquiries, because the "busses" that the scsi mid-layer probes are totally fabricated by this driver, so new devices wouldn't show up. the scsi3addr's of devices won't change so long as the adapter is not reset. That means we can rescan and tell which devices we already know about, vs. new devices, vs. disappearing devices. Also, if you yank out a tape drive, then put in a disk in it's place, (say, a configured volume from another array controller for instance) _don't_ poke this driver (so it thinks it's still a tape, but _do_ poke the scsi mid layer, so it does an inquiry... the scsi mid layer will see the physical disk. This would be bad. Need to think about how to prevent that. One idea would be to snoop all scsi responses and if an inquiry repsonse comes back that reports a disk, chuck it an return selection timeout instead and adjust our table... Not sure i like that though. */#define OBDR_TAPE_INQ_SIZE 49#define OBDR_TAPE_SIG "$DR-10" ReportLunData_struct *ld_buff; unsigned char *inq_buff; unsigned char scsi3addr[8]; ctlr_info_t *c; __u32 num_luns=0; unsigned char *ch; /* unsigned char found[CCISS_MAX_SCSI_DEVS_PER_HBA]; */ struct cciss_scsi_dev_t currentsd[CCISS_MAX_SCSI_DEVS_PER_HBA]; int ncurrent=0; int reportlunsize = sizeof(*ld_buff) + CISS_MAX_PHYS_LUN * 8; int i; c = (ctlr_info_t *) hba[cntl_num]; ld_buff = kmalloc(reportlunsize, GFP_KERNEL); if (ld_buff == NULL) { printk(KERN_ERR "cciss: out of memory\n"); return; } memset(ld_buff, 0, reportlunsize); inq_buff = kmalloc(OBDR_TAPE_INQ_SIZE, GFP_KERNEL); if (inq_buff == NULL) { printk(KERN_ERR "cciss: out of memory\n"); kfree(ld_buff); return; } if (cciss_scsi_do_report_phys_luns(c, ld_buff, reportlunsize) == 0) { ch = &ld_buff->LUNListLength[0]; num_luns = ((ch[0]<<24) | (ch[1]<<16) | (ch[2]<<8) | ch[3]) / 8; if (num_luns > CISS_MAX_PHYS_LUN) { printk(KERN_WARNING "cciss: Maximum physical LUNs (%d) exceeded. " "%d LUNs ignored.\n", CISS_MAX_PHYS_LUN, num_luns - CISS_MAX_PHYS_LUN); num_luns = CISS_MAX_PHYS_LUN; } } else { printk(KERN_ERR "cciss: Report physical LUNs failed.\n"); goto out; } /* adjust our table of devices */ for(i=0; i<num_luns; i++) { int devtype; /* for each physical lun, do an inquiry */ if (ld_buff->LUN[i][3] & 0xC0) continue; memset(inq_buff, 0, OBDR_TAPE_INQ_SIZE); memcpy(&scsi3addr[0], &ld_buff->LUN[i][0], 8); if (cciss_scsi_do_inquiry(hba[cntl_num], scsi3addr, inq_buff, (unsigned char) OBDR_TAPE_INQ_SIZE) != 0) { /* Inquiry failed (msg printed already) */ devtype = 0; /* so we will skip this device. */ } else /* what kind of device is this? */ devtype = (inq_buff[0] & 0x1f); switch (devtype) { case 0x05: /* CD-ROM */ { /* We don't *really* support actual CD-ROM devices, * just this "One Button Disaster Recovery" tape drive * which temporarily pretends to be a CD-ROM drive. * So we check that the device is really an OBDR tape * device by checking for "$DR-10" in bytes 43-48 of * the inquiry data. */ char obdr_sig[7]; strncpy(obdr_sig, &inq_buff[43], 6); obdr_sig[6] = '\0'; if (strncmp(obdr_sig, OBDR_TAPE_SIG, 6) != 0) /* Not OBDR device, ignore it. */ break; } /* fall through . . . */ case 0x01: /* sequential access, (tape) */ case 0x08: /* medium changer */ if (ncurrent >= CCISS_MAX_SCSI_DEVS_PER_HBA) { printk(KERN_INFO "cciss%d: %s ignored, " "too many devices.\n", cntl_num, DEVICETYPE(devtype)); break; } memcpy(¤tsd[ncurrent].scsi3addr[0], &scsi3addr[0], 8); currentsd[ncurrent].devtype = devtype; currentsd[ncurrent].bus = -1; currentsd[ncurrent].target = -1; currentsd[ncurrent].lun = -1; ncurrent++; break; default: break; } } adjust_cciss_scsi_table(cntl_num, hostno, currentsd, ncurrent);out: kfree(inq_buff); kfree(ld_buff); return;}static intis_keyword(char *ptr, int len, char *verb) // Thanks to ncr53c8xx.c{ int verb_len = strlen(verb); if (len >= verb_len && !memcmp(verb,ptr,verb_len)) return verb_len; else return 0;}static intcciss_scsi_user_command(int ctlr, int hostno, char *buffer, int length){ int arg_len; if ((arg_len = is_keyword(buffer, length, "rescan")) != 0) cciss_update_non_disk_devices(ctlr, hostno); else return -EINVAL; return length;}static intcciss_scsi_proc_info(struct Scsi_Host *sh, char *buffer, /* data buffer */ char **start, /* where data in buffer starts */ off_t offset, /* offset from start of imaginary file */ int length, /* length of data in buffer */ int func) /* 0 == read, 1 == write */{ int buflen, datalen; ctlr_info_t *ci; int i; int cntl_num; ci = (ctlr_info_t *) sh->hostdata[0]; if (ci == NULL) /* This really shouldn't ever happen. */ return -EINVAL; cntl_num = ci->ctlr; /* Get our index into the hba[] array */ if (func == 0) { /* User is reading from /proc/scsi/ciss*?/?* */ buflen = sprintf(buffer, "cciss%d: SCSI host: %d\n", cntl_num, sh->host_no); /* this information is needed by apps to know which cciss device corresponds to which scsi host number without having to open a scsi target device node. The device information is not a duplicate of /proc/scsi/scsi because the two may be out of sync due to scsi hotplug, rather this info is for an app to be able to use to know how to get them back in sync. */ for (i=0;i<ccissscsi[cntl_num].ndevices;i++) { struct cciss_scsi_dev_t *sd = &ccissscsi[cntl_num].dev[i]; buflen += sprintf(&buffer[buflen], "c%db%dt%dl%d %02d " "0x%02x%02x%02x%02x%02x%02x%02x%02x\n", sh->host_no, sd->bus, sd->target, sd->lun, sd->devtype, sd->scsi3addr[0], sd->scsi3addr[1], sd->scsi3addr[2], sd->scsi3addr[3], sd->scsi3addr[4], sd->scsi3addr[5], sd->scsi3addr[6], sd->scsi3addr[7]); } datalen = buflen - offset; if (datalen < 0) { /* they're reading past EOF. */ datalen = 0; *start = buffer+buflen; } else *start = buffer + offset; return(datalen); } else /* User is writing to /proc/scsi/cciss*?/?* ... */ return cciss_scsi_user_command(cntl_num, sh->host_no, buffer, length); } /* cciss_scatter_gather takes a struct scsi_cmnd, (cmd), and does the pci dma mapping and fills in the scatter gather entries of the cciss command, cp. */static voidcciss_scatter_gather(struct pci_dev *pdev, CommandList_struct *cp, struct scsi_cmnd *cmd){ unsigned int use_sg, nsegs=0, len; struct scatterlist *scatter = (struct scatterlist *) cmd->buffer; __u64 addr64; /* is it just one virtual address? */ if (!cmd->use_sg) { if (cmd->request_bufflen) { /* anything to xfer? */ addr64 = (__u64) pci_map_single(pdev, cmd->request_buffer, cmd->request_bufflen, cmd->sc_data_direction); cp->SG[0].Addr.lower = (__u32) (addr64 & (__u64) 0x00000000FFFFFFFF); cp->SG[0].Addr.upper = (__u32) ((addr64 >> 32) & (__u64) 0x00000000FFFFFFFF); cp->SG[0].Len = cmd->request_bufflen; nsegs=1; } } /* else, must be a list of virtual addresses.... */ else if (cmd->use_sg <= MAXSGENTRIES) { /* not too many addrs? */ use_sg = pci_map_sg(pdev, cmd->buffer, cmd->use_sg, cmd->sc_data_direction); for (nsegs=0; nsegs < use_sg; nsegs++) { addr64 = (__u64) sg_dma_address(&scatter[nsegs]); len = sg_dma_len(&scatter[nsegs]); cp->SG[nsegs].Addr.lower = (__u32) (addr64 & (__u64) 0x00000000FFFFFFFF); cp->SG[nsegs].Addr.upper = (__u32) ((addr64 >> 32) & (__u64) 0x00000000FFFFFFFF); cp->SG[nsegs].Len = len; cp->SG[nsegs].Ext = 0; // we are not chaining } } else BUG(); cp->Header.SGList = (__u8) nsegs; /* no. SGs contig in this cmd */ cp->Header.SGTotal = (__u16) nsegs; /* total sgs in this cmd list */ return;}static intcciss_scsi_queue_command (struct scsi_cmnd *cmd, void (* done)(struct scsi_cmnd *)){ ctlr_info_t **c; int ctlr, rc; unsigned char scsi3addr[8]; CommandList_struct *cp; unsigned long flags; // Get the ptr to our adapter structure (hba[i]) out of cmd->host. // We violate cmd->host privacy here. (Is there another way?) c = (ctlr_info_t **) &cmd->device->host->hostdata[0]; ctlr = (*c)->ctlr; rc = lookup_scsi3addr(ctlr, cmd->device->channel, cmd->device->id, cmd->device->lun, scsi3addr); if (rc != 0) { /* the scsi nexus does not match any that we presented... */ /* pretend to mid layer that we got selection timeout */ cmd->result = DID_NO_CONNECT << 16; done(cmd); /* we might want to think about registering controller itself as a processor device on the bus so sg binds to it. */ return 0; } /* printk("cciss_queue_command, p=%p, cmd=0x%02x, c%db%dt%dl%d\n", cmd, cmd->cmnd[0], ctlr, cmd->channel, cmd->target, cmd->lun);*/ // printk("q:%p:c%db%dt%dl%d ", cmd, ctlr, cmd->channel, // cmd->target, cmd->lun); /* Ok, we have a reasonable scsi nexus, so send the cmd down, and see what the device thinks of it. */ spin_lock_irqsave(CCISS_LOCK(ctlr), flags); cp = scsi_cmd_alloc(*c); spin_unlock_irqrestore(CCISS_LOCK(ctlr), flags); if (cp == NULL) { /* trouble... */ printk("scsi_cmd_alloc returned NULL!\n"); /* FIXME: next 3 lines are -> BAD! <- */ cmd->result = DID_NO_CONNECT << 16; done(cmd); return 0; } // Fill in the command list header cmd->scsi_done = done; // save this for use by completion code // save cp in case we have to abort it cmd->host_scribble = (unsigned char *) cp; cp->cmd_type = CMD_SCSI; cp->scsi_cmd = cmd; cp->Header.ReplyQueue = 0; // unused in simple mode memcpy(&cp->Header.LUN.LunAddrBytes[0], &scsi3addr[0], 8); cp->Header.Tag.lower = cp->busaddr; // Use k. address of cmd as tag // Fill in the request block... cp->Request.Timeout = 0; memset(cp->Request.CDB, 0, sizeof(cp->Request.CDB)); if (cmd->cmd_len > sizeof(cp->Request.CDB)) BUG(); cp->Request.CDBLen = cmd->cmd_len; memcpy(cp->Request.CDB, cmd->cmnd, cmd->cmd_len); cp->Request.Type.Type = TYPE_CMD; cp->Request.Type.Attribute = ATTR_SIMPLE; switch(cmd->sc_data_direction) { case DMA_TO_DEVICE: cp->Request.Type.Direction = XFER_WRITE; break; case DMA_FROM_DEVICE: cp->Request.Type.Direction = XFER_READ; break; case DMA_NONE: cp->Request.Type.Direction = XFER_NONE; break; case DMA_BIDIRECTIONAL: // This can happen if a buggy application does a scsi passthru // and sets both inlen and outlen to non-zero. ( see // ../scsi/scsi_ioctl.c:scsi_ioctl_send_command() ) cp->Request.Type.Direction = XFER_RSVD; // This is technically wrong, and cciss controllers should // reject it with CMD_INVALID, which is the most correct // response, but non-fibre backends appear to let it // slide by, and give the same results as if this field // were set correctly. Either way is acceptable for // our purposes here. break; default: printk("cciss: unknown data direction: %d\n", cmd->sc_data_direction); BUG(); break; } cciss_scatter_gather((*c)->pdev, cp, cmd); // Fill the SG list /* Put the request on the tail of the request queue */ spin_lock_irqsave(CCISS_LOCK(ctlr), flags); addQ(&(*c)->reqQ, cp); (*c)->Qdepth++; start_io(*c); spin_unlock_irqrestore(CCISS_LOCK(ctlr), flags); /* the cmd'll come back via intr handler in complete_scsi_command() */ return 0;}static void cciss_unregister_scsi(int ctlr){ struct cciss_scsi_adapter_data_t *sa; struct cciss_scsi_cmd_stack_t *stk; unsigned long flags; /* we are being forcibly unloaded, and may not refuse. */ spin_lock_irqsave(CCISS_LOCK(ctlr), flags); sa = (struct cciss_scsi_adapter_data_t *) hba[ctlr]->scsi_ctlr; stk = &sa->cmd_stack; /* if we weren't ever actually registered, don't unregister */ if (sa->registered) { spin_unlock_irqrestore(CCISS_LOCK(ctlr), flags); scsi_remove_host(sa->scsi_host); scsi_host_put(sa->scsi_host); spin_lock_irqsave(CCISS_LOCK(ctlr), flags); } /* set scsi_host to NULL so our detect routine will find us on register */ sa->scsi_host = NULL; scsi_cmd_stack_free(ctlr); kfree(sa); spin_unlock_irqrestore(CCISS_LOCK(ctlr), flags);}static int cciss_register_scsi(int ctlr){ unsigned long flags; CPQ_TAPE_LOCK(ctlr, flags); /* Since this is really a block driver, the SCSI core may not be initialized at init time, in which case, calling scsi_register_host would hang. Instead, we do it later, via /proc filesystem and rc scripts, when we know SCSI core is good to go. */ /* Only register if SCSI devices are detected. */ if (ccissscsi[ctlr].ndevices != 0) { ((struct cciss_scsi_adapter_data_t *) hba[ctlr]->scsi_ctlr)->registered = 1; CPQ_TAPE_UNLOCK(ctlr, flags); return cciss_scsi_detect(ctlr); } CPQ_TAPE_UNLOCK(ctlr, flags); printk(KERN_INFO "cciss%d: No appropriate SCSI device detected, " "SCSI subsystem not engaged.\n", ctlr); return 0;}static int cciss_engage_scsi(int ctlr){ struct cciss_scsi_adapter_data_t *sa; struct cciss_scsi_cmd_stack_t *stk; unsigned long flags; spin_lock_irqsave(CCISS_LOCK(ctlr), flags); sa = (struct cciss_scsi_adapter_data_t *) hba[ctlr]->scsi_ctlr; stk = &sa->cmd_stack; if (((struct cciss_scsi_adapter_data_t *) hba[ctlr]->scsi_ctlr)->registered) { printk("cciss%d: SCSI subsystem already engaged.\n", ctlr); spin_unlock_irqrestore(CCISS_LOCK(ctlr), flags); return ENXIO; } spin_unlock_irqrestore(CCISS_LOCK(ctlr), flags); cciss_update_non_disk_devices(ctlr, -1); cciss_register_scsi(ctlr); return 0;}static voidcciss_proc_tape_report(int ctlr, unsigned char *buffer, off_t *pos, off_t *len){ unsigned long flags; int size; *pos = *pos -1; *len = *len - 1; // cut off the last trailing newline CPQ_TAPE_LOCK(ctlr, flags); size = sprintf(buffer + *len, "Sequential access devices: %d\n\n", ccissscsi[ctlr].ndevices); CPQ_TAPE_UNLOCK(ctlr, flags); *pos += size; *len += size;}#else /* no CONFIG_CISS_SCSI_TAPE *//* If no tape support, then these become defined out of existence */#define cciss_scsi_setup(cntl_num)#define cciss_unregister_scsi(ctlr)#define cciss_register_scsi(ctlr)#define cciss_proc_tape_report(ctlr, buffer, pos, len)#endif /* CONFIG_CISS_SCSI_TAPE */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -