Commit 4d5e18d9 authored by Guido Kiener's avatar Guido Kiener Committed by Greg Kroah-Hartman
Browse files

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: default avatarGuido Kiener <guido.kiener@rohde-schwarz.com>
Reviewed-by: default avatarSteve Bayless <steve_bayless@keysight.com>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent ec34d08e
Loading
Loading
Loading
Loading
+106 −64
Original line number Diff line number Diff line
@@ -1509,36 +1509,53 @@ static ssize_t usbtmc_write(struct file *filp, const char __user *buf,
{
	struct usbtmc_file_data *file_data;
	struct usbtmc_device_data *data;
	struct urb *urb = NULL;
	ssize_t retval = 0;
	u8 *buffer;
	int retval;
	int actual;
	unsigned long int n_bytes;
	int remaining;
	int done;
	int this_part;
	u32 remaining, done;
	u32 transfersize, aligned, buflen;

	file_data = filp->private_data;
	data = file_data->data;

	buffer = kmalloc(USBTMC_SIZE_IOBUFFER, GFP_KERNEL);
	if (!buffer)
		return -ENOMEM;

	mutex_lock(&data->io_mutex);

	if (data->zombie) {
		retval = -ENODEV;
		goto exit;
	}

	remaining = count;
	done = 0;

	while (remaining > 0) {
		if (remaining > USBTMC_SIZE_IOBUFFER - USBTMC_HEADER_SIZE) {
			this_part = USBTMC_SIZE_IOBUFFER - USBTMC_HEADER_SIZE;
	spin_lock_irq(&file_data->err_lock);
	file_data->out_transfer_size = 0;
	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;
	} else {
			this_part = remaining;
		transfersize = count;
		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[2] = ~data->bTag;
	buffer[3] = 0; /* Reserved */
		buffer[4] = this_part >> 0;
		buffer[5] = this_part >> 8;
		buffer[6] = this_part >> 16;
		buffer[7] = this_part >> 24;
	buffer[4] = transfersize >> 0;
	buffer[5] = transfersize >> 8;
	buffer[6] = transfersize >> 16;
	buffer[7] = transfersize >> 24;
	/* buffer[8] is set above... */
	buffer[9] = 0; /* Reserved */
	buffer[10] = 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;
		up(&file_data->limit_write_sem);
		goto exit;
	}

		n_bytes = roundup(USBTMC_HEADER_SIZE + this_part, 4);
		memset(buffer + USBTMC_HEADER_SIZE + this_part, 0, n_bytes - (USBTMC_HEADER_SIZE + this_part));
	dev_dbg(&data->intf->dev, "%s(size:%u align:%u)\n", __func__,
		(unsigned int)transfersize, (unsigned int)aligned);

		do {
			retval = usb_bulk_msg(data->usb_dev,
					      usb_sndbulkpipe(data->usb_dev,
							      data->bulk_out),
					      buffer, n_bytes,
					      &actual, file_data->timeout);
			if (retval != 0)
				break;
			n_bytes -= actual;
		} while (n_bytes);
	print_hex_dump_debug("usbtmc ", DUMP_PREFIX_NONE,
			     16, 1, buffer, aligned, true);

	usb_fill_bulk_urb(urb, data->usb_dev,
		usb_sndbulkpipe(data->usb_dev, data->bulk_out),
		urb->transfer_buffer, aligned,
		usbtmc_write_bulk_cb, file_data);

	usb_anchor_urb(urb, &file_data->submitted);
	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++;
@@ -1581,22 +1615,30 @@ static ssize_t usbtmc_write(struct file *filp, const char __user *buf,
	if (!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) {
		usb_kill_anchored_urbs(&file_data->submitted);

		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)
			usbtmc_ioctl_abort_bulk_out(data);
		goto exit;
	}

		remaining -= this_part;
		done += this_part;
	}

	retval = count;
	retval = done;
exit:
	usb_free_urb(urb);
	mutex_unlock(&data->io_mutex);
	kfree(buffer);
	return retval;
}