Commit 05f534c9 authored by Alan Cox's avatar Alan Cox Committed by Linus Torvalds

[PATCH] clean up the mess someone merged into 3wxxx scsi

- Redo the timing stuff using jiffies properly
- Clean up the exit paths
- Make the ioctl use a semaphore
- Fix broken locking on the AEN list
parent 0cb53a3f
...@@ -622,61 +622,48 @@ static int tw_chrdev_ioctl(struct inode *inode, struct file *file, unsigned int ...@@ -622,61 +622,48 @@ static int tw_chrdev_ioctl(struct inode *inode, struct file *file, unsigned int
int error, request_id; int error, request_id;
dma_addr_t dma_handle; dma_addr_t dma_handle;
unsigned short tw_aen_code; unsigned short tw_aen_code;
struct timeval before, timeout; unsigned long before, timeout;
unsigned long flags = 0x0; unsigned long flags;
unsigned int data_buffer_length = 0; unsigned int data_buffer_length = 0;
unsigned long data_buffer_length_adjusted = 0; unsigned long data_buffer_length_adjusted = 0;
unsigned long *cpu_addr; unsigned long *cpu_addr;
TW_New_Ioctl *tw_ioctl; TW_New_Ioctl *tw_ioctl;
TW_Passthru *passthru; TW_Passthru *passthru;
TW_Device_Extension *tw_dev = tw_device_extension_list[minor(inode->i_rdev)]; TW_Device_Extension *tw_dev = tw_device_extension_list[minor(inode->i_rdev)];
int retval = -EFAULT;
dprintk(KERN_WARNING "3w-xxxx: tw_chrdev_ioctl()\n"); dprintk(KERN_WARNING "3w-xxxx: tw_chrdev_ioctl()\n");
/* Only let one of these through at a time */ /* Only let one of these through at a time */
if (test_and_set_bit(TW_IN_CHRDEV_IOCTL, &tw_dev->flags)) { down(&tw_dev->ioctl_sem);
return -EBUSY;
}
/* First copy down the buffer length */ /* First copy down the buffer length */
error = copy_from_user(&data_buffer_length, (void *)arg, sizeof(unsigned int)); error = copy_from_user(&data_buffer_length, (void *)arg, sizeof(unsigned int));
if (error) { if (error)
printk(KERN_WARNING "3w-xxxx: tw_chrdev_ioctl(): Error copying buffer length from userspace.\n"); goto out;
clear_bit(TW_IN_CHRDEV_IOCTL, &tw_dev->flags);
return -EFAULT;
}
/* Check size */ /* Check size */
if (data_buffer_length > TW_MAX_SECTORS * 512) { if (data_buffer_length > TW_MAX_SECTORS * 512) {
printk(KERN_WARNING "3w-xxxx: tw_chrdev_ioctl(): Invalid buffer size (%d).\n", data_buffer_length); retval = -EINVAL;
clear_bit(TW_IN_CHRDEV_IOCTL, &tw_dev->flags); goto out;
return -EFAULT;
} }
/* Hardware can only do multiple of 512 byte transfers */ /* Hardware can only do multiple of 512 byte transfers */
if (data_buffer_length % 512) data_buffer_length_adjusted = (data_buffer_length + 511) & ~511;
data_buffer_length_adjusted = data_buffer_length + 512 - (data_buffer_length % 512);
else
data_buffer_length_adjusted = data_buffer_length;
/* Now allocate ioctl buf memory */ /* Now allocate ioctl buf memory */
cpu_addr = pci_alloc_consistent(tw_dev->tw_pci_dev, data_buffer_length_adjusted+sizeof(TW_New_Ioctl) - 1, &dma_handle); cpu_addr = pci_alloc_consistent(tw_dev->tw_pci_dev, data_buffer_length_adjusted+sizeof(TW_New_Ioctl) - 1, &dma_handle);
if (cpu_addr == NULL) { if (cpu_addr == NULL) {
printk(KERN_WARNING "3w-xxxx: tw_chrdev_ioctl(): Error allocating memory.\n"); retval -ENOMEM;
clear_bit(TW_IN_CHRDEV_IOCTL, &tw_dev->flags); goto out;
return -ENOMEM;
} }
tw_ioctl = (TW_New_Ioctl *)cpu_addr; tw_ioctl = (TW_New_Ioctl *)cpu_addr;
/* Now copy down the entire ioctl */ /* Now copy down the entire ioctl */
error = copy_from_user(tw_ioctl, (void *)arg, data_buffer_length + sizeof(TW_New_Ioctl) - 1); error = copy_from_user(tw_ioctl, (void *)arg, data_buffer_length + sizeof(TW_New_Ioctl) - 1);
if (error) { if (error)
printk(KERN_WARNING "3w-xxxx: tw_chrdev_ioctl(): Error copying data from userspace.\n"); goto out2;
pci_free_consistent(tw_dev->tw_pci_dev, data_buffer_length_adjusted+sizeof(TW_New_Ioctl) - 1, cpu_addr, dma_handle);
clear_bit(TW_IN_CHRDEV_IOCTL, &tw_dev->flags);
return -EFAULT;
}
passthru = (TW_Passthru *)&tw_ioctl->firmware_command; passthru = (TW_Passthru *)&tw_ioctl->firmware_command;
...@@ -688,6 +675,8 @@ static int tw_chrdev_ioctl(struct inode *inode, struct file *file, unsigned int ...@@ -688,6 +675,8 @@ static int tw_chrdev_ioctl(struct inode *inode, struct file *file, unsigned int
case TW_OP_AEN_LISTEN: case TW_OP_AEN_LISTEN:
dprintk(KERN_WARNING "3w-xxxx: tw_chrdev_ioctl(): caught TW_AEN_LISTEN.\n"); dprintk(KERN_WARNING "3w-xxxx: tw_chrdev_ioctl(): caught TW_AEN_LISTEN.\n");
memset(tw_ioctl->data_buffer, 0, tw_ioctl->data_buffer_length); memset(tw_ioctl->data_buffer, 0, tw_ioctl->data_buffer_length);
spin_lock_irqsave(&tw_dev->host->host_lock, flags);
if (tw_dev->aen_head == tw_dev->aen_tail) { if (tw_dev->aen_head == tw_dev->aen_tail) {
tw_aen_code = TW_AEN_QUEUE_EMPTY; tw_aen_code = TW_AEN_QUEUE_EMPTY;
} else { } else {
...@@ -698,6 +687,7 @@ static int tw_chrdev_ioctl(struct inode *inode, struct file *file, unsigned int ...@@ -698,6 +687,7 @@ static int tw_chrdev_ioctl(struct inode *inode, struct file *file, unsigned int
tw_dev->aen_head = tw_dev->aen_head + 1; tw_dev->aen_head = tw_dev->aen_head + 1;
} }
} }
spin_unlock_irqrestore(&tw_dev->tw_lock, flags);
memcpy(tw_ioctl->data_buffer, &tw_aen_code, sizeof(tw_aen_code)); memcpy(tw_ioctl->data_buffer, &tw_aen_code, sizeof(tw_aen_code));
break; break;
case TW_CMD_PACKET_WITH_DATA: case TW_CMD_PACKET_WITH_DATA:
...@@ -737,32 +727,28 @@ static int tw_chrdev_ioctl(struct inode *inode, struct file *file, unsigned int ...@@ -737,32 +727,28 @@ static int tw_chrdev_ioctl(struct inode *inode, struct file *file, unsigned int
spin_unlock_irqrestore(&tw_dev->tw_lock, flags); spin_unlock_irqrestore(&tw_dev->tw_lock, flags);
/* Now wait for the command to complete */ /* Now wait for the command to complete */
do_gettimeofday(&before); before = jiffies;
tw_ioctl_chrdev_retry:
if (tw_dev->chrdev_request_id != TW_IOCTL_CHRDEV_FREE) { while (tw_dev->chrdev_request_id != TW_IOCTL_CHRDEV_FREE)
{
/* FIXME: race condition on the wait*/
interruptible_sleep_on_timeout(&tw_dev->ioctl_wqueue, 1); interruptible_sleep_on_timeout(&tw_dev->ioctl_wqueue, 1);
do_gettimeofday(&timeout); if (time_after(jiffies, before + HZ *TW_IOCTL_CHRDEV_TIMEOUT)) {
if (before.tv_sec + TW_IOCTL_CHRDEV_TIMEOUT < timeout.tv_sec) {
/* Now we need to reset the board */ /* 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); 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); spin_lock_irqsave(&tw_dev->tw_lock, flags);
tw_dev->state[request_id] = TW_S_COMPLETED; tw_dev->state[request_id] = TW_S_COMPLETED;
tw_state_request_finish(tw_dev, request_id); 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--; tw_dev->posted_request_count--;
if (tw_reset_device_extension(tw_dev)) { 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); 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); spin_unlock_irqrestore(&tw_dev->tw_lock, flags);
clear_bit(TW_IN_CHRDEV_IOCTL, &tw_dev->flags);
if (signal_pending(current)) if (signal_pending(current))
return -EINTR; retval = -EINTR;
else else
return -EIO; retval = -EIO;
} else { goto out2;
goto tw_ioctl_chrdev_retry;
} }
} }
...@@ -777,24 +763,20 @@ static int tw_chrdev_ioctl(struct inode *inode, struct file *file, unsigned int ...@@ -777,24 +763,20 @@ static int tw_chrdev_ioctl(struct inode *inode, struct file *file, unsigned int
spin_unlock_irqrestore(&tw_dev->tw_lock, flags); spin_unlock_irqrestore(&tw_dev->tw_lock, flags);
break; break;
default: default:
printk(KERN_WARNING "3w-xxxx: Unknown chrdev ioctl 0x%x.\n", cmd); retval = -ENOTTY;
goto out2;
} }
/* Now copy the response to userspace */ /* Now copy the response to userspace */
error = copy_to_user((void *)arg, tw_ioctl, sizeof(TW_New_Ioctl) + tw_ioctl->data_buffer_length - 1); error = copy_to_user((void *)arg, tw_ioctl, sizeof(TW_New_Ioctl) + tw_ioctl->data_buffer_length - 1);
if (error) { if (error == 0)
printk(KERN_WARNING "3w-xxxx: tw_chrdev_ioctl(): Error copying data to userspace.\n"); retval = 0;
pci_free_consistent(tw_dev->tw_pci_dev, data_buffer_length_adjusted+sizeof(TW_New_Ioctl) - 1, cpu_addr, dma_handle); out2:
clear_bit(TW_IN_CHRDEV_IOCTL, &tw_dev->flags);
return -EFAULT;
}
/* Now free ioctl buf memory */ /* Now free ioctl buf memory */
pci_free_consistent(tw_dev->tw_pci_dev, data_buffer_length_adjusted+sizeof(TW_New_Ioctl) - 1, cpu_addr, dma_handle); pci_free_consistent(tw_dev->tw_pci_dev, data_buffer_length_adjusted+sizeof(TW_New_Ioctl) - 1, cpu_addr, dma_handle);
out:
clear_bit(TW_IN_CHRDEV_IOCTL, &tw_dev->flags); up(&tw_dev->ioctl_sem);
return retval;
return 0;
} /* End tw_chrdev_ioctl() */ } /* End tw_chrdev_ioctl() */
/* This function handles open for the character device */ /* This function handles open for the character device */
...@@ -1054,6 +1036,8 @@ int tw_findcards(Scsi_Host_Template *tw_host) ...@@ -1054,6 +1036,8 @@ int tw_findcards(Scsi_Host_Template *tw_host)
} }
memset(tw_dev, 0, sizeof(TW_Device_Extension)); memset(tw_dev, 0, sizeof(TW_Device_Extension));
init_MUTEX(&tw_dev->ioctl_sem);
/* Save pci_dev struct to device extension */ /* Save pci_dev struct to device extension */
tw_dev->tw_pci_dev = tw_pci_dev; tw_dev->tw_pci_dev = tw_pci_dev;
......
...@@ -425,6 +425,7 @@ typedef struct TAG_TW_Device_Extension { ...@@ -425,6 +425,7 @@ typedef struct TAG_TW_Device_Extension {
u32 aen_count; u32 aen_count;
struct Scsi_Host *host; struct Scsi_Host *host;
spinlock_t tw_lock; spinlock_t tw_lock;
struct semaphore ioctl_sem;
int ioctl_size[TW_Q_LENGTH]; int ioctl_size[TW_Q_LENGTH];
unsigned short aen_queue[TW_Q_LENGTH]; unsigned short aen_queue[TW_Q_LENGTH];
unsigned char aen_head; unsigned char aen_head;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment