From 6e03b7cca280a350f3d881ac777504176f59cebb Mon Sep 17 00:00:00 2001 From: wlf Date: Mon, 19 May 2014 18:27:21 +0800 Subject: [PATCH] USB: Improve transmission performance of the mass storage. --- drivers/usb/gadget/Kconfig | 13 +++ drivers/usb/gadget/android.c | 32 +++--- drivers/usb/gadget/f_mass_storage.c | 145 ++++++++++++++++++++++++++-- drivers/usb/gadget/storage_common.c | 52 ++++++++++ mm/page-writeback.c | 3 +- 5 files changed, 225 insertions(+), 20 deletions(-) mode change 100644 => 100755 drivers/usb/gadget/Kconfig mode change 100644 => 100755 drivers/usb/gadget/f_mass_storage.c mode change 100644 => 100755 drivers/usb/gadget/storage_common.c diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig old mode 100644 new mode 100755 index 67409fda70db..0ef3cdf97e6a --- a/drivers/usb/gadget/Kconfig +++ b/drivers/usb/gadget/Kconfig @@ -990,4 +990,17 @@ config USB_G_WEBCAM endchoice +config USB_CSW_HACK + boolean "USB Mass storage csw hack Feature" + default y + help + This csw hack feature is for increasing the performance of the mass + storage + +config USB_MSC_PROFILING + bool "USB MSC performance profiling" + help + If you say Y here, support will be added for collecting + Mass-storage performance numbers at the VFS level. + endif # USB_GADGET diff --git a/drivers/usb/gadget/android.c b/drivers/usb/gadget/android.c index 5d4b9628e3ec..aade8773c8a2 100755 --- a/drivers/usb/gadget/android.c +++ b/drivers/usb/gadget/android.c @@ -756,7 +756,8 @@ static int mass_storage_function_init(struct android_usb_function *f, { struct mass_storage_function_config *config; struct fsg_common *common; - int err; + int err, i; + const char *name[2]; config = kzalloc(sizeof(struct mass_storage_function_config), GFP_KERNEL); @@ -764,8 +765,12 @@ static int mass_storage_function_init(struct android_usb_function *f, return -ENOMEM; config->fsg.nluns = 2; - config->fsg.luns[0].removable = 1; - config->fsg.luns[1].removable = 1; + name[0] = "lun"; + name[1] = "lun1"; + for (i = 0; i < config->fsg.nluns; i++) { + config->fsg.luns[i].removable = 1; + config->fsg.luns[i].nofua = 1; + } common = fsg_common_init(NULL, cdev, &config->fsg); if (IS_ERR(common)) { @@ -773,20 +778,23 @@ static int mass_storage_function_init(struct android_usb_function *f, return PTR_ERR(common); } - err = sysfs_create_link(&f->dev->kobj, - &common->luns[0].dev.kobj, - "lun"); - err = sysfs_create_link(&f->dev->kobj, - &common->luns[1].dev.kobj, - "lun1"); - if (err) { - kfree(config); - return err; + for (i = 0; i < config->fsg.nluns; i++) { + err = sysfs_create_link(&f->dev->kobj, + &common->luns[i].dev.kobj, + name[i]); + if (err) + goto error; } config->common = common; f->config = config; return 0; +error: + for (; i > 0 ; i--) + sysfs_remove_link(&f->dev->kobj, name[i-1]); + fsg_common_release(&common->ref); + kfree(config); + return err; } static void mass_storage_function_cleanup(struct android_usb_function *f) diff --git a/drivers/usb/gadget/f_mass_storage.c b/drivers/usb/gadget/f_mass_storage.c old mode 100644 new mode 100755 index 817a98db92fe..7f4c3605b9f9 --- a/drivers/usb/gadget/f_mass_storage.c +++ b/drivers/usb/gadget/f_mass_storage.c @@ -234,7 +234,10 @@ static const char fsg_string_interface[] = "Mass Storage"; #include "storage_common.c" - +#ifdef CONFIG_USB_CSW_HACK +static int write_error_after_csw_sent; +static int csw_hack_sent; +#endif /*-------------------------------------------------------------------------*/ struct fsg_dev; @@ -250,6 +253,19 @@ struct fsg_operations { * set). */ int (*thread_exits)(struct fsg_common *common); + /* + * Called prior to ejection. Negative return means error, + * zero means to continue with ejection, positive means not to + * eject. + */ + int (*pre_eject)(struct fsg_common *common, + struct fsg_lun *lun, int num); + /* + * Called after ejection. Negative return means error, zero + * or positive is just a success. + */ + int (*post_eject)(struct fsg_common *common, + struct fsg_lun *lun, int num); }; /* Data shared by all the FSG instances. */ @@ -373,6 +389,7 @@ static inline struct fsg_dev *fsg_from_func(struct usb_function *f) } typedef void (*fsg_routine_t)(struct fsg_dev *); +static int send_status(struct fsg_common *common); static int exception_in_progress(struct fsg_common *common) { @@ -654,6 +671,9 @@ static int do_read(struct fsg_common *common) loff_t file_offset, file_offset_tmp; unsigned int amount; ssize_t nread; +#ifdef CONFIG_USB_MSC_PROFILING + ktime_t start, diff; +#endif /* * Get the starting Logical Block Address and check that it's @@ -728,11 +748,20 @@ static int do_read(struct fsg_common *common) /* Perform the read */ file_offset_tmp = file_offset; + +#ifdef CONFIG_USB_MSC_PROFILING + start = ktime_get(); +#endif nread = vfs_read(curlun->filp, (char __user *)bh->buf, amount, &file_offset_tmp); VLDBG(curlun, "file read %u @ %llu -> %d\n", amount, (unsigned long long)file_offset, (int)nread); +#ifdef CONFIG_USB_MSC_PROFILING + diff = ktime_sub(ktime_get(), start); + curlun->perf.rbytes += nread; + curlun->perf.rtime = ktime_add(curlun->perf.rtime, diff); +#endif if (signal_pending(current)) return -EINTR; @@ -794,6 +823,13 @@ static int do_write(struct fsg_common *common) ssize_t nwritten; int rc; +#ifdef CONFIG_USB_CSW_HACK + int i; +#endif + +#ifdef CONFIG_USB_MSC_PROFILING + ktime_t start, diff; +#endif if (curlun->ro) { curlun->sense_data = SS_WRITE_PROTECTED; return -EINVAL; @@ -893,7 +929,17 @@ static int do_write(struct fsg_common *common) bh = common->next_buffhd_to_drain; if (bh->state == BUF_STATE_EMPTY && !get_some_more) break; /* We stopped early */ +#ifdef CONFIG_USB_CSW_HACK + /* + * If the csw packet is already submmitted to the hardware, + * by marking the state of buffer as full, then by checking + * the residue, we make sure that this csw packet is not + * written on to the storage media. + */ + if (bh->state == BUF_STATE_FULL && common->residue) { +#else if (bh->state == BUF_STATE_FULL) { +#endif smp_rmb(); common->next_buffhd_to_drain = bh->next; bh->state = BUF_STATE_EMPTY; @@ -928,11 +974,20 @@ static int do_write(struct fsg_common *common) /* Perform the write */ file_offset_tmp = file_offset; +#ifdef CONFIG_USB_MSC_PROFILING + start = ktime_get(); +#endif nwritten = vfs_write(curlun->filp, (char __user *)bh->buf, amount, &file_offset_tmp); VLDBG(curlun, "file write %u @ %llu -> %d\n", amount, (unsigned long long)file_offset, (int)nwritten); +#ifdef CONFIG_USB_MSC_PROFILING + diff = ktime_sub(ktime_get(), start); + curlun->perf.wbytes += nwritten; + curlun->perf.wtime = + ktime_add(curlun->perf.wtime, diff); +#endif if (signal_pending(current)) return -EINTR; /* Interrupted! */ @@ -955,9 +1010,37 @@ static int do_write(struct fsg_common *common) curlun->sense_data_info = file_offset >> curlun->blkbits; curlun->info_valid = 1; +#ifdef CONFIG_USB_CSW_HACK + write_error_after_csw_sent = 1; + goto write_error; +#endif break; } +#ifdef CONFIG_USB_CSW_HACK +write_error: + if ((nwritten == amount) && !csw_hack_sent) { + if (write_error_after_csw_sent) + break; + /* + * Check if any of the buffer is in the + * busy state, if any buffer is in busy state, + * means the complete data is not received + * yet from the host. So there is no point in + * csw right away without the complete data. + */ + for (i = 0; i < fsg_num_buffers; i++) { + if (common->buffhds[i].state == + BUF_STATE_BUSY) + break; + } + if (!amount_left_to_req && i == fsg_num_buffers) { + csw_hack_sent = 1; + send_status(common); + } + } +#endif + empty_write: /* Did the host decide to stop early? */ if (bh->outreq->actual < bh->bulk_out_intended_length) { @@ -1380,13 +1463,26 @@ static int do_start_stop(struct fsg_common *common) if (!loej) return 0; + /* Simulate an unload/eject */ + if (common->ops && common->ops->pre_eject) { + int r = common->ops->pre_eject(common, curlun, + curlun - common->luns); + if (unlikely(r < 0)) + return r; + else if (r) + return 0; + } + up_read(&common->filesem); down_write(&common->filesem); fsg_lun_close(curlun); up_write(&common->filesem); down_read(&common->filesem); - return 0; + return common->ops && common->ops->post_eject + ? min(0, common->ops->post_eject(common, curlun, + curlun - common->luns)) + : 0; } static int do_prevent_allow(struct fsg_common *common) @@ -1407,7 +1503,7 @@ static int do_prevent_allow(struct fsg_common *common) return -EINVAL; } - if (curlun->prevent_medium_removal && !prevent) + if (!curlun->nofua && curlun->prevent_medium_removal && !prevent) fsg_lun_fsync_sub(curlun); curlun->prevent_medium_removal = prevent; return 0; @@ -1687,6 +1783,19 @@ static int send_status(struct fsg_common *common) csw->Signature = cpu_to_le32(US_BULK_CS_SIGN); csw->Tag = common->tag; csw->Residue = cpu_to_le32(common->residue); +#ifdef CONFIG_USB_CSW_HACK + /* Since csw is being sent early, before + * writing on to storage media, need to set + * residue to zero,assuming that write will succeed. + */ + if (write_error_after_csw_sent) { + write_error_after_csw_sent = 0; + csw->Residue = cpu_to_le32(common->residue); + } else + csw->Residue = 0; +#else + csw->Residue = cpu_to_le32(common->residue); +#endif csw->Status = status; bh->inreq->length = US_BULK_CS_WRAP_LEN; @@ -1906,7 +2015,7 @@ static int do_get_product_name(int ret ,char *buf) char *tag = "MACHINE_MODEL:"; char *pname; #if MSC_EXT_DBG - char tbuf[1300]; + char tbuf[1024]; if( buf == NULL ) buf = tbuf; #endif memset( buf , 0 , ret ); @@ -1937,7 +2046,7 @@ static int do_get_versions( int ret ,char* buf ) char *fw_tag = "FIRMWARE_VER:"; #if MSC_EXT_DBG - char tbuf[1300]; + char tbuf[1024]; if( ver == NULL ) ver = tbuf; #endif @@ -2364,7 +2473,7 @@ static int received_cbw(struct fsg_dev *fsg, struct fsg_buffhd *bh) if (common->data_size == 0) common->data_dir = DATA_DIR_NONE; common->lun = cbw->Lun; - if (common->lun < common->nluns) + if (common->lun >= 0 && common->lun < common->nluns) common->curlun = &common->luns[common->lun]; else common->curlun = NULL; @@ -2735,6 +2844,16 @@ static int fsg_main_thread(void *common_) common->state = FSG_STATE_STATUS_PHASE; spin_unlock_irq(&common->lock); +#ifdef CONFIG_USB_CSW_HACK + /* Since status is already sent for write scsi command, + * need to skip sending status once again if it is a + * write scsi command. + */ + if (csw_hack_sent) { + csw_hack_sent = 0; + continue; + } +#endif if (send_status(common)) continue; @@ -2774,6 +2893,9 @@ static int fsg_main_thread(void *common_) static DEVICE_ATTR(ro, 0644, fsg_show_ro, fsg_store_ro); static DEVICE_ATTR(nofua, 0644, fsg_show_nofua, fsg_store_nofua); static DEVICE_ATTR(file, 0644, fsg_show_file, fsg_store_file); +#ifdef CONFIG_USB_MSC_PROFILING +static DEVICE_ATTR(perf, 0644, fsg_show_perf, fsg_store_perf); +#endif static struct device_attribute dev_attr_ro_cdrom = __ATTR(ro, 0444, fsg_show_ro, NULL); @@ -2876,6 +2998,7 @@ static struct fsg_common *fsg_common_init(struct fsg_common *common, curlun->ro = lcfg->cdrom || lcfg->ro; curlun->initially_ro = curlun->ro; curlun->removable = lcfg->removable; + curlun->nofua = lcfg->nofua; curlun->dev.release = fsg_lun_release; curlun->dev.parent = &gadget->dev; /* curlun->dev.driver = &fsg_driver.driver; XXX */ @@ -2905,7 +3028,12 @@ static struct fsg_common *fsg_common_init(struct fsg_common *common, rc = device_create_file(&curlun->dev, &dev_attr_nofua); if (rc) goto error_luns; - +#ifdef CONFIG_USB_MSC_PROFILING + rc = device_create_file(&curlun->dev, &dev_attr_perf); + if (rc) + dev_err(&gadget->dev, "failed to create sysfs entry:" + "(dev_attr_perf) error: %d\n", rc); +#endif if (lcfg->filename) { rc = fsg_lun_open(curlun, lcfg->filename); if (rc) @@ -3022,6 +3150,9 @@ static void fsg_common_release(struct kref *ref) /* In error recovery common->nluns may be zero. */ for (; i; --i, ++lun) { +#ifdef CONFIG_USB_MSC_PROFILING + device_remove_file(&lun->dev, &dev_attr_perf); +#endif device_remove_file(&lun->dev, &dev_attr_nofua); device_remove_file(&lun->dev, lun->cdrom diff --git a/drivers/usb/gadget/storage_common.c b/drivers/usb/gadget/storage_common.c old mode 100644 new mode 100755 index 8d84935aad04..3c203df9041f --- a/drivers/usb/gadget/storage_common.c +++ b/drivers/usb/gadget/storage_common.c @@ -141,6 +141,17 @@ struct fsg_lun { unsigned int blkbits; /* Bits of logical block size of bound block device */ unsigned int blksize; /* logical block size of bound block device */ struct device dev; +#ifdef CONFIG_USB_MSC_PROFILING + spinlock_t lock; + struct { + + unsigned long rbytes; + unsigned long wbytes; + ktime_t rtime; + ktime_t wtime; + } perf; + +#endif }; static inline bool fsg_lun_is_open(struct fsg_lun *curlun) @@ -158,6 +169,9 @@ static inline struct fsg_lun *fsg_lun_from_dev(struct device *dev) #define EP0_BUFSIZE 256 #define DELAYED_STATUS (EP0_BUFSIZE + 999) /* An impossibly large value */ +#ifdef CONFIG_USB_CSW_HACK +#define fsg_num_buffers 4 +#else #ifdef CONFIG_USB_GADGET_DEBUG_FILES static unsigned int fsg_num_buffers = CONFIG_USB_GADGET_STORAGE_NUM_BUFFERS; @@ -173,6 +187,7 @@ MODULE_PARM_DESC(num_buffers, "Number of pipeline buffers"); #define fsg_num_buffers CONFIG_USB_GADGET_STORAGE_NUM_BUFFERS #endif /* CONFIG_USB_DEBUG */ +#endif /* CONFIG_USB_CSW_HACK */ /* check if fsg_num_buffers is within a valid range */ static inline int fsg_num_buffers_validate(void) @@ -565,6 +580,43 @@ static ssize_t fsg_show_nofua(struct device *dev, struct device_attribute *attr, return sprintf(buf, "%u\n", curlun->nofua); } +#ifdef CONFIG_USB_MSC_PROFILING +static ssize_t fsg_show_perf(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct fsg_lun *curlun = fsg_lun_from_dev(dev); + unsigned long rbytes, wbytes; + int64_t rtime, wtime; + + spin_lock(&curlun->lock); + rbytes = curlun->perf.rbytes; + wbytes = curlun->perf.wbytes; + rtime = ktime_to_us(curlun->perf.rtime); + wtime = ktime_to_us(curlun->perf.wtime); + spin_unlock(&curlun->lock); + + return snprintf(buf, PAGE_SIZE, "Write performance :" + "%lu bytes in %lld microseconds\n" + "Read performance :" + "%lu bytes in %lld microseconds\n", + wbytes, wtime, rbytes, rtime); +} +static ssize_t fsg_store_perf(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct fsg_lun *curlun = fsg_lun_from_dev(dev); + int value; + + sscanf(buf, "%d", &value); + if (!value) { + spin_lock(&curlun->lock); + memset(&curlun->perf, 0, sizeof(curlun->perf)); + spin_unlock(&curlun->lock); + } + + return count; +} +#endif static ssize_t fsg_show_file(struct device *dev, struct device_attribute *attr, char *buf) { diff --git a/mm/page-writeback.c b/mm/page-writeback.c index 73cbc5dc150b..5a45b9d0acd4 100644 --- a/mm/page-writeback.c +++ b/mm/page-writeback.c @@ -94,8 +94,9 @@ unsigned long vm_dirty_bytes; /* * The interval between `kupdate'-style writebacks + * modify by wlf@20140515 */ -unsigned int dirty_writeback_interval = 5 * 100; /* centiseconds */ +unsigned int dirty_writeback_interval = 1 * 100; /* centiseconds */ EXPORT_SYMBOL_GPL(dirty_writeback_interval); -- 2.34.1