diff --git a/drivers/usb/gadget/function/f_mtp.c b/drivers/usb/gadget/function/f_mtp.c index cd7a9d621720..49d60f38338a 100644 --- a/drivers/usb/gadget/function/f_mtp.c +++ b/drivers/usb/gadget/function/f_mtp.c @@ -26,6 +26,8 @@ #include #include +#include +#include #include #include #include @@ -74,6 +76,8 @@ #define MTP_RESPONSE_DEVICE_BUSY 0x2019 #define DRIVER_NAME "mtp" +#define MAX_ITERATION 100 + static unsigned int mtp_rx_req_len = MTP_BULK_BUFFER_SIZE; module_param(mtp_rx_req_len, uint, 0644); @@ -123,6 +127,14 @@ struct mtp_dev { uint16_t xfer_command; uint32_t xfer_transaction_id; int xfer_result; + struct { + unsigned long vfs_rbytes; + unsigned long vfs_wbytes; + unsigned int vfs_rtime; + unsigned int vfs_wtime; + } perf[MAX_ITERATION]; + unsigned int dbg_read_index; + unsigned int dbg_write_index; }; static struct usb_interface_descriptor mtp_interface_desc = { @@ -769,6 +781,7 @@ static void send_file_work(struct work_struct *data) int xfer, ret, hdr_size; int r = 0; int sendZLP = 0; + ktime_t start_time; /* read our parameters */ smp_rmb(); @@ -830,13 +843,19 @@ static void send_file_work(struct work_struct *data) __cpu_to_le32(dev->xfer_transaction_id); } + start_time = ktime_get(); ret = vfs_read(filp, req->buf + hdr_size, xfer - hdr_size, &offset); if (ret < 0) { r = ret; break; } + xfer = ret + hdr_size; + dev->perf[dev->dbg_read_index].vfs_rtime = + ktime_to_us(ktime_sub(ktime_get(), start_time)); + dev->perf[dev->dbg_read_index].vfs_rbytes = xfer; + dev->dbg_read_index = (dev->dbg_read_index + 1) % MAX_ITERATION; hdr_size = 0; req->length = xfer; @@ -875,6 +894,7 @@ static void receive_file_work(struct work_struct *data) int64_t count; int ret, cur_buf = 0; int r = 0; + ktime_t start_time; /* read our parameters */ smp_rmb(); @@ -903,6 +923,7 @@ static void receive_file_work(struct work_struct *data) if (write_req) { DBG(cdev, "rx %p %d\n", write_req, write_req->actual); + start_time = ktime_get(); ret = vfs_write(filp, write_req->buf, write_req->actual, &offset); DBG(cdev, "vfs_write %d\n", ret); @@ -911,6 +932,11 @@ static void receive_file_work(struct work_struct *data) dev->state = STATE_ERROR; break; } + dev->perf[dev->dbg_write_index].vfs_wtime = + ktime_to_us(ktime_sub(ktime_get(), start_time)); + dev->perf[dev->dbg_write_index].vfs_wbytes = ret; + dev->dbg_write_index = + (dev->dbg_write_index + 1) % MAX_ITERATION; write_req = NULL; } @@ -1391,6 +1417,133 @@ static void mtp_function_disable(struct usb_function *f) VDBG(cdev, "%s disabled\n", dev->function.name); } +static int debug_mtp_read_stats(struct seq_file *s, void *unused) +{ + struct mtp_dev *dev = _mtp_dev; + int i; + unsigned long flags; + unsigned min, max = 0, sum = 0, iteration = 0; + + seq_puts(s, "\n=======================\n"); + seq_puts(s, "MTP Write Stats:\n"); + seq_puts(s, "\n=======================\n"); + + spin_lock_irqsave(&dev->lock, flags); + + min = dev->perf[0].vfs_wtime; + + for (i = 0; i < MAX_ITERATION; i++) { + seq_printf(s, "vfs write: bytes:%ld\t\t time:%d\n", + dev->perf[i].vfs_wbytes, + dev->perf[i].vfs_wtime); + if (dev->perf[i].vfs_wbytes == mtp_rx_req_len) { + sum += dev->perf[i].vfs_wtime; + if (min > dev->perf[i].vfs_wtime) + min = dev->perf[i].vfs_wtime; + if (max < dev->perf[i].vfs_wtime) + max = dev->perf[i].vfs_wtime; + iteration++; + } + } + + if (iteration) { + seq_printf(s, "vfs_write(time in usec) min:%d\t max:%d\t avg:%d\n", + min, max, sum / iteration); + } + + seq_puts(s, "\n=======================\n"); + seq_puts(s, "MTP Read Stats:\n"); + seq_puts(s, "\n=======================\n"); + + min = dev->perf[0].vfs_rtime; + max = 0; + sum = 0; + iteration = 0; + + for (i = 0; i < MAX_ITERATION; i++) { + seq_printf(s, "vfs read: bytes:%ld\t\t time:%d\n", + dev->perf[i].vfs_rbytes, + dev->perf[i].vfs_rtime); + if (dev->perf[i].vfs_rbytes == mtp_tx_req_len) { + sum += dev->perf[i].vfs_rtime; + if (min > dev->perf[i].vfs_rtime) + min = dev->perf[i].vfs_rtime; + if (max < dev->perf[i].vfs_rtime) + max = dev->perf[i].vfs_rtime; + iteration++; + } + } + + if (iteration) { + seq_printf(s, "vfs_read(time in usec) min:%d\t max:%d\t avg:%d\n", + min, max, sum / iteration); + } + + spin_unlock_irqrestore(&dev->lock, flags); + return 0; +} + +static ssize_t debug_mtp_reset_stats(struct file *file, + const char __user *ubuf, + size_t count, loff_t *ppos) +{ + unsigned long flags; + struct mtp_dev *dev = _mtp_dev; + char buf[8]; + + if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count))) { + pr_err("[%s] EINVAL\n", __func__); + goto done; + } + + if (strncmp(buf, "0", 1)) { + pr_err("Wrong value. To clear stats, enter value as 0.\n"); + goto done; + } + + spin_lock_irqsave(&dev->lock, flags); + memset(&dev->perf[0], 0, MAX_ITERATION * sizeof(dev->perf[0])); + dev->dbg_read_index = 0; + dev->dbg_write_index = 0; + spin_unlock_irqrestore(&dev->lock, flags); +done: + return count; +} + +static int debug_mtp_open(struct inode *inode, struct file *file) +{ + return single_open(file, debug_mtp_read_stats, inode->i_private); +} + +static const struct file_operations debug_mtp_ops = { + .open = debug_mtp_open, + .read = seq_read, + .write = debug_mtp_reset_stats, +}; + +struct dentry *dent_mtp; +static void mtp_debugfs_init(void) +{ + struct dentry *dent_mtp_status; + + dent_mtp = debugfs_create_dir("usb_mtp", 0); + if (!dent_mtp || IS_ERR(dent_mtp)) + return; + + dent_mtp_status = debugfs_create_file("status", 0644, dent_mtp, + 0, &debug_mtp_ops); + if (!dent_mtp_status || IS_ERR(dent_mtp_status)) { + debugfs_remove(dent_mtp); + dent_mtp = NULL; + return; + } +} + +static void mtp_debugfs_remove(void) +{ + debugfs_remove_recursive(dent_mtp); +} + static int __mtp_setup(struct mtp_instance *fi_mtp) { struct mtp_dev *dev; @@ -1427,6 +1580,7 @@ static int __mtp_setup(struct mtp_instance *fi_mtp) if (ret) goto err2; + mtp_debugfs_init(); return 0; err2: @@ -1451,6 +1605,7 @@ static void mtp_cleanup(void) if (!dev) return; + mtp_debugfs_remove(); misc_deregister(&mtp_device); destroy_workqueue(dev->wq); _mtp_dev = NULL;