usb: usbtmc: Optimize usbtmc_write
Use new usbtmc_generic_write function to maximize bandwidth during long data transfer. The maximum output transfer size is limited to INT_MAX (=2GB). Signed-off-by: Guido Kiener <guido.kiener@rohde-schwarz.com> Reviewed-by: Steve Bayless <steve_bayless@keysight.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
committed by
Greg Kroah-Hartman
parent
ec34d08eff
commit
4d5e18d9ed
@@ -1509,36 +1509,53 @@ static ssize_t usbtmc_write(struct file *filp, const char __user *buf,
|
|||||||
{
|
{
|
||||||
struct usbtmc_file_data *file_data;
|
struct usbtmc_file_data *file_data;
|
||||||
struct usbtmc_device_data *data;
|
struct usbtmc_device_data *data;
|
||||||
|
struct urb *urb = NULL;
|
||||||
|
ssize_t retval = 0;
|
||||||
u8 *buffer;
|
u8 *buffer;
|
||||||
int retval;
|
u32 remaining, done;
|
||||||
int actual;
|
u32 transfersize, aligned, buflen;
|
||||||
unsigned long int n_bytes;
|
|
||||||
int remaining;
|
|
||||||
int done;
|
|
||||||
int this_part;
|
|
||||||
|
|
||||||
file_data = filp->private_data;
|
file_data = filp->private_data;
|
||||||
data = file_data->data;
|
data = file_data->data;
|
||||||
|
|
||||||
buffer = kmalloc(USBTMC_SIZE_IOBUFFER, GFP_KERNEL);
|
|
||||||
if (!buffer)
|
|
||||||
return -ENOMEM;
|
|
||||||
|
|
||||||
mutex_lock(&data->io_mutex);
|
mutex_lock(&data->io_mutex);
|
||||||
|
|
||||||
if (data->zombie) {
|
if (data->zombie) {
|
||||||
retval = -ENODEV;
|
retval = -ENODEV;
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
remaining = count;
|
|
||||||
done = 0;
|
done = 0;
|
||||||
|
|
||||||
while (remaining > 0) {
|
spin_lock_irq(&file_data->err_lock);
|
||||||
if (remaining > USBTMC_SIZE_IOBUFFER - USBTMC_HEADER_SIZE) {
|
file_data->out_transfer_size = 0;
|
||||||
this_part = USBTMC_SIZE_IOBUFFER - USBTMC_HEADER_SIZE;
|
file_data->out_status = 0;
|
||||||
|
spin_unlock_irq(&file_data->err_lock);
|
||||||
|
|
||||||
|
if (!count)
|
||||||
|
goto exit;
|
||||||
|
|
||||||
|
if (down_trylock(&file_data->limit_write_sem)) {
|
||||||
|
/* previous calls were async */
|
||||||
|
retval = -EBUSY;
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
urb = usbtmc_create_urb();
|
||||||
|
if (!urb) {
|
||||||
|
retval = -ENOMEM;
|
||||||
|
up(&file_data->limit_write_sem);
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
buffer = urb->transfer_buffer;
|
||||||
|
buflen = urb->transfer_buffer_length;
|
||||||
|
|
||||||
|
if (count > INT_MAX) {
|
||||||
|
transfersize = INT_MAX;
|
||||||
buffer[8] = 0;
|
buffer[8] = 0;
|
||||||
} else {
|
} else {
|
||||||
this_part = remaining;
|
transfersize = count;
|
||||||
buffer[8] = file_data->eom_val;
|
buffer[8] = file_data->eom_val;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1547,33 +1564,50 @@ static ssize_t usbtmc_write(struct file *filp, const char __user *buf,
|
|||||||
buffer[1] = data->bTag;
|
buffer[1] = data->bTag;
|
||||||
buffer[2] = ~data->bTag;
|
buffer[2] = ~data->bTag;
|
||||||
buffer[3] = 0; /* Reserved */
|
buffer[3] = 0; /* Reserved */
|
||||||
buffer[4] = this_part >> 0;
|
buffer[4] = transfersize >> 0;
|
||||||
buffer[5] = this_part >> 8;
|
buffer[5] = transfersize >> 8;
|
||||||
buffer[6] = this_part >> 16;
|
buffer[6] = transfersize >> 16;
|
||||||
buffer[7] = this_part >> 24;
|
buffer[7] = transfersize >> 24;
|
||||||
/* buffer[8] is set above... */
|
/* buffer[8] is set above... */
|
||||||
buffer[9] = 0; /* Reserved */
|
buffer[9] = 0; /* Reserved */
|
||||||
buffer[10] = 0; /* Reserved */
|
buffer[10] = 0; /* Reserved */
|
||||||
buffer[11] = 0; /* Reserved */
|
buffer[11] = 0; /* Reserved */
|
||||||
|
|
||||||
if (copy_from_user(&buffer[USBTMC_HEADER_SIZE], buf + done, this_part)) {
|
remaining = transfersize;
|
||||||
|
|
||||||
|
if (transfersize + USBTMC_HEADER_SIZE > buflen) {
|
||||||
|
transfersize = buflen - USBTMC_HEADER_SIZE;
|
||||||
|
aligned = buflen;
|
||||||
|
} else {
|
||||||
|
aligned = (transfersize + (USBTMC_HEADER_SIZE + 3)) & ~3;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (copy_from_user(&buffer[USBTMC_HEADER_SIZE], buf, transfersize)) {
|
||||||
retval = -EFAULT;
|
retval = -EFAULT;
|
||||||
|
up(&file_data->limit_write_sem);
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
n_bytes = roundup(USBTMC_HEADER_SIZE + this_part, 4);
|
dev_dbg(&data->intf->dev, "%s(size:%u align:%u)\n", __func__,
|
||||||
memset(buffer + USBTMC_HEADER_SIZE + this_part, 0, n_bytes - (USBTMC_HEADER_SIZE + this_part));
|
(unsigned int)transfersize, (unsigned int)aligned);
|
||||||
|
|
||||||
do {
|
print_hex_dump_debug("usbtmc ", DUMP_PREFIX_NONE,
|
||||||
retval = usb_bulk_msg(data->usb_dev,
|
16, 1, buffer, aligned, true);
|
||||||
usb_sndbulkpipe(data->usb_dev,
|
|
||||||
data->bulk_out),
|
usb_fill_bulk_urb(urb, data->usb_dev,
|
||||||
buffer, n_bytes,
|
usb_sndbulkpipe(data->usb_dev, data->bulk_out),
|
||||||
&actual, file_data->timeout);
|
urb->transfer_buffer, aligned,
|
||||||
if (retval != 0)
|
usbtmc_write_bulk_cb, file_data);
|
||||||
break;
|
|
||||||
n_bytes -= actual;
|
usb_anchor_urb(urb, &file_data->submitted);
|
||||||
} while (n_bytes);
|
retval = usb_submit_urb(urb, GFP_KERNEL);
|
||||||
|
if (unlikely(retval)) {
|
||||||
|
usb_unanchor_urb(urb);
|
||||||
|
up(&file_data->limit_write_sem);
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
remaining -= transfersize;
|
||||||
|
|
||||||
data->bTag_last_write = data->bTag;
|
data->bTag_last_write = data->bTag;
|
||||||
data->bTag++;
|
data->bTag++;
|
||||||
@@ -1581,22 +1615,30 @@ static ssize_t usbtmc_write(struct file *filp, const char __user *buf,
|
|||||||
if (!data->bTag)
|
if (!data->bTag)
|
||||||
data->bTag++;
|
data->bTag++;
|
||||||
|
|
||||||
|
/* call generic_write even when remaining = 0 */
|
||||||
|
retval = usbtmc_generic_write(file_data, buf + transfersize, remaining,
|
||||||
|
&done, USBTMC_FLAG_APPEND);
|
||||||
|
/* truncate alignment bytes */
|
||||||
|
if (done > remaining)
|
||||||
|
done = remaining;
|
||||||
|
|
||||||
|
/*add size of first urb*/
|
||||||
|
done += transfersize;
|
||||||
|
|
||||||
if (retval < 0) {
|
if (retval < 0) {
|
||||||
|
usb_kill_anchored_urbs(&file_data->submitted);
|
||||||
|
|
||||||
dev_err(&data->intf->dev,
|
dev_err(&data->intf->dev,
|
||||||
"Unable to send data, error %d\n", retval);
|
"Unable to send data, error %d\n", (int)retval);
|
||||||
if (file_data->auto_abort)
|
if (file_data->auto_abort)
|
||||||
usbtmc_ioctl_abort_bulk_out(data);
|
usbtmc_ioctl_abort_bulk_out(data);
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
remaining -= this_part;
|
retval = done;
|
||||||
done += this_part;
|
|
||||||
}
|
|
||||||
|
|
||||||
retval = count;
|
|
||||||
exit:
|
exit:
|
||||||
|
usb_free_urb(urb);
|
||||||
mutex_unlock(&data->io_mutex);
|
mutex_unlock(&data->io_mutex);
|
||||||
kfree(buffer);
|
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user