X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=drivers%2Fblock%2Fcciss_scsi.c;h=597c007fe81b421614635eaf3cb8ae00347000fe;hb=34a75f0025b9cf803b6a88db032e6ad6950c9313;hp=9df82b8e95a5c021a9266c2dca54b5e6b2e58c96;hpb=9bf4aaab3e101692164d49b7ca357651eb691cb6;p=linux-2.6.git diff --git a/drivers/block/cciss_scsi.c b/drivers/block/cciss_scsi.c index 9df82b8e9..597c007fe 100644 --- a/drivers/block/cciss_scsi.c +++ b/drivers/block/cciss_scsi.c @@ -16,7 +16,7 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * - * Questions/Comments/Bugfixes to arrays@compaq.com + * Questions/Comments/Bugfixes to iss_storagedev@hp.com * * Author: Stephen M. Cameron */ @@ -28,14 +28,23 @@ through the array controller. Note in particular, neither physical nor logical disks are presented through the scsi layer. */ -#include "../scsi/scsi.h" -#include -#include #include #include +#include +#include + +#include + +#include +#include +#include +#include #include "cciss_scsi.h" +#define CCISS_ABORT_MSG 0x00 +#define CCISS_RESET_MSG 0x01 + /* some prototypes... */ static int sendcmd( __u8 cmd, @@ -51,9 +60,7 @@ static int sendcmd( int cmd_type); -const char *cciss_scsi_info(struct Scsi_Host *sa); - -int cciss_scsi_proc_info( +static int cciss_scsi_proc_info( struct Scsi_Host *sh, char *buffer, /* data buffer */ char **start, /* where data in buffer starts */ @@ -61,15 +68,10 @@ int cciss_scsi_proc_info( int length, /* length of data in buffer */ int func); /* 0 == read, 1 == write */ -int cciss_scsi_queue_command (Scsi_Cmnd *cmd, void (* done)(Scsi_Cmnd *)); -#if 0 -int cciss_scsi_abort(Scsi_Cmnd *cmd); -#if defined SCSI_RESET_SYNCHRONOUS && defined SCSI_RESET_ASYNCHRONOUS -int cciss_scsi_reset(Scsi_Cmnd *cmd, unsigned int reset_flags); -#else -int cciss_scsi_reset(Scsi_Cmnd *cmd); -#endif -#endif +static int cciss_scsi_queue_command (struct scsi_cmnd *cmd, + void (* done)(struct scsi_cmnd *)); +static int cciss_eh_device_reset_handler(struct scsi_cmnd *); +static int cciss_eh_abort_handler(struct scsi_cmnd *); static struct cciss_scsi_hba_t ccissscsi[MAX_CTLR] = { { .name = "cciss0", .ndevices = 0 }, @@ -82,7 +84,7 @@ static struct cciss_scsi_hba_t ccissscsi[MAX_CTLR] = { { .name = "cciss7", .ndevices = 0 }, }; -static Scsi_Host_Template cciss_driver_template = { +static struct scsi_host_template cciss_driver_template = { .module = THIS_MODULE, .name = "cciss", .proc_name = "cciss", @@ -93,6 +95,9 @@ static Scsi_Host_Template cciss_driver_template = { .sg_tablesize = MAXSGENTRIES, .cmd_per_lun = 1, .use_clustering = DISABLE_CLUSTERING, + /* Can't have eh_bus_reset_handler or eh_host_reset_handler for cciss */ + .eh_device_reset_handler= cciss_eh_device_reset_handler, + .eh_abort_handler = cciss_eh_abort_handler, }; #pragma pack(1) @@ -100,6 +105,7 @@ struct cciss_scsi_cmd_stack_elem_t { CommandList_struct cmd; ErrorInfo_struct Err; __u32 busaddr; + __u32 pad; }; #pragma pack() @@ -539,7 +545,7 @@ cciss_scsi_setup(int cntl_num) if (shba == NULL) return; shba->scsi_host = NULL; - shba->lock = SPIN_LOCK_UNLOCKED; + spin_lock_init(&shba->lock); shba->registered = 0; if (scsi_cmd_stack_setup(cntl_num, shba) != 0) { kfree(shba); @@ -552,7 +558,7 @@ cciss_scsi_setup(int cntl_num) static void complete_scsi_command( CommandList_struct *cp, int timeout, __u32 tag) { - Scsi_Cmnd *cmd; + struct scsi_cmnd *cmd; ctlr_info_t *ctlr; u64bit addr64; ErrorInfo_struct *ei; @@ -565,7 +571,7 @@ complete_scsi_command( CommandList_struct *cp, int timeout, __u32 tag) return; } - cmd = (Scsi_Cmnd *) cp->scsi_cmd; + cmd = (struct scsi_cmnd *) cp->scsi_cmd; ctlr = hba[cp->ctlr]; /* undo the DMA mappings */ @@ -573,14 +579,14 @@ complete_scsi_command( CommandList_struct *cp, int timeout, __u32 tag) if (cmd->use_sg) { pci_unmap_sg(ctlr->pdev, cmd->buffer, cmd->use_sg, - scsi_to_pci_dma_dir(cmd->sc_data_direction)); + cmd->sc_data_direction); } else if (cmd->request_bufflen) { addr64.val32.lower = cp->SG[0].Addr.lower; addr64.val32.upper = cp->SG[0].Addr.upper; pci_unmap_single(ctlr->pdev, (dma_addr_t) addr64.val, cmd->request_bufflen, - scsi_to_pci_dma_dir(cmd->sc_data_direction)); + cmd->sc_data_direction); } cmd->result = (DID_OK << 16); /* host byte */ @@ -696,28 +702,31 @@ static int cciss_scsi_detect(int ctlr) { struct Scsi_Host *sh; + int error; sh = scsi_host_alloc(&cciss_driver_template, sizeof(struct ctlr_info *)); if (sh == NULL) - return 0; - + goto fail; sh->io_port = 0; // good enough? FIXME, sh->n_io_port = 0; // I don't think we use these two... - sh->this_id = SELF_SCSI_ID; ((struct cciss_scsi_adapter_data_t *) hba[ctlr]->scsi_ctlr)->scsi_host = (void *) sh; sh->hostdata[0] = (unsigned long) hba[ctlr]; - sh->irq = hba[ctlr]->intr; + sh->irq = hba[ctlr]->intr[SIMPLE_MODE_INT]; sh->unique_id = sh->irq; - scsi_add_host(sh, &hba[ctlr]->pdev->dev); /* XXX handle failure */ + error = scsi_add_host(sh, &hba[ctlr]->pdev->dev); + if (error) + goto fail_host_put; scsi_scan_host(sh); - return 1; -} -static void __exit cleanup_cciss_module(void); + fail_host_put: + scsi_host_put(sh); + fail: + return 0; +} static void cciss_unmap_one(struct pci_dev *pdev, @@ -783,9 +792,8 @@ cciss_scsi_do_simple_cmd(ctlr_info_t *c, cp->Request.Type.Direction = direction; /* Fill in the SG list and do dma mapping */ - cciss_map_one(c->pdev, cp, - (unsigned char *) buf, bufsize, - scsi_to_pci_dma_dir(SCSI_DATA_READ)); + cciss_map_one(c->pdev, cp, (unsigned char *) buf, + bufsize, DMA_FROM_DEVICE); cp->waiting = &wait; @@ -799,9 +807,7 @@ cciss_scsi_do_simple_cmd(ctlr_info_t *c, wait_for_completion(&wait); /* undo the dma mapping */ - cciss_unmap_one(c->pdev, cp, bufsize, - scsi_to_pci_dma_dir(SCSI_DATA_READ)); - + cciss_unmap_one(c->pdev, cp, bufsize, DMA_FROM_DEVICE); return(0); } @@ -884,7 +890,7 @@ cciss_scsi_interpret_error(CommandList_struct *cp) static int cciss_scsi_do_inquiry(ctlr_info_t *c, unsigned char *scsi3addr, - InquiryData_struct *buf) + unsigned char *buf, unsigned char bufsize) { int rc; CommandList_struct *cp; @@ -907,11 +913,10 @@ cciss_scsi_do_inquiry(ctlr_info_t *c, unsigned char *scsi3addr, cdb[1] = 0; cdb[2] = 0; cdb[3] = 0; - cdb[4] = sizeof(*buf) & 0xff; + cdb[4] = bufsize; cdb[5] = 0; rc = cciss_scsi_do_simple_cmd(c, cp, scsi3addr, cdb, - 6, (unsigned char *) buf, - sizeof(*buf), XFER_READ); + 6, buf, bufsize, XFER_READ); if (rc != 0) return rc; /* something went wrong */ @@ -1007,9 +1012,10 @@ cciss_update_non_disk_devices(int cntl_num, int hostno) that though. */ - +#define OBDR_TAPE_INQ_SIZE 49 +#define OBDR_TAPE_SIG "$DR-10" ReportLunData_struct *ld_buff; - InquiryData_struct *inq_buff; + unsigned char *inq_buff; unsigned char scsi3addr[8]; ctlr_info_t *c; __u32 num_luns=0; @@ -1021,13 +1027,12 @@ cciss_update_non_disk_devices(int cntl_num, int hostno) int i; c = (ctlr_info_t *) hba[cntl_num]; - ld_buff = kmalloc(reportlunsize, GFP_KERNEL); + ld_buff = kzalloc(reportlunsize, GFP_KERNEL); if (ld_buff == NULL) { printk(KERN_ERR "cciss: out of memory\n"); return; } - memset(ld_buff, 0, reportlunsize); - inq_buff = kmalloc(sizeof( InquiryData_struct), GFP_KERNEL); + inq_buff = kmalloc(OBDR_TAPE_INQ_SIZE, GFP_KERNEL); if (inq_buff == NULL) { printk(KERN_ERR "cciss: out of memory\n"); kfree(ld_buff); @@ -1058,19 +1063,36 @@ cciss_update_non_disk_devices(int cntl_num, int hostno) /* for each physical lun, do an inquiry */ if (ld_buff->LUN[i][3] & 0xC0) continue; - memset(inq_buff, 0, sizeof(InquiryData_struct)); + 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) != 0) - { + 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->data_byte[0] & 0x1f); + 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) { @@ -1122,7 +1144,7 @@ cciss_scsi_user_command(int ctlr, int hostno, char *buffer, int length) } -int +static int cciss_scsi_proc_info(struct Scsi_Host *sh, char *buffer, /* data buffer */ char **start, /* where data in buffer starts */ @@ -1133,6 +1155,7 @@ cciss_scsi_proc_info(struct Scsi_Host *sh, int buflen, datalen; ctlr_info_t *ci; + int i; int cntl_num; @@ -1143,8 +1166,28 @@ cciss_scsi_proc_info(struct Scsi_Host *sh, cntl_num = ci->ctlr; /* Get our index into the hba[] array */ if (func == 0) { /* User is reading from /proc/scsi/ciss*?/?* */ - buflen = sprintf(buffer, "hostnum=%d\n", sh->host_no); - + 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;ihost_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; @@ -1157,37 +1200,14 @@ cciss_scsi_proc_info(struct Scsi_Host *sh, buffer, length); } -/* this is via the generic proc support */ -const char * -cciss_scsi_info(struct Scsi_Host *sa) -{ - static char buf[300]; - ctlr_info_t *ci; - - /* probably need to work on putting a bit more info in here... */ - /* this is output via the /proc filesystem. */ - - ci = (ctlr_info_t *) sa->hostdata[0]; - - sprintf(buf, "%s %c%c%c%c\n", - ci->product_name, - ci->firm_ver[0], - ci->firm_ver[1], - ci->firm_ver[2], - ci->firm_ver[3]); - - return buf; -} - - -/* cciss_scatter_gather takes a Scsi_Cmnd, (cmd), and does the pci +/* 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 void cciss_scatter_gather(struct pci_dev *pdev, CommandList_struct *cp, - Scsi_Cmnd *cmd) + struct scsi_cmnd *cmd) { unsigned int use_sg, nsegs=0, len; struct scatterlist *scatter = (struct scatterlist *) cmd->buffer; @@ -1200,7 +1220,7 @@ cciss_scatter_gather(struct pci_dev *pdev, addr64 = (__u64) pci_map_single(pdev, cmd->request_buffer, cmd->request_bufflen, - scsi_to_pci_dma_dir(cmd->sc_data_direction)); + cmd->sc_data_direction); cp->SG[0].Addr.lower = (__u32) (addr64 & (__u64) 0x00000000FFFFFFFF); @@ -1213,7 +1233,7 @@ cciss_scatter_gather(struct pci_dev *pdev, else if (cmd->use_sg <= MAXSGENTRIES) { /* not too many addrs? */ use_sg = pci_map_sg(pdev, cmd->buffer, cmd->use_sg, - scsi_to_pci_dma_dir(cmd->sc_data_direction)); + cmd->sc_data_direction); for (nsegs=0; nsegs < use_sg; nsegs++) { addr64 = (__u64) sg_dma_address(&scatter[nsegs]); @@ -1233,8 +1253,8 @@ cciss_scatter_gather(struct pci_dev *pdev, } -int -cciss_scsi_queue_command (Scsi_Cmnd *cmd, void (* done)(Scsi_Cmnd *)) +static int +cciss_scsi_queue_command (struct scsi_cmnd *cmd, void (* done)(struct scsi_cmnd *)) { ctlr_info_t **c; int ctlr, rc; @@ -1295,18 +1315,17 @@ cciss_scsi_queue_command (Scsi_Cmnd *cmd, void (* done)(Scsi_Cmnd *)) cp->Request.Timeout = 0; memset(cp->Request.CDB, 0, sizeof(cp->Request.CDB)); - if (cmd->cmd_len > sizeof(cp->Request.CDB)) BUG(); + BUG_ON(cmd->cmd_len > sizeof(cp->Request.CDB)); 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 SCSI_DATA_WRITE: cp->Request.Type.Direction = XFER_WRITE; break; - case SCSI_DATA_READ: cp->Request.Type.Direction = XFER_READ; break; - case SCSI_DATA_NONE: cp->Request.Type.Direction = XFER_NONE; break; - - case SCSI_DATA_UNKNOWN: + 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() ) @@ -1430,12 +1449,84 @@ cciss_proc_tape_report(int ctlr, unsigned char *buffer, off_t *pos, off_t *len) CPQ_TAPE_LOCK(ctlr, flags); size = sprintf(buffer + *len, - " Sequential access devices: %d\n\n", + "Sequential access devices: %d\n\n", ccissscsi[ctlr].ndevices); CPQ_TAPE_UNLOCK(ctlr, flags); *pos += size; *len += size; } +/* Need at least one of these error handlers to keep ../scsi/hosts.c from + * complaining. Doing a host- or bus-reset can't do anything good here. + * Despite what it might say in scsi_error.c, there may well be commands + * on the controller, as the cciss driver registers twice, once as a block + * device for the logical drives, and once as a scsi device, for any tape + * drives. So we know there are no commands out on the tape drives, but we + * don't know there are no commands on the controller, and it is likely + * that there probably are, as the cciss block device is most commonly used + * as a boot device (embedded controller on HP/Compaq systems.) +*/ + +static int cciss_eh_device_reset_handler(struct scsi_cmnd *scsicmd) +{ + int rc; + CommandList_struct *cmd_in_trouble; + ctlr_info_t **c; + int ctlr; + + /* find the controller to which the command to be aborted was sent */ + c = (ctlr_info_t **) &scsicmd->device->host->hostdata[0]; + if (c == NULL) /* paranoia */ + return FAILED; + ctlr = (*c)->ctlr; + printk(KERN_WARNING "cciss%d: resetting tape drive or medium changer.\n", ctlr); + + /* find the command that's giving us trouble */ + cmd_in_trouble = (CommandList_struct *) scsicmd->host_scribble; + if (cmd_in_trouble == NULL) { /* paranoia */ + return FAILED; + } + /* send a reset to the SCSI LUN which the command was sent to */ + rc = sendcmd(CCISS_RESET_MSG, ctlr, NULL, 0, 2, 0, 0, + (unsigned char *) &cmd_in_trouble->Header.LUN.LunAddrBytes[0], + TYPE_MSG); + /* sendcmd turned off interrputs on the board, turn 'em back on. */ + (*c)->access.set_intr_mask(*c, CCISS_INTR_ON); + if (rc == 0) + return SUCCESS; + printk(KERN_WARNING "cciss%d: resetting device failed.\n", ctlr); + return FAILED; +} + +static int cciss_eh_abort_handler(struct scsi_cmnd *scsicmd) +{ + int rc; + CommandList_struct *cmd_to_abort; + ctlr_info_t **c; + int ctlr; + + /* find the controller to which the command to be aborted was sent */ + c = (ctlr_info_t **) &scsicmd->device->host->hostdata[0]; + if (c == NULL) /* paranoia */ + return FAILED; + ctlr = (*c)->ctlr; + printk(KERN_WARNING "cciss%d: aborting tardy SCSI cmd\n", ctlr); + + /* find the command to be aborted */ + cmd_to_abort = (CommandList_struct *) scsicmd->host_scribble; + if (cmd_to_abort == NULL) /* paranoia */ + return FAILED; + rc = sendcmd(CCISS_ABORT_MSG, ctlr, &cmd_to_abort->Header.Tag, + 0, 2, 0, 0, + (unsigned char *) &cmd_to_abort->Header.LUN.LunAddrBytes[0], + TYPE_MSG); + /* sendcmd turned off interrputs on the board, turn 'em back on. */ + (*c)->access.set_intr_mask(*c, CCISS_INTR_ON); + if (rc == 0) + return SUCCESS; + return FAILED; + +} + #else /* no CONFIG_CISS_SCSI_TAPE */ /* If no tape support, then these become defined out of existence */