Re: [PATCH 1/2] Shared USB host controller throughput statistics.

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Mon, 27 Apr 2009, Sarah Sharp wrote:

> This patch provides a generic way for USB host controllers (and potentially
> other drivers) to report throughput statistics.  The caller passes in a dentry
> for a debugging directory, and the code sets up a file to report the statistics
> under that.
> 
> Userspace can cat that file to receive a pair of numbers that represent the
> amount of data transferred (in bytes) and the time it took to send that data
> (using ktime_t and ktime_get()).  The file will block if no statistics are
> available.  When the driver has throughput data, it calls hcd_stat_update(),
> which will wakeup the blocked reader thread.
> 
> These statistics can be piped through a simple script to sample the data and
> feed the traffic data into a real-time graphing program, such as Trend.
> 	http://www.thregr.org/~wavexx/software/trend/
> 
> The script I used to gather EHCI and xHCI statistics can be found at
> 	http://minilop.net/~sarah/trend-sample.c
> 
> Special thanks to Jamey Sharp for writing this sampling code!

In addition to the issues raised by David, I don't see how this can 
possibly work as it stands.

For one thing, there's almost no synchronization between readers and 
writers.  What's to prevent the ring buffer from wrapping around?  This 
certainly will happen if more than 20 URBs are submitted before the 
stats file is opened.

Remember also that struct completions use a counting mechanism.  If you
call complete() N times in a row then the next N wait_for_completion()  
calls will return immediately.

> --- a/drivers/usb/Makefile
> +++ b/drivers/usb/Makefile
> @@ -20,6 +20,7 @@ obj-$(CONFIG_USB_U132_HCD)	+= host/
>  obj-$(CONFIG_USB_R8A66597_HCD)	+= host/
>  obj-$(CONFIG_USB_HWA_HCD)	+= host/
>  obj-$(CONFIG_USB_ISP1760_HCD)	+= host/
> +obj-$(CONFIG_USB_HCD_STAT)	+= host/

I suspect this line isn't really needed.  If no host controller drivers
are configured, what good will the stats do?

> +void hcd_stat_update(struct hcd_stat *dbg_stat, int len, ktime_t delta)
> +{
> +	unsigned int write_ptr = dbg_stat->write_ptr;
> +
> +	dbg_stat->len[write_ptr] = len;
> +	dbg_stat->delta[write_ptr] = delta;
> +	dbg_stat->write_ptr = (write_ptr + 1) % BUFFER_SIZE;
> +	complete(&dbg_stat->comp);
> +}
> +EXPORT_SYMBOL_GPL(hcd_stat_update);

No information should be retained if the stats file isn't open,
although this is a minor point.  See below...

> +/* Block the read if there's no statistics to return.
> + * Yes, we could just return zero if there's no data.  We'd rather block the
> + * input stream so that the CPU spends less cycles on passing back and forth
> + * zeros if the user is streaming this data to a traffic chart.
> + */
> +static ssize_t hcd_stat_in_read(struct file *file, char __user *buf,
> +			    size_t len, loff_t *offset)
> +{
> +	unsigned int read_ptr;
> +	struct hcd_stat *dbg_stat;
> +	wait_queue_head_t write_event;
> +	unsigned int buffer_len;
> +
> +	init_waitqueue_head(&write_event);
> +	dbg_stat = (struct hcd_stat *) file->private_data;
> +	read_ptr = dbg_stat->read_ptr;
> +	wait_for_completion_interruptible(
> +			&dbg_stat->comp);
> +	buffer_len = snprintf(buf, len, "%d %lld\n",
> +			dbg_stat->len[read_ptr],
> +			ktime_to_ns(dbg_stat->delta[read_ptr]));
> +
> +	if (dbg_stat->len[read_ptr] == 0 &&
> +			ktime_to_ns(dbg_stat->delta[read_ptr]) == 0)
> +		return buffer_len;

According to the comment above, you don't return zero if there is no
data available.  So what's the point of this test?  To prevent errors 
from wrap-around?  Why not block instead of returning the 0s?

> +
> +	dbg_stat->len[read_ptr] = 0;
> +	dbg_stat->delta[read_ptr] = ktime_set(0, 0);
> +	dbg_stat->read_ptr = (read_ptr + 1) % BUFFER_SIZE;
> +	return buffer_len;
> +}

Beyond these details, this design has a serious conceptual error.  
Suppose you want to measure the throughput of a 30-KB transfer, and the
transfer is broken up into three 10-KB URBs.  It's not unlikely that
all three will be submitted at once (that's how the scatter-gather
library works, for example).  Let's suppose the actual bus throughput
is 1 KB/ms (typical of full speed).

The delta times are measured from submission to completion.  As a
result, your statistics file will report:

	First read: Transfer length of URB 1 (10 KB) and transfer time 
		of URB 1 (10 ms).

	Second read: Transfer length of URB 2 (10 KB) and combined 
		transfer times of URBs 1 and 2 (20 ms).

	Third read: Transfer length of URB 3 (10 KB) and combined 
		transfer times of URBs 1, 2, and 3 (30 ms).

Surely that isn't what you really want, is it?  The reader would
calculate estimated throughputs of 1 KB/ms, 0.5 KB/ms, and 0.3 KB/ms,
for an average estimated throughput of 0.6 KB/ms.

What you really want to do is accumulate lengths as URBs complete, and
reset the total to 0 (and reset the start time) after every read.  
Which means you don't need a ring buffer at all.  And you don't need to 
store a delta time, only a start time.

Alan Stern

--
To unsubscribe from this list: send the line "unsubscribe linux-usb" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html

[Index of Archives]     [Linux Media]     [Linux Input]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Old Linux USB Devel Archive]

  Powered by Linux