diff options
Diffstat (limited to 'drivers/scsi/3w-xxxx.c')
| -rw-r--r-- | drivers/scsi/3w-xxxx.c | 283 |
1 files changed, 136 insertions, 147 deletions
diff --git a/drivers/scsi/3w-xxxx.c b/drivers/scsi/3w-xxxx.c index 973c51fb0fe..752624e6bc0 100644 --- a/drivers/scsi/3w-xxxx.c +++ b/drivers/scsi/3w-xxxx.c @@ -1,14 +1,14 @@ /* 3w-xxxx.c -- 3ware Storage Controller device driver for Linux. - Written By: Adam Radford <linuxraid@amcc.com> + Written By: Adam Radford <linuxraid@lsi.com> Modifications By: Joel Jacobson <linux@3ware.com> Arnaldo Carvalho de Melo <acme@conectiva.com.br> Brad Strand <linux@3ware.com> - Copyright (C) 1999-2005 3ware Inc. + Copyright (C) 1999-2010 3ware Inc. - Kernel compatiblity By: Andre Hedrick <andre@suse.com> + Kernel compatibility By: Andre Hedrick <andre@suse.com> Non-Copyright (C) 2000 Andre Hedrick <andre@suse.com> Further tiny build fixes and trivial hoovering Alan Cox @@ -47,10 +47,10 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Bugs/Comments/Suggestions should be mailed to: - linuxraid@amcc.com + linuxraid@lsi.com For more information, goto: - http://www.amcc.com + http://www.lsi.com History ------- @@ -191,6 +191,10 @@ before shutting down card. Change to new 'change_queue_depth' api. Fix 'handled=1' ISR usage, remove bogus IRQ check. + 1.26.02.002 - Free irq handler in __tw_shutdown(). + Turn on RCD bit for caching mode page. + Serialize reset code. + 1.26.02.003 - Force 60 second timeout default. */ #include <linux/module.h> @@ -201,8 +205,10 @@ #include <linux/errno.h> #include <linux/types.h> #include <linux/delay.h> +#include <linux/gfp.h> #include <linux/pci.h> #include <linux/time.h> +#include <linux/mutex.h> #include <asm/io.h> #include <asm/irq.h> #include <asm/uaccess.h> @@ -210,22 +216,24 @@ #include <scsi/scsi_host.h> #include <scsi/scsi_tcq.h> #include <scsi/scsi_cmnd.h> +#include <scsi/scsi_eh.h> #include "3w-xxxx.h" /* Globals */ -#define TW_DRIVER_VERSION "1.26.02.001" +#define TW_DRIVER_VERSION "1.26.02.003" +static DEFINE_MUTEX(tw_mutex); static TW_Device_Extension *tw_device_extension_list[TW_MAX_SLOT]; static int tw_device_extension_count = 0; static int twe_major = -1; /* Module parameters */ -MODULE_AUTHOR("AMCC"); +MODULE_AUTHOR("LSI"); MODULE_DESCRIPTION("3ware Storage Controller Linux Driver"); MODULE_LICENSE("GPL"); MODULE_VERSION(TW_DRIVER_VERSION); /* Function prototypes */ -static int tw_reset_device_extension(TW_Device_Extension *tw_dev, int ioctl_reset); +static int tw_reset_device_extension(TW_Device_Extension *tw_dev); /* Functions */ @@ -404,7 +412,7 @@ static int tw_decode_sense(TW_Device_Extension *tw_dev, int request_id, int fill /* Attempt to return intelligent sense information */ if (fill_sense) { if ((command->status == 0xc7) || (command->status == 0xcb)) { - for (i=0;i<(sizeof(tw_sense_table)/sizeof(tw_sense_table[0]));i++) { + for (i = 0; i < ARRAY_SIZE(tw_sense_table); i++) { if (command->flags == tw_sense_table[i][0]) { /* Valid bit and 'current errors' */ @@ -480,9 +488,10 @@ static void tw_state_request_start(TW_Device_Extension *tw_dev, int *request_id) } /* End tw_state_request_start() */ /* Show some statistics about the card */ -static ssize_t tw_show_stats(struct class_device *class_dev, char *buf) +static ssize_t tw_show_stats(struct device *dev, struct device_attribute *attr, + char *buf) { - struct Scsi_Host *host = class_to_shost(class_dev); + struct Scsi_Host *host = class_to_shost(dev); TW_Device_Extension *tw_dev = (TW_Device_Extension *)host->hostdata; unsigned long flags = 0; ssize_t len; @@ -515,8 +524,12 @@ static ssize_t tw_show_stats(struct class_device *class_dev, char *buf) } /* End tw_show_stats() */ /* This function will set a devices queue depth */ -static int tw_change_queue_depth(struct scsi_device *sdev, int queue_depth) +static int tw_change_queue_depth(struct scsi_device *sdev, int queue_depth, + int reason) { + if (reason != SCSI_QDEPTH_DEFAULT) + return -EOPNOTSUPP; + if (queue_depth > TW_Q_LENGTH-2) queue_depth = TW_Q_LENGTH-2; scsi_adjust_queue_depth(sdev, MSG_ORDERED_TAG, queue_depth); @@ -524,7 +537,7 @@ static int tw_change_queue_depth(struct scsi_device *sdev, int queue_depth) } /* End tw_change_queue_depth() */ /* Create sysfs 'stats' entry */ -static struct class_device_attribute tw_host_stats_attr = { +static struct device_attribute tw_host_stats_attr = { .attr = { .name = "stats", .mode = S_IRUGO, @@ -533,7 +546,7 @@ static struct class_device_attribute tw_host_stats_attr = { }; /* Host attributes initializer */ -static struct class_device_attribute *tw_host_attrs[] = { +static struct device_attribute *tw_host_attrs[] = { &tw_host_stats_attr, NULL, }; @@ -624,7 +637,7 @@ static int tw_aen_complete(TW_Device_Extension *tw_dev, int request_id) if (aen == 0x0ff) { printk(KERN_WARNING "3w-xxxx: scsi%d: AEN: INFO: AEN queue overflow.\n", tw_dev->host->host_no); } else { - table_max = sizeof(tw_aen_string)/sizeof(char *); + table_max = ARRAY_SIZE(tw_aen_string); if ((aen & 0x0ff) < table_max) { if ((tw_aen_string[aen & 0xff][strlen(tw_aen_string[aen & 0xff])-1]) == '#') { printk(KERN_WARNING "3w-xxxx: scsi%d: AEN: %s%d.\n", tw_dev->host->host_no, tw_aen_string[aen & 0xff], aen >> 8); @@ -785,7 +798,7 @@ static int tw_aen_drain_queue(TW_Device_Extension *tw_dev) if (aen == 0x0ff) { printk(KERN_WARNING "3w-xxxx: AEN: INFO: AEN queue overflow.\n"); } else { - table_max = sizeof(tw_aen_string)/sizeof(char *); + table_max = ARRAY_SIZE(tw_aen_string); if ((aen & 0x0ff) < table_max) { if ((tw_aen_string[aen & 0xff][strlen(tw_aen_string[aen & 0xff])-1]) == '#') { printk(KERN_WARNING "3w-xxxx: AEN: %s%d.\n", tw_aen_string[aen & 0xff], aen >> 8); @@ -869,7 +882,7 @@ static int tw_allocate_memory(TW_Device_Extension *tw_dev, int size, int which) } /* End tw_allocate_memory() */ /* This function handles ioctl for the character device */ -static int tw_chrdev_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) +static long tw_chrdev_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { int request_id; dma_addr_t dma_handle; @@ -877,6 +890,7 @@ static int tw_chrdev_ioctl(struct inode *inode, struct file *file, unsigned int unsigned long flags; unsigned int data_buffer_length = 0; unsigned long data_buffer_length_adjusted = 0; + struct inode *inode = file_inode(file); unsigned long *cpu_addr; long timeout; TW_New_Ioctl *tw_ioctl; @@ -887,9 +901,12 @@ static int tw_chrdev_ioctl(struct inode *inode, struct file *file, unsigned int dprintk(KERN_WARNING "3w-xxxx: tw_chrdev_ioctl()\n"); + mutex_lock(&tw_mutex); /* Only let one of these through at a time */ - if (down_interruptible(&tw_dev->ioctl_sem)) + if (mutex_lock_interruptible(&tw_dev->ioctl_lock)) { + mutex_unlock(&tw_mutex); return -EINTR; + } /* First copy down the buffer length */ if (copy_from_user(&data_buffer_length, argp, sizeof(unsigned int))) @@ -983,24 +1000,12 @@ static int tw_chrdev_ioctl(struct inode *inode, struct file *file, unsigned int /* Now wait for the command to complete */ timeout = wait_event_timeout(tw_dev->ioctl_wqueue, tw_dev->chrdev_request_id == TW_IOCTL_CHRDEV_FREE, timeout); - /* See if we reset while waiting for the ioctl to complete */ - if (test_bit(TW_IN_RESET, &tw_dev->flags)) { - clear_bit(TW_IN_RESET, &tw_dev->flags); - retval = -ERESTARTSYS; - goto out2; - } - /* We timed out, and didn't get an interrupt */ if (tw_dev->chrdev_request_id != TW_IOCTL_CHRDEV_FREE) { /* Now we need to reset the board */ printk(KERN_WARNING "3w-xxxx: scsi%d: Character ioctl (0x%x) timed out, resetting card.\n", tw_dev->host->host_no, cmd); retval = -EIO; - spin_lock_irqsave(tw_dev->host->host_lock, flags); - tw_dev->state[request_id] = TW_S_COMPLETED; - tw_state_request_finish(tw_dev, request_id); - tw_dev->posted_request_count--; - spin_unlock_irqrestore(tw_dev->host->host_lock, flags); - if (tw_reset_device_extension(tw_dev, 1)) { + if (tw_reset_device_extension(tw_dev)) { printk(KERN_WARNING "3w-xxxx: tw_chrdev_ioctl(): Reset failed for card %d.\n", tw_dev->host->host_no); } goto out2; @@ -1029,11 +1034,13 @@ out2: /* Now free ioctl buf memory */ dma_free_coherent(&tw_dev->tw_pci_dev->dev, data_buffer_length_adjusted+sizeof(TW_New_Ioctl) - 1, cpu_addr, dma_handle); out: - up(&tw_dev->ioctl_sem); + mutex_unlock(&tw_dev->ioctl_lock); + mutex_unlock(&tw_mutex); return retval; } /* End tw_chrdev_ioctl() */ /* This function handles open for the character device */ +/* NOTE that this function races with remove. */ static int tw_chrdev_open(struct inode *inode, struct file *file) { unsigned int minor_number; @@ -1048,11 +1055,12 @@ static int tw_chrdev_open(struct inode *inode, struct file *file) } /* End tw_chrdev_open() */ /* File operations struct for character device */ -static struct file_operations tw_fops = { +static const struct file_operations tw_fops = { .owner = THIS_MODULE, - .ioctl = tw_chrdev_ioctl, + .unlocked_ioctl = tw_chrdev_ioctl, .open = tw_chrdev_open, - .release = NULL + .release = NULL, + .llseek = noop_llseek, }; /* This function will free up device extension resources */ @@ -1270,7 +1278,7 @@ static int tw_initialize_device_extension(TW_Device_Extension *tw_dev) tw_dev->pending_tail = TW_Q_START; tw_dev->chrdev_request_id = TW_IOCTL_CHRDEV_FREE; - init_MUTEX(&tw_dev->ioctl_sem); + mutex_init(&tw_dev->ioctl_lock); init_waitqueue_head(&tw_dev->ioctl_wqueue); return 0; @@ -1281,61 +1289,29 @@ static int tw_map_scsi_sg_data(struct pci_dev *pdev, struct scsi_cmnd *cmd) int use_sg; dprintk(KERN_WARNING "3w-xxxx: tw_map_scsi_sg_data()\n"); - - if (cmd->use_sg == 0) - return 0; - use_sg = pci_map_sg(pdev, cmd->buffer, cmd->use_sg, DMA_BIDIRECTIONAL); - - if (use_sg == 0) { + use_sg = scsi_dma_map(cmd); + if (use_sg < 0) { printk(KERN_WARNING "3w-xxxx: tw_map_scsi_sg_data(): pci_map_sg() failed.\n"); return 0; } cmd->SCp.phase = TW_PHASE_SGLIST; cmd->SCp.have_data_in = use_sg; - + return use_sg; } /* End tw_map_scsi_sg_data() */ -static u32 tw_map_scsi_single_data(struct pci_dev *pdev, struct scsi_cmnd *cmd) -{ - dma_addr_t mapping; - - dprintk(KERN_WARNING "3w-xxxx: tw_map_scsi_single_data()\n"); - - if (cmd->request_bufflen == 0) - return 0; - - mapping = pci_map_page(pdev, virt_to_page(cmd->request_buffer), offset_in_page(cmd->request_buffer), cmd->request_bufflen, DMA_BIDIRECTIONAL); - - if (mapping == 0) { - printk(KERN_WARNING "3w-xxxx: tw_map_scsi_single_data(): pci_map_page() failed.\n"); - return 0; - } - - cmd->SCp.phase = TW_PHASE_SINGLE; - cmd->SCp.have_data_in = mapping; - - return mapping; -} /* End tw_map_scsi_single_data() */ - static void tw_unmap_scsi_data(struct pci_dev *pdev, struct scsi_cmnd *cmd) { dprintk(KERN_WARNING "3w-xxxx: tw_unmap_scsi_data()\n"); - switch(cmd->SCp.phase) { - case TW_PHASE_SINGLE: - pci_unmap_page(pdev, cmd->SCp.have_data_in, cmd->request_bufflen, DMA_BIDIRECTIONAL); - break; - case TW_PHASE_SGLIST: - pci_unmap_sg(pdev, cmd->request_buffer, cmd->use_sg, DMA_BIDIRECTIONAL); - break; - } + if (cmd->SCp.phase == TW_PHASE_SGLIST) + scsi_dma_unmap(cmd); } /* End tw_unmap_scsi_data() */ /* This function will reset a device extension */ -static int tw_reset_device_extension(TW_Device_Extension *tw_dev, int ioctl_reset) +static int tw_reset_device_extension(TW_Device_Extension *tw_dev) { int i = 0; struct scsi_cmnd *srb; @@ -1381,15 +1357,10 @@ static int tw_reset_device_extension(TW_Device_Extension *tw_dev, int ioctl_rese printk(KERN_WARNING "3w-xxxx: scsi%d: Reset sequence failed.\n", tw_dev->host->host_no); return 1; } - TW_ENABLE_AND_CLEAR_INTERRUPTS(tw_dev); - /* Wake up any ioctl that was pending before the reset */ - if ((tw_dev->chrdev_request_id == TW_IOCTL_CHRDEV_FREE) || (ioctl_reset)) { - clear_bit(TW_IN_RESET, &tw_dev->flags); - } else { - tw_dev->chrdev_request_id = TW_IOCTL_CHRDEV_FREE; - wake_up(&tw_dev->ioctl_wqueue); - } + TW_ENABLE_AND_CLEAR_INTERRUPTS(tw_dev); + clear_bit(TW_IN_RESET, &tw_dev->flags); + tw_dev->chrdev_request_id = TW_IOCTL_CHRDEV_FREE; return 0; } /* End tw_reset_device_extension() */ @@ -1432,16 +1403,22 @@ static int tw_scsi_eh_reset(struct scsi_cmnd *SCpnt) tw_dev->num_resets++; - printk(KERN_WARNING "3w-xxxx: scsi%d: WARNING: Unit #%d: Command (0x%x) timed out, resetting card.\n", tw_dev->host->host_no, SCpnt->device->id, SCpnt->cmnd[0]); + sdev_printk(KERN_WARNING, SCpnt->device, + "WARNING: Command (0x%x) timed out, resetting card.\n", + SCpnt->cmnd[0]); + + /* Make sure we are not issuing an ioctl or resetting from ioctl */ + mutex_lock(&tw_dev->ioctl_lock); /* Now reset the card and some of the device extension data */ - if (tw_reset_device_extension(tw_dev, 0)) { + if (tw_reset_device_extension(tw_dev)) { printk(KERN_WARNING "3w-xxxx: scsi%d: Reset failed.\n", tw_dev->host->host_no); goto out; } retval = SUCCESS; out: + mutex_unlock(&tw_dev->ioctl_lock); return retval; } /* End tw_scsi_eh_reset() */ @@ -1499,22 +1476,22 @@ static int tw_scsiop_inquiry(TW_Device_Extension *tw_dev, int request_id) return 0; } /* End tw_scsiop_inquiry() */ +static void tw_transfer_internal(TW_Device_Extension *tw_dev, int request_id, + void *data, unsigned int len) +{ + scsi_sg_copy_from_buffer(tw_dev->srb[request_id], data, len); +} + /* This function is called by the isr to complete an inquiry command */ static int tw_scsiop_inquiry_complete(TW_Device_Extension *tw_dev, int request_id) { unsigned char *is_unit_present; - unsigned char *request_buffer; + unsigned char request_buffer[36]; TW_Param *param; dprintk(KERN_NOTICE "3w-xxxx: tw_scsiop_inquiry_complete()\n"); - /* Fill request buffer */ - if (tw_dev->srb[request_id]->request_buffer == NULL) { - printk(KERN_WARNING "3w-xxxx: tw_scsiop_inquiry_complete(): Request buffer NULL.\n"); - return 1; - } - request_buffer = tw_dev->srb[request_id]->request_buffer; - memset(request_buffer, 0, tw_dev->srb[request_id]->request_bufflen); + memset(request_buffer, 0, sizeof(request_buffer)); request_buffer[0] = TYPE_DISK; /* Peripheral device type */ request_buffer[1] = 0; /* Device type modifier */ request_buffer[2] = 0; /* No ansi/iso compliance */ @@ -1522,6 +1499,8 @@ static int tw_scsiop_inquiry_complete(TW_Device_Extension *tw_dev, int request_i memcpy(&request_buffer[8], "3ware ", 8); /* Vendor ID */ sprintf(&request_buffer[16], "Logical Disk %-2d ", tw_dev->srb[request_id]->device->id); memcpy(&request_buffer[32], TW_DRIVER_VERSION, 3); + tw_transfer_internal(tw_dev, request_id, request_buffer, + sizeof(request_buffer)); param = (TW_Param *)tw_dev->alignment_virtual_address[request_id]; if (param == NULL) { @@ -1612,7 +1591,7 @@ static int tw_scsiop_mode_sense_complete(TW_Device_Extension *tw_dev, int reques { TW_Param *param; unsigned char *flags; - unsigned char *request_buffer; + unsigned char request_buffer[8]; dprintk(KERN_NOTICE "3w-xxxx: tw_scsiop_mode_sense_complete()\n"); @@ -1622,8 +1601,7 @@ static int tw_scsiop_mode_sense_complete(TW_Device_Extension *tw_dev, int reques return 1; } flags = (char *)&(param->data[0]); - request_buffer = tw_dev->srb[request_id]->buffer; - memset(request_buffer, 0, tw_dev->srb[request_id]->request_bufflen); + memset(request_buffer, 0, sizeof(request_buffer)); request_buffer[0] = 0xf; /* mode data length */ request_buffer[1] = 0; /* default medium type */ @@ -1632,9 +1610,11 @@ static int tw_scsiop_mode_sense_complete(TW_Device_Extension *tw_dev, int reques request_buffer[4] = 0x8; /* caching page */ request_buffer[5] = 0xa; /* page length */ if (*flags & 0x1) - request_buffer[6] = 0x4; /* WCE on */ + request_buffer[6] = 0x5; /* WCE on, RCD on */ else - request_buffer[6] = 0x0; /* WCE off */ + request_buffer[6] = 0x1; /* WCE off, RCD on */ + tw_transfer_internal(tw_dev, request_id, request_buffer, + sizeof(request_buffer)); return 0; } /* End tw_scsiop_mode_sense_complete() */ @@ -1701,17 +1681,12 @@ static int tw_scsiop_read_capacity_complete(TW_Device_Extension *tw_dev, int req { unsigned char *param_data; u32 capacity; - char *buff; + char buff[8]; TW_Param *param; dprintk(KERN_NOTICE "3w-xxxx: tw_scsiop_read_capacity_complete()\n"); - buff = tw_dev->srb[request_id]->request_buffer; - if (buff == NULL) { - printk(KERN_WARNING "3w-xxxx: tw_scsiop_read_capacity_complete(): Request buffer NULL.\n"); - return 1; - } - memset(buff, 0, tw_dev->srb[request_id]->request_bufflen); + memset(buff, 0, sizeof(buff)); param = (TW_Param *)tw_dev->alignment_virtual_address[request_id]; if (param == NULL) { printk(KERN_WARNING "3w-xxxx: tw_scsiop_read_capacity_complete(): Bad alignment virtual address.\n"); @@ -1739,6 +1714,8 @@ static int tw_scsiop_read_capacity_complete(TW_Device_Extension *tw_dev, int req buff[6] = (TW_BLOCK_SIZE >> 8) & 0xff; buff[7] = TW_BLOCK_SIZE & 0xff; + tw_transfer_internal(tw_dev, request_id, buff, sizeof(buff)); + return 0; } /* End tw_scsiop_read_capacity_complete() */ @@ -1747,19 +1724,20 @@ static int tw_scsiop_read_write(TW_Device_Extension *tw_dev, int request_id) { TW_Command *command_packet; unsigned long command_que_value; - u32 lba = 0x0, num_sectors = 0x0, buffaddr = 0x0; + u32 lba = 0x0, num_sectors = 0x0; int i, use_sg; struct scsi_cmnd *srb; - struct scatterlist *sglist; + struct scatterlist *sglist, *sg; dprintk(KERN_NOTICE "3w-xxxx: tw_scsiop_read_write()\n"); - if (tw_dev->srb[request_id]->request_buffer == NULL) { + srb = tw_dev->srb[request_id]; + + sglist = scsi_sglist(srb); + if (!sglist) { printk(KERN_WARNING "3w-xxxx: tw_scsiop_read_write(): Request buffer NULL.\n"); return 1; } - sglist = (struct scatterlist *)tw_dev->srb[request_id]->request_buffer; - srb = tw_dev->srb[request_id]; /* Initialize command packet */ command_packet = (TW_Command *)tw_dev->command_packet_virtual_address[request_id]; @@ -1802,33 +1780,18 @@ static int tw_scsiop_read_write(TW_Device_Extension *tw_dev, int request_id) command_packet->byte8.io.lba = lba; command_packet->byte6.block_count = num_sectors; - /* Do this if there are no sg list entries */ - if (tw_dev->srb[request_id]->use_sg == 0) { - dprintk(KERN_NOTICE "3w-xxxx: tw_scsiop_read_write(): SG = 0\n"); - buffaddr = tw_map_scsi_single_data(tw_dev->tw_pci_dev, tw_dev->srb[request_id]); - if (buffaddr == 0) - return 1; + use_sg = tw_map_scsi_sg_data(tw_dev->tw_pci_dev, tw_dev->srb[request_id]); + if (!use_sg) + return 1; - command_packet->byte8.io.sgl[0].address = buffaddr; - command_packet->byte8.io.sgl[0].length = tw_dev->srb[request_id]->request_bufflen; + scsi_for_each_sg(tw_dev->srb[request_id], sg, use_sg, i) { + command_packet->byte8.io.sgl[i].address = sg_dma_address(sg); + command_packet->byte8.io.sgl[i].length = sg_dma_len(sg); command_packet->size+=2; } - /* Do this if we have multiple sg list entries */ - if (tw_dev->srb[request_id]->use_sg > 0) { - use_sg = tw_map_scsi_sg_data(tw_dev->tw_pci_dev, tw_dev->srb[request_id]); - if (use_sg == 0) - return 1; - - for (i=0;i<use_sg; i++) { - command_packet->byte8.io.sgl[i].address = sg_dma_address(&sglist[i]); - command_packet->byte8.io.sgl[i].length = sg_dma_len(&sglist[i]); - command_packet->size+=2; - } - } - /* Update SG statistics */ - tw_dev->sgl_entries = tw_dev->srb[request_id]->use_sg; + tw_dev->sgl_entries = scsi_sg_count(tw_dev->srb[request_id]); if (tw_dev->sgl_entries > tw_dev->max_sgl_entries) tw_dev->max_sgl_entries = tw_dev->sgl_entries; @@ -1847,10 +1810,17 @@ static int tw_scsiop_read_write(TW_Device_Extension *tw_dev, int request_id) /* This function will handle the request sense scsi command */ static int tw_scsiop_request_sense(TW_Device_Extension *tw_dev, int request_id) { + char request_buffer[18]; + dprintk(KERN_NOTICE "3w-xxxx: tw_scsiop_request_sense()\n"); - /* For now we just zero the request buffer */ - memset(tw_dev->srb[request_id]->request_buffer, 0, tw_dev->srb[request_id]->request_bufflen); + memset(request_buffer, 0, sizeof(request_buffer)); + request_buffer[0] = 0x70; /* Immediate fixed format */ + request_buffer[7] = 10; /* minimum size per SPC: 18 bytes */ + /* leave all other fields zero, giving effectively NO_SENSE return */ + tw_transfer_internal(tw_dev, request_id, request_buffer, + sizeof(request_buffer)); + tw_dev->state[request_id] = TW_S_COMPLETED; tw_state_request_finish(tw_dev, request_id); @@ -1978,13 +1948,17 @@ static int tw_scsiop_test_unit_ready_complete(TW_Device_Extension *tw_dev, int r } /* End tw_scsiop_test_unit_ready_complete() */ /* This is the main scsi queue function to handle scsi opcodes */ -static int tw_scsi_queue(struct scsi_cmnd *SCpnt, void (*done)(struct scsi_cmnd *)) +static int tw_scsi_queue_lck(struct scsi_cmnd *SCpnt, void (*done)(struct scsi_cmnd *)) { unsigned char *command = SCpnt->cmnd; int request_id = 0; int retval = 1; TW_Device_Extension *tw_dev = (TW_Device_Extension *)SCpnt->device->host->hostdata; + /* If we are resetting due to timed out ioctl, report as busy */ + if (test_bit(TW_IN_RESET, &tw_dev->flags)) + return SCSI_MLQUEUE_HOST_BUSY; + /* Save done function into Scsi_Cmnd struct */ SCpnt->scsi_done = done; @@ -2036,7 +2010,8 @@ static int tw_scsi_queue(struct scsi_cmnd *SCpnt, void (*done)(struct scsi_cmnd printk(KERN_NOTICE "3w-xxxx: scsi%d: Unknown scsi opcode: 0x%x\n", tw_dev->host->host_no, *command); tw_dev->state[request_id] = TW_S_COMPLETED; tw_state_request_finish(tw_dev, request_id); - SCpnt->result = (DID_BAD_TARGET << 16); + SCpnt->result = (DRIVER_SENSE << 24) | SAM_STAT_CHECK_CONDITION; + scsi_build_sense_buffer(1, SCpnt->sense_buffer, ILLEGAL_REQUEST, 0x20, 0); done(SCpnt); retval = 0; } @@ -2050,9 +2025,10 @@ static int tw_scsi_queue(struct scsi_cmnd *SCpnt, void (*done)(struct scsi_cmnd return retval; } /* End tw_scsi_queue() */ +static DEF_SCSI_QCMD(tw_scsi_queue) + /* This function is the interrupt service routine */ -static irqreturn_t tw_interrupt(int irq, void *dev_instance, - struct pt_regs *regs) +static irqreturn_t tw_interrupt(int irq, void *dev_instance) { int request_id; u32 status_reg_value; @@ -2074,6 +2050,10 @@ static irqreturn_t tw_interrupt(int irq, void *dev_instance, handled = 1; + /* If we are resetting, bail */ + if (test_bit(TW_IN_RESET, &tw_dev->flags)) + goto tw_interrupt_bail; + /* Check controller for errors */ if (tw_check_bits(status_reg_value)) { dprintk(KERN_WARNING "3w-xxxx: tw_interrupt(): Unexpected bits.\n"); @@ -2250,6 +2230,9 @@ static void __tw_shutdown(TW_Device_Extension *tw_dev) /* Disable interrupts */ TW_DISABLE_INTERRUPTS(tw_dev); + /* Free up the IRQ */ + free_irq(tw_dev->tw_pci_dev->irq, tw_dev); + printk(KERN_WARNING "3w-xxxx: Shutting down host %d.\n", tw_dev->host->host_no); /* Tell the card we are shutting down */ @@ -2272,6 +2255,15 @@ static void tw_shutdown(struct pci_dev *pdev) __tw_shutdown(tw_dev); } /* End tw_shutdown() */ +/* This function gets called when a disk is coming online */ +static int tw_slave_configure(struct scsi_device *sdev) +{ + /* Force 60 second timeout */ + blk_queue_rq_timeout(sdev->request_queue, 60 * HZ); + + return 0; +} /* End tw_slave_configure() */ + static struct scsi_host_template driver_template = { .module = THIS_MODULE, .name = "3ware Storage Controller", @@ -2280,17 +2272,19 @@ static struct scsi_host_template driver_template = { .bios_param = tw_scsi_biosparam, .change_queue_depth = tw_change_queue_depth, .can_queue = TW_Q_LENGTH-2, + .slave_configure = tw_slave_configure, .this_id = -1, .sg_tablesize = TW_MAX_SGL_LENGTH, .max_sectors = TW_MAX_SECTORS, .cmd_per_lun = TW_MAX_CMDS_PER_LUN, .use_clustering = ENABLE_CLUSTERING, .shost_attrs = tw_host_attrs, - .emulated = 1 + .emulated = 1, + .no_write_same = 1, }; /* This function will probe and initialize a card */ -static int __devinit tw_probe(struct pci_dev *pdev, const struct pci_device_id *dev_id) +static int tw_probe(struct pci_dev *pdev, const struct pci_device_id *dev_id) { struct Scsi_Host *host = NULL; TW_Device_Extension *tw_dev; @@ -2318,8 +2312,6 @@ static int __devinit tw_probe(struct pci_dev *pdev, const struct pci_device_id * } tw_dev = (TW_Device_Extension *)host->hostdata; - memset(tw_dev, 0, sizeof(TW_Device_Extension)); - /* Save values to device extension */ tw_dev->host = host; tw_dev->tw_pci_dev = pdev; @@ -2370,7 +2362,7 @@ static int __devinit tw_probe(struct pci_dev *pdev, const struct pci_device_id * printk(KERN_WARNING "3w-xxxx: scsi%d: Found a 3ware Storage Controller at 0x%x, IRQ: %d.\n", host->host_no, tw_dev->base_addr, pdev->irq); /* Now setup the interrupt handler */ - retval = request_irq(pdev->irq, tw_interrupt, SA_SHIRQ, "3w-xxxx", tw_dev); + retval = request_irq(pdev->irq, tw_interrupt, IRQF_SHARED, "3w-xxxx", tw_dev); if (retval) { printk(KERN_WARNING "3w-xxxx: Error requesting IRQ."); goto out_remove_host; @@ -2418,9 +2410,6 @@ static void tw_remove(struct pci_dev *pdev) twe_major = -1; } - /* Free up the IRQ */ - free_irq(tw_dev->tw_pci_dev->irq, tw_dev); - /* Shutdown the card */ __tw_shutdown(tw_dev); @@ -2436,7 +2425,7 @@ static void tw_remove(struct pci_dev *pdev) } /* End tw_remove() */ /* PCI Devices supported by this driver */ -static struct pci_device_id tw_pci_tbl[] __devinitdata = { +static struct pci_device_id tw_pci_tbl[] = { { PCI_VENDOR_ID_3WARE, PCI_DEVICE_ID_3WARE_1000, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, { PCI_VENDOR_ID_3WARE, PCI_DEVICE_ID_3WARE_7000, @@ -2459,7 +2448,7 @@ static int __init tw_init(void) { printk(KERN_WARNING "3ware Storage Controller device driver for Linux v%s.\n", TW_DRIVER_VERSION); - return pci_module_init(&tw_driver); + return pci_register_driver(&tw_driver); } /* End tw_init() */ /* This function is called on driver exit */ |
