diff -Naur kernel-2.4.20-old/linux-2.4.20/drivers/scsi/3w-xxxx.c kernel-2.4.20/linux-2.4.20/drivers/scsi/3w-xxxx.c --- kernel-2.4.20-old/linux-2.4.20/drivers/scsi/3w-xxxx.c Tue Dec 21 17:35:30 2004 +++ kernel-2.4.20/linux-2.4.20/drivers/scsi/3w-xxxx.c Wed Dec 22 11:46:14 2004 @@ -165,12 +165,22 @@ Add support for "twe" character device for ioctls. Clean up request_id queueing code. Fix tw_scsi_queue() spinlocks. + 1.02.00.033 - Fix tw_aen_complete() to not queue 'queue empty' AEN's. + Initialize queues correctly when loading with no valid units. + 1.02.00.034 - Fix tw_decode_bits() to handle multiple errors. + Add support for user configurable cmd_per_lun. + Add support for sht->select_queue_depths. + 1.02.00.035 - Improve tw_allocate_memory() memory allocation. + Fix tw_chrdev_ioctl() to sleep correctly. + 1.02.00.036 - Increase character ioctl timeout to 60 seconds. + 1.02.00.037 - Fix tw_ioctl() to handle all non-data ATA passthru cmds + for 'smartmontools' support. */ #include MODULE_AUTHOR ("3ware Inc."); -#ifdef __SMP__ +#ifdef CONFIG_SMP MODULE_DESCRIPTION ("3ware Storage Controller Linux Driver (SMP)"); #else MODULE_DESCRIPTION ("3ware Storage Controller Linux Driver"); @@ -232,7 +242,7 @@ }; /* Globals */ -char *tw_driver_version="1.02.00.032"; +char *tw_driver_version="1.02.00.037"; TW_Device_Extension *tw_device_extension_list[TW_MAX_SLOT]; int tw_device_extension_count = 0; static int twe_major = -1; @@ -271,25 +281,24 @@ printk(KERN_WARNING "3w-xxxx: scsi%d: Received AEN %d.\n", tw_dev->host->host_no, aen); } } - if (aen != 0x0) + if (aen != TW_AEN_QUEUE_EMPTY) { tw_dev->aen_count++; - /* Now queue the code */ - tw_dev->aen_queue[tw_dev->aen_tail] = aen; - if (tw_dev->aen_tail == TW_Q_LENGTH - 1) { - tw_dev->aen_tail = TW_Q_START; - } else { - tw_dev->aen_tail = tw_dev->aen_tail + 1; - } - if (tw_dev->aen_head == tw_dev->aen_tail) { - if (tw_dev->aen_head == TW_Q_LENGTH - 1) { - tw_dev->aen_head = TW_Q_START; + /* Now queue the code */ + tw_dev->aen_queue[tw_dev->aen_tail] = aen; + if (tw_dev->aen_tail == TW_Q_LENGTH - 1) { + tw_dev->aen_tail = TW_Q_START; } else { - tw_dev->aen_head = tw_dev->aen_head + 1; + tw_dev->aen_tail = tw_dev->aen_tail + 1; + } + if (tw_dev->aen_head == tw_dev->aen_tail) { + if (tw_dev->aen_head == TW_Q_LENGTH - 1) { + tw_dev->aen_head = TW_Q_START; + } else { + tw_dev->aen_head = tw_dev->aen_head + 1; + } } - } - if (aen != TW_AEN_QUEUE_EMPTY) { error = tw_aen_read_queue(tw_dev, request_id); if (error) { printk(KERN_WARNING "3w-xxxx: scsi%d: Error completing AEN.\n", tw_dev->host->host_no); @@ -556,34 +565,33 @@ else imax = TW_Q_LENGTH; - for (i=0;itw_pci_dev, size, &dma_handle); - if (cpu_addr == NULL) { - printk(KERN_WARNING "3w-xxxx: pci_alloc_consistent() failed.\n"); - return 1; - } + cpu_addr = pci_alloc_consistent(tw_dev->tw_pci_dev, size*imax, &dma_handle); + if (cpu_addr == NULL) { + printk(KERN_WARNING "3w-xxxx: pci_alloc_consistent() failed.\n"); + return 1; + } - if ((unsigned long)cpu_addr % (tw_dev->tw_pci_dev->device == TW_DEVICE_ID ? TW_ALIGNMENT_6000 : TW_ALIGNMENT_7000)) { - printk(KERN_WARNING "3w-xxxx: Couldn't allocate correctly aligned memory.\n"); - pci_free_consistent(tw_dev->tw_pci_dev, size, cpu_addr, dma_handle); - return 1; - } + if ((unsigned long)cpu_addr % (tw_dev->tw_pci_dev->device == TW_DEVICE_ID ? TW_ALIGNMENT_6000 : TW_ALIGNMENT_7000)) { + printk(KERN_WARNING "3w-xxxx: Couldn't allocate correctly aligned memory.\n"); + pci_free_consistent(tw_dev->tw_pci_dev, size*imax, cpu_addr, dma_handle); + return 1; + } + memset(cpu_addr, 0, size*imax); + + for (i=0;icommand_packet_virtual_address[i] = cpu_addr; - tw_dev->command_packet_physical_address[i] = dma_handle; - memset(tw_dev->command_packet_virtual_address[i], 0, size); + tw_dev->command_packet_physical_address[i] = dma_handle+(i*size); + tw_dev->command_packet_virtual_address[i] = (unsigned long *)((unsigned char *)cpu_addr + (i*size)); break; case 1: - tw_dev->alignment_virtual_address[i] = cpu_addr; - tw_dev->alignment_physical_address[i] = dma_handle; - memset(tw_dev->alignment_virtual_address[i], 0, size); + tw_dev->alignment_physical_address[i] = dma_handle+(i*size); + tw_dev->alignment_virtual_address[i] = (unsigned long *)((unsigned char *)cpu_addr + (i*size)); break; case 2: - tw_dev->bounce_buffer[i] = cpu_addr; - tw_dev->bounce_buffer_phys[i] = dma_handle; - memset(tw_dev->bounce_buffer[i], 0, size); + tw_dev->bounce_buffer_phys[i] = dma_handle+(i*size); + tw_dev->bounce_buffer[i] = (unsigned long *)((unsigned char *)cpu_addr + (i*size)); break; default: printk(KERN_WARNING "3w-xxxx: tw_allocate_memory(): case slip in tw_allocate_memory()\n"); @@ -631,11 +639,11 @@ int error, request_id; dma_addr_t dma_handle; unsigned short tw_aen_code; - unsigned long before; unsigned long flags; unsigned int data_buffer_length = 0; unsigned long data_buffer_length_adjusted = 0; unsigned long *cpu_addr; + long timeout; TW_New_Ioctl *tw_ioctl; TW_Passthru *passthru; TW_Device_Extension *tw_dev = tw_device_extension_list[MINOR(inode->i_rdev)]; @@ -735,30 +743,30 @@ tw_post_command_packet(tw_dev, request_id); spin_unlock_irqrestore(&tw_dev->tw_lock, flags); + timeout = TW_IOCTL_CHRDEV_TIMEOUT*HZ; + /* Now wait for the command to complete */ - before = jiffies; + tw_wait_event_interruptible_timeout(tw_dev->ioctl_wqueue, tw_dev->chrdev_request_id == TW_IOCTL_CHRDEV_FREE, timeout); - while (tw_dev->chrdev_request_id != TW_IOCTL_CHRDEV_FREE) { - /* FIXME: we need to sleep here */ - udelay(10); - if (time_after(jiffies, before + HZ *TW_IOCTL_CHRDEV_TIMEOUT)) { - /* Now we need to reset the board */ + /* Check if we timed out, got a signal, or didn't get + an interrupt */ + if ((timeout <= 0) && (tw_dev->chrdev_request_id != TW_IOCTL_CHRDEV_FREE)) { + /* Now we need to reset the board */ + if (timeout == -ERESTARTSYS) { + retval = timeout; + } else { printk(KERN_WARNING "3w-xxxx: scsi%d: Character ioctl (0x%x) timed out, resetting card.\n", tw_dev->host->host_no, cmd); - spin_lock_irqsave(&tw_dev->tw_lock, flags); - tw_dev->state[request_id] = TW_S_COMPLETED; - tw_state_request_finish(tw_dev, request_id); - pci_free_consistent(tw_dev->tw_pci_dev, data_buffer_length_adjusted+sizeof(TW_New_Ioctl) - 1, cpu_addr, dma_handle); - tw_dev->posted_request_count--; - 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); - } - spin_unlock_irqrestore(&tw_dev->tw_lock, flags); - if (signal_pending(current)) - retval = -EINTR; - else - retval = -EIO; - goto out2; + retval = -EIO; + } + spin_lock_irqsave(&tw_dev->tw_lock, flags); + tw_dev->state[request_id] = TW_S_COMPLETED; + tw_state_request_finish(tw_dev, request_id); + tw_dev->posted_request_count--; + 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); } + spin_unlock_irqrestore(&tw_dev->tw_lock, flags); + goto out2; } /* Now copy in the command packet response */ @@ -886,30 +894,33 @@ else host[0] = '\0'; - switch (status_reg_value & TW_STATUS_UNEXPECTED_BITS) { - case TW_STATUS_PCI_PARITY_ERROR: + if (status_reg_value & TW_STATUS_PCI_PARITY_ERROR) { printk(KERN_WARNING "3w-xxxx:%s PCI Parity Error: clearing.\n", host); outl(TW_CONTROL_CLEAR_PARITY_ERROR, tw_dev->registers.control_reg_addr); - break; - case TW_STATUS_MICROCONTROLLER_ERROR: - if (tw_dev->reset_print == 0) { - printk(KERN_WARNING "3w-xxxx:%s Microcontroller Error: clearing.\n", host); - tw_dev->reset_print = 1; - } - return 1; - case TW_STATUS_PCI_ABORT: + } + + if (status_reg_value & TW_STATUS_PCI_ABORT) { printk(KERN_WARNING "3w-xxxx:%s PCI Abort: clearing.\n", host); outl(TW_CONTROL_CLEAR_PCI_ABORT, tw_dev->registers.control_reg_addr); pci_write_config_word(tw_dev->tw_pci_dev, PCI_STATUS, TW_PCI_CLEAR_PCI_ABORT); - break; - case TW_STATUS_QUEUE_ERROR: + } + + if (status_reg_value & TW_STATUS_QUEUE_ERROR) { printk(KERN_WARNING "3w-xxxx:%s Controller Queue Error: clearing.\n", host); outl(TW_CONTROL_CLEAR_QUEUE_ERROR, tw_dev->registers.control_reg_addr); - break; - case TW_STATUS_SBUF_WRITE_ERROR: + } + + if (status_reg_value & TW_STATUS_SBUF_WRITE_ERROR) { printk(KERN_WARNING "3w-xxxx:%s SBUF Write Error: clearing.\n", host); outl(TW_CONTROL_CLEAR_SBUF_WRITE_ERROR, tw_dev->registers.control_reg_addr); - break; + } + + if (status_reg_value & TW_STATUS_MICROCONTROLLER_ERROR) { + if (tw_dev->reset_print == 0) { + printk(KERN_WARNING "3w-xxxx:%s Microcontroller Error: clearing.\n", host); + tw_dev->reset_print = 1; + } + return 1; } return 0; @@ -1143,19 +1154,24 @@ tw_dev->online = 1; /* Calculate max cmds per lun, and setup queues */ - if (tw_dev->num_units > 0) { - if ((tw_dev->num_raid_five > 0) && (tw_dev->tw_pci_dev->device == TW_DEVICE_ID)) { - tw_host->cmd_per_lun = (TW_MAX_BOUNCEBUF-2)/tw_dev->num_units; - tw_dev->free_head = TW_Q_START; - tw_dev->free_tail = TW_Q_START; - tw_dev->free_wrap = TW_MAX_BOUNCEBUF - 1; - } else { - /* Use SHT cmd_per_lun here */ + if ((tw_dev->num_raid_five > 0) && (tw_dev->tw_pci_dev->device == TW_DEVICE_ID)) { + tw_host->cmd_per_lun = (TW_MAX_BOUNCEBUF-2)/tw_dev->num_units; + tw_dev->free_head = TW_Q_START; + tw_dev->free_tail = TW_Q_START; + tw_dev->free_wrap = TW_MAX_BOUNCEBUF - 1; + } else { + /* Check for user configured cmd_per_lun */ +#ifdef CONFIG_3W_XXXX_CMD_PER_LUN + tw_host->cmd_per_lun = CONFIG_3W_XXXX_CMD_PER_LUN; + if (tw_host->cmd_per_lun > TW_MAX_CMDS_PER_LUN) tw_host->cmd_per_lun = TW_MAX_CMDS_PER_LUN; - tw_dev->free_head = TW_Q_START; - tw_dev->free_tail = TW_Q_START; - tw_dev->free_wrap = TW_Q_LENGTH - 1; - } +#else + /* Use SHT cmd_per_lun default */ + tw_host->cmd_per_lun = TW_MAX_CMDS_PER_LUN; +#endif + tw_dev->free_head = TW_Q_START; + tw_dev->free_tail = TW_Q_START; + tw_dev->free_wrap = TW_Q_LENGTH - 1; } /* Register the card with the kernel SCSI layer */ @@ -1184,6 +1200,8 @@ host->max_sectors = TW_MAX_SECTORS; #endif + host->select_queue_depths = tw_select_queue_depths; + #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,4) scsi_set_pci_device(host, tw_pci_dev); #endif @@ -1198,6 +1216,10 @@ if (host->hostdata) { tw_dev2 = (TW_Device_Extension *)host->hostdata; memcpy(tw_dev2, tw_dev, sizeof(TW_Device_Extension)); + /* Need to init the sem/wqueue after the copy */ + init_MUTEX(&tw_dev2->ioctl_sem); + init_waitqueue_head(&tw_dev2->ioctl_wqueue); + tw_device_extension_list[tw_device_extension_count] = tw_dev2; numcards++; tw_device_extension_count = numcards; @@ -1254,22 +1276,17 @@ /* This function will free up device extension resources */ void tw_free_device_extension(TW_Device_Extension *tw_dev) { - int i; - dprintk(KERN_NOTICE "3w-xxxx: tw_free_device_extension()\n"); + /* Free command packet and generic buffer memory */ - for (i=0;icommand_packet_virtual_address[i]) - pci_free_consistent(tw_dev->tw_pci_dev, sizeof(TW_Sector), tw_dev->command_packet_virtual_address[i], tw_dev->command_packet_physical_address[i]); + if (tw_dev->command_packet_physical_address[0]) + pci_free_consistent(tw_dev->tw_pci_dev, sizeof(TW_Command)*TW_Q_LENGTH, tw_dev->command_packet_virtual_address[0], tw_dev->command_packet_physical_address[0]); - if (tw_dev->alignment_virtual_address[i]) - pci_free_consistent(tw_dev->tw_pci_dev, sizeof(TW_Sector), tw_dev->alignment_virtual_address[i], tw_dev->alignment_physical_address[i]); + if (tw_dev->alignment_physical_address[0]) + pci_free_consistent(tw_dev->tw_pci_dev, sizeof(TW_Sector)*TW_Q_LENGTH, tw_dev->alignment_virtual_address[0], tw_dev->alignment_physical_address[0]); - } - for (i=0;ibounce_buffer[i]) - pci_free_consistent(tw_dev->tw_pci_dev, sizeof(TW_Sector)*TW_MAX_BOUNCE_SECTORS, tw_dev->bounce_buffer[i], tw_dev->bounce_buffer_phys[i]); - } + if (tw_dev->bounce_buffer[0]) + pci_free_consistent(tw_dev->tw_pci_dev, sizeof(TW_Sector)*TW_MAX_BOUNCE_SECTORS*TW_MAX_BOUNCEBUF, tw_dev->bounce_buffer[0], tw_dev->bounce_buffer_phys[0]); } /* End tw_free_device_extension() */ /* Clean shutdown routine */ @@ -1379,8 +1396,6 @@ tw_dev->pending_tail = TW_Q_START; spin_lock_init(&tw_dev->tw_lock); tw_dev->chrdev_request_id = TW_IOCTL_CHRDEV_FREE; - init_waitqueue_head(&tw_dev->ioctl_wqueue); - init_MUTEX(&tw_dev->ioctl_sem); return 0; } /* End tw_initialize_device_extension() */ @@ -1720,6 +1735,7 @@ } } else { tw_dev->chrdev_request_id = TW_IOCTL_CHRDEV_FREE; + wake_up(&tw_dev->ioctl_wqueue); } } else { switch (tw_dev->srb[request_id]->cmnd[0]) { @@ -1916,12 +1932,15 @@ } passthru = (TW_Passthru *)tw_dev->command_packet_virtual_address[request_id]; - passthru->sg_list[0].length = passthru->sector_count*512; - if (passthru->sg_list[0].length > TW_MAX_PASSTHRU_BYTES) { - printk(KERN_WARNING "3w-xxxx: tw_ioctl(): Passthru size (%d) too big.\n", passthru->sg_list[0].length); - return 1; + /* Don't load sg_list for non-data ATA cmds */ + if ((passthru->param != 0) && (passthru->param != 0x8)) { + passthru->sg_list[0].length = passthru->sector_count*512; + if (passthru->sg_list[0].length > TW_MAX_PASSTHRU_BYTES) { + printk(KERN_WARNING "3w-xxxx: tw_ioctl(): Passthru size (%d) too big.\n", passthru->sg_list[0].length); + return 1; + } + passthru->sg_list[0].address = tw_dev->alignment_physical_address[request_id]; } - passthru->sg_list[0].address = tw_dev->alignment_physical_address[request_id]; tw_post_command_packet(tw_dev, request_id); return 0; case TW_CMD_PACKET: @@ -2172,7 +2191,14 @@ switch (ioctl->opcode) { case TW_ATA_PASSTHRU: passthru = (TW_Passthru *)ioctl->data; - memcpy(buff, tw_dev->alignment_virtual_address[request_id], passthru->sector_count * 512); + /* Don't return data for non-data ATA cmds */ + if ((passthru->param != 0) && (passthru->param != 0x8)) + memcpy(buff, tw_dev->alignment_virtual_address[request_id], passthru->sector_count * 512); + else { + /* For non-data cmds, return cmd pkt */ + if (tw_dev->srb[request_id]->request_bufflen >= sizeof(TW_Command)) + memcpy(buff, tw_dev->command_packet_virtual_address[request_id], sizeof(TW_Command)); + } break; case TW_CMD_PACKET_WITH_DATA: dprintk(KERN_WARNING "3w-xxxx: tw_ioctl_complete(): caught TW_CMD_PACKET_WITH_DATA.\n"); @@ -3395,6 +3421,31 @@ return 0; } /* End tw_scsiop_test_unit_ready_complete() */ +/* This function will select queue depths for a target */ +void tw_select_queue_depths(struct Scsi_Host *host, Scsi_Device *dev) +{ + Scsi_Device *ptr; + TW_Device_Extension *tw_dev = (TW_Device_Extension *)host->hostdata; + + dprintk(KERN_WARNING "3w-xxxx: tw_select_queue_depths()\n"); + + for (ptr = dev; ptr != NULL; ptr = ptr->next) { + if (ptr->host == host) { + if ((tw_dev->num_raid_five > 0) && (tw_dev->tw_pci_dev->device == TW_DEVICE_ID)) { + ptr->queue_depth = (TW_MAX_BOUNCEBUF-2)/tw_dev->num_units; + } else { +#ifdef CONFIG_3W_XXXX_CMD_PER_LUN + ptr->queue_depth = CONFIG_3W_XXXX_CMD_PER_LUN; + if (ptr->queue_depth > TW_MAX_CMDS_PER_LUN) + ptr->queue_depth = TW_MAX_CMDS_PER_LUN; +#else + ptr->queue_depth = TW_MAX_CMDS_PER_LUN; +#endif + } + } + } +} /* End tw_select_queue_depths() */ + /* Set a value in the features table */ int tw_setfeature(TW_Device_Extension *tw_dev, int parm, int param_size, unsigned char *val) diff -Naur kernel-2.4.20-old/linux-2.4.20/drivers/scsi/3w-xxxx.h kernel-2.4.20/linux-2.4.20/drivers/scsi/3w-xxxx.h --- kernel-2.4.20-old/linux-2.4.20/drivers/scsi/3w-xxxx.h Tue Dec 21 17:35:30 2004 +++ kernel-2.4.20/linux-2.4.20/drivers/scsi/3w-xxxx.h Wed Dec 22 11:46:21 2004 @@ -115,10 +115,10 @@ {0xd0, 0x0b, 0x00, 0x00}, // Device busy Aborted command {0xd1, 0x0b, 0x00, 0x00}, // Device busy Aborted command {0x37, 0x02, 0x04, 0x00}, // Unit offline Not ready + {0x09, 0x02, 0x04, 0x00}, // Unrecovered disk error Not ready /* Codes for older firmware */ // 3ware Error SCSI Error - {0x09, 0x0b, 0x00, 0x00}, // Unrecovered disk error Aborted command {0x51, 0x0b, 0x00, 0x00} // Unspecified Aborted command }; @@ -238,7 +238,7 @@ #define TW_ISR_DONT_COMPLETE 2 #define TW_ISR_DONT_RESULT 3 #define TW_IOCTL_TIMEOUT 25 /* 25 seconds */ -#define TW_IOCTL_CHRDEV_TIMEOUT 25 /* 25 seconds */ +#define TW_IOCTL_CHRDEV_TIMEOUT 60 /* 60 seconds */ #define TW_IOCTL_CHRDEV_FREE -1 /* Macros */ @@ -249,6 +249,30 @@ (x & TW_STATUS_MICROCONTROLLER_ERROR)) && \ (x & TW_STATUS_MICROCONTROLLER_READY)) +/* This was taken from 2.5 kernel */ +#define tw_wait_event_interruptible_timeout(wq, condition, ret) \ +do { \ + wait_queue_t __wait; \ + init_waitqueue_entry(&__wait, current); \ + \ + add_wait_queue(&wq, &__wait); \ + for (;;) { \ + set_current_state(TASK_INTERRUPTIBLE); \ + if (condition) \ + break; \ + if (!signal_pending(current)) { \ + ret = schedule_timeout(ret); \ + if (!ret) \ + break; \ + continue; \ + } \ + ret = -ERESTARTSYS; \ + break; \ + } \ + current->state = TASK_RUNNING; \ + remove_wait_queue(&wq, &__wait); \ +} while (0) + #ifdef TW_DEBUG #define dprintk(msg...) printk(msg) #else @@ -498,6 +522,7 @@ int tw_scsiop_synchronize_cache(TW_Device_Extension *tw_dev, int request_id); int tw_scsiop_test_unit_ready(TW_Device_Extension *tw_dev, int request_id); int tw_scsiop_test_unit_ready_complete(TW_Device_Extension *tw_dev, int request_id); +void tw_select_queue_depths(struct Scsi_Host *host, Scsi_Device *dev); int tw_setfeature(TW_Device_Extension *tw_dev, int parm, int param_size, unsigned char *val); int tw_setup_irq(TW_Device_Extension *tw_dev);