mirror of
https://github.com/torvalds/linux.git
synced 2024-11-17 01:22:07 +00:00
usb: gadget: storage: make FSG_NUM_BUFFERS variable size
FSG_NUM_BUFFERS is set to 2 as default. Usually 2 buffers are enough to establish a good buffering pipeline. The number may be increased in order to compensate a for bursty VFS behaviour. Here follows a description of system that may require more than 2 buffers. * CPU ondemand governor active * latency cost for wake up and/or frequency change * DMA for IO Use case description. * Data transfer from MMC via VFS to USB. * DMA shuffles data from MMC and to USB. * The CPU wakes up every now and then to pass data in and out from VFS, which cause the bursty VFS behaviour. Test set up * Running dd on the host reading from the mass storage device * cmdline: dd if=/dev/sdb of=/dev/null bs=4k count=$((256*100)) * Caches are dropped on the host and on the device before each run Measurements on a Snowball board with ondemand_governor active. FSG_NUM_BUFFERS 2 104857600 bytes (105 MB) copied, 5.62173 s, 18.7 MB/s 104857600 bytes (105 MB) copied, 5.61811 s, 18.7 MB/s 104857600 bytes (105 MB) copied, 5.57817 s, 18.8 MB/s FSG_NUM_BUFFERS 4 104857600 bytes (105 MB) copied, 5.26839 s, 19.9 MB/s 104857600 bytes (105 MB) copied, 5.2691 s, 19.9 MB/s 104857600 bytes (105 MB) copied, 5.2711 s, 19.9 MB/s There may not be one optimal number for all boards. This is why the number is added to Kconfig. If selecting USB_GADGET_DEBUG_FILES this value may be set by a module parameter as well. Signed-off-by: Per Forlin <per.forlin@linaro.org> Acked-by: Michal Nazarewicz <mina86@mina86.com> Acked-by: Alan Stern <stern@rowland.harvard.edu> Signed-off-by: Felipe Balbi <balbi@ti.com>
This commit is contained in:
parent
04eee25b1d
commit
6532c7fdb2
@ -96,6 +96,22 @@ config USB_GADGET_VBUS_DRAW
|
|||||||
This value will be used except for system-specific gadget
|
This value will be used except for system-specific gadget
|
||||||
drivers that have more specific information.
|
drivers that have more specific information.
|
||||||
|
|
||||||
|
config USB_GADGET_STORAGE_NUM_BUFFERS
|
||||||
|
int "Number of storage pipeline buffers"
|
||||||
|
range 2 4
|
||||||
|
default 2
|
||||||
|
help
|
||||||
|
Usually 2 buffers are enough to establish a good buffering
|
||||||
|
pipeline. The number may be increased in order to compensate
|
||||||
|
for a bursty VFS behaviour. For instance there may be CPU wake up
|
||||||
|
latencies that makes the VFS to appear bursty in a system with
|
||||||
|
an CPU on-demand governor. Especially if DMA is doing IO to
|
||||||
|
offload the CPU. In this case the CPU will go into power
|
||||||
|
save often and spin up occasionally to move data within VFS.
|
||||||
|
If selecting USB_GADGET_DEBUG_FILES this value may be set by
|
||||||
|
a module parameter as well.
|
||||||
|
If unsure, say 2.
|
||||||
|
|
||||||
#
|
#
|
||||||
# USB Peripheral Controller Support
|
# USB Peripheral Controller Support
|
||||||
#
|
#
|
||||||
|
@ -362,7 +362,7 @@ struct fsg_common {
|
|||||||
|
|
||||||
struct fsg_buffhd *next_buffhd_to_fill;
|
struct fsg_buffhd *next_buffhd_to_fill;
|
||||||
struct fsg_buffhd *next_buffhd_to_drain;
|
struct fsg_buffhd *next_buffhd_to_drain;
|
||||||
struct fsg_buffhd buffhds[FSG_NUM_BUFFERS];
|
struct fsg_buffhd *buffhds;
|
||||||
|
|
||||||
int cmnd_size;
|
int cmnd_size;
|
||||||
u8 cmnd[MAX_COMMAND_SIZE];
|
u8 cmnd[MAX_COMMAND_SIZE];
|
||||||
@ -2340,7 +2340,7 @@ reset:
|
|||||||
if (common->fsg) {
|
if (common->fsg) {
|
||||||
fsg = common->fsg;
|
fsg = common->fsg;
|
||||||
|
|
||||||
for (i = 0; i < FSG_NUM_BUFFERS; ++i) {
|
for (i = 0; i < fsg_num_buffers; ++i) {
|
||||||
struct fsg_buffhd *bh = &common->buffhds[i];
|
struct fsg_buffhd *bh = &common->buffhds[i];
|
||||||
|
|
||||||
if (bh->inreq) {
|
if (bh->inreq) {
|
||||||
@ -2397,7 +2397,7 @@ reset:
|
|||||||
clear_bit(IGNORE_BULK_OUT, &fsg->atomic_bitflags);
|
clear_bit(IGNORE_BULK_OUT, &fsg->atomic_bitflags);
|
||||||
|
|
||||||
/* Allocate the requests */
|
/* Allocate the requests */
|
||||||
for (i = 0; i < FSG_NUM_BUFFERS; ++i) {
|
for (i = 0; i < fsg_num_buffers; ++i) {
|
||||||
struct fsg_buffhd *bh = &common->buffhds[i];
|
struct fsg_buffhd *bh = &common->buffhds[i];
|
||||||
|
|
||||||
rc = alloc_request(common, fsg->bulk_in, &bh->inreq);
|
rc = alloc_request(common, fsg->bulk_in, &bh->inreq);
|
||||||
@ -2466,7 +2466,7 @@ static void handle_exception(struct fsg_common *common)
|
|||||||
|
|
||||||
/* Cancel all the pending transfers */
|
/* Cancel all the pending transfers */
|
||||||
if (likely(common->fsg)) {
|
if (likely(common->fsg)) {
|
||||||
for (i = 0; i < FSG_NUM_BUFFERS; ++i) {
|
for (i = 0; i < fsg_num_buffers; ++i) {
|
||||||
bh = &common->buffhds[i];
|
bh = &common->buffhds[i];
|
||||||
if (bh->inreq_busy)
|
if (bh->inreq_busy)
|
||||||
usb_ep_dequeue(common->fsg->bulk_in, bh->inreq);
|
usb_ep_dequeue(common->fsg->bulk_in, bh->inreq);
|
||||||
@ -2478,7 +2478,7 @@ static void handle_exception(struct fsg_common *common)
|
|||||||
/* Wait until everything is idle */
|
/* Wait until everything is idle */
|
||||||
for (;;) {
|
for (;;) {
|
||||||
int num_active = 0;
|
int num_active = 0;
|
||||||
for (i = 0; i < FSG_NUM_BUFFERS; ++i) {
|
for (i = 0; i < fsg_num_buffers; ++i) {
|
||||||
bh = &common->buffhds[i];
|
bh = &common->buffhds[i];
|
||||||
num_active += bh->inreq_busy + bh->outreq_busy;
|
num_active += bh->inreq_busy + bh->outreq_busy;
|
||||||
}
|
}
|
||||||
@ -2501,7 +2501,7 @@ static void handle_exception(struct fsg_common *common)
|
|||||||
*/
|
*/
|
||||||
spin_lock_irq(&common->lock);
|
spin_lock_irq(&common->lock);
|
||||||
|
|
||||||
for (i = 0; i < FSG_NUM_BUFFERS; ++i) {
|
for (i = 0; i < fsg_num_buffers; ++i) {
|
||||||
bh = &common->buffhds[i];
|
bh = &common->buffhds[i];
|
||||||
bh->state = BUF_STATE_EMPTY;
|
bh->state = BUF_STATE_EMPTY;
|
||||||
}
|
}
|
||||||
@ -2710,6 +2710,10 @@ static struct fsg_common *fsg_common_init(struct fsg_common *common,
|
|||||||
int nluns, i, rc;
|
int nluns, i, rc;
|
||||||
char *pathbuf;
|
char *pathbuf;
|
||||||
|
|
||||||
|
rc = fsg_num_buffers_validate();
|
||||||
|
if (rc != 0)
|
||||||
|
return ERR_PTR(rc);
|
||||||
|
|
||||||
/* Find out how many LUNs there should be */
|
/* Find out how many LUNs there should be */
|
||||||
nluns = cfg->nluns;
|
nluns = cfg->nluns;
|
||||||
if (nluns < 1 || nluns > FSG_MAX_LUNS) {
|
if (nluns < 1 || nluns > FSG_MAX_LUNS) {
|
||||||
@ -2728,6 +2732,14 @@ static struct fsg_common *fsg_common_init(struct fsg_common *common,
|
|||||||
common->free_storage_on_release = 0;
|
common->free_storage_on_release = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
common->buffhds = kcalloc(fsg_num_buffers,
|
||||||
|
sizeof *(common->buffhds), GFP_KERNEL);
|
||||||
|
if (!common->buffhds) {
|
||||||
|
if (common->free_storage_on_release)
|
||||||
|
kfree(common);
|
||||||
|
return ERR_PTR(-ENOMEM);
|
||||||
|
}
|
||||||
|
|
||||||
common->ops = cfg->ops;
|
common->ops = cfg->ops;
|
||||||
common->private_data = cfg->private_data;
|
common->private_data = cfg->private_data;
|
||||||
|
|
||||||
@ -2805,7 +2817,7 @@ static struct fsg_common *fsg_common_init(struct fsg_common *common,
|
|||||||
|
|
||||||
/* Data buffers cyclic list */
|
/* Data buffers cyclic list */
|
||||||
bh = common->buffhds;
|
bh = common->buffhds;
|
||||||
i = FSG_NUM_BUFFERS;
|
i = fsg_num_buffers;
|
||||||
goto buffhds_first_it;
|
goto buffhds_first_it;
|
||||||
do {
|
do {
|
||||||
bh->next = bh + 1;
|
bh->next = bh + 1;
|
||||||
@ -2931,12 +2943,13 @@ static void fsg_common_release(struct kref *ref)
|
|||||||
|
|
||||||
{
|
{
|
||||||
struct fsg_buffhd *bh = common->buffhds;
|
struct fsg_buffhd *bh = common->buffhds;
|
||||||
unsigned i = FSG_NUM_BUFFERS;
|
unsigned i = fsg_num_buffers;
|
||||||
do {
|
do {
|
||||||
kfree(bh->buf);
|
kfree(bh->buf);
|
||||||
} while (++bh, --i);
|
} while (++bh, --i);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
kfree(common->buffhds);
|
||||||
if (common->free_storage_on_release)
|
if (common->free_storage_on_release)
|
||||||
kfree(common);
|
kfree(common);
|
||||||
}
|
}
|
||||||
|
@ -460,7 +460,6 @@ struct fsg_dev {
|
|||||||
|
|
||||||
struct fsg_buffhd *next_buffhd_to_fill;
|
struct fsg_buffhd *next_buffhd_to_fill;
|
||||||
struct fsg_buffhd *next_buffhd_to_drain;
|
struct fsg_buffhd *next_buffhd_to_drain;
|
||||||
struct fsg_buffhd buffhds[FSG_NUM_BUFFERS];
|
|
||||||
|
|
||||||
int thread_wakeup_needed;
|
int thread_wakeup_needed;
|
||||||
struct completion thread_notifier;
|
struct completion thread_notifier;
|
||||||
@ -487,6 +486,8 @@ struct fsg_dev {
|
|||||||
unsigned int nluns;
|
unsigned int nluns;
|
||||||
struct fsg_lun *luns;
|
struct fsg_lun *luns;
|
||||||
struct fsg_lun *curlun;
|
struct fsg_lun *curlun;
|
||||||
|
/* Must be the last entry */
|
||||||
|
struct fsg_buffhd buffhds[];
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef void (*fsg_routine_t)(struct fsg_dev *);
|
typedef void (*fsg_routine_t)(struct fsg_dev *);
|
||||||
@ -2737,7 +2738,7 @@ static int do_set_interface(struct fsg_dev *fsg, int altsetting)
|
|||||||
|
|
||||||
reset:
|
reset:
|
||||||
/* Deallocate the requests */
|
/* Deallocate the requests */
|
||||||
for (i = 0; i < FSG_NUM_BUFFERS; ++i) {
|
for (i = 0; i < fsg_num_buffers; ++i) {
|
||||||
struct fsg_buffhd *bh = &fsg->buffhds[i];
|
struct fsg_buffhd *bh = &fsg->buffhds[i];
|
||||||
|
|
||||||
if (bh->inreq) {
|
if (bh->inreq) {
|
||||||
@ -2798,7 +2799,7 @@ reset:
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Allocate the requests */
|
/* Allocate the requests */
|
||||||
for (i = 0; i < FSG_NUM_BUFFERS; ++i) {
|
for (i = 0; i < fsg_num_buffers; ++i) {
|
||||||
struct fsg_buffhd *bh = &fsg->buffhds[i];
|
struct fsg_buffhd *bh = &fsg->buffhds[i];
|
||||||
|
|
||||||
if ((rc = alloc_request(fsg, fsg->bulk_in, &bh->inreq)) != 0)
|
if ((rc = alloc_request(fsg, fsg->bulk_in, &bh->inreq)) != 0)
|
||||||
@ -2894,7 +2895,7 @@ static void handle_exception(struct fsg_dev *fsg)
|
|||||||
/* Cancel all the pending transfers */
|
/* Cancel all the pending transfers */
|
||||||
if (fsg->intreq_busy)
|
if (fsg->intreq_busy)
|
||||||
usb_ep_dequeue(fsg->intr_in, fsg->intreq);
|
usb_ep_dequeue(fsg->intr_in, fsg->intreq);
|
||||||
for (i = 0; i < FSG_NUM_BUFFERS; ++i) {
|
for (i = 0; i < fsg_num_buffers; ++i) {
|
||||||
bh = &fsg->buffhds[i];
|
bh = &fsg->buffhds[i];
|
||||||
if (bh->inreq_busy)
|
if (bh->inreq_busy)
|
||||||
usb_ep_dequeue(fsg->bulk_in, bh->inreq);
|
usb_ep_dequeue(fsg->bulk_in, bh->inreq);
|
||||||
@ -2905,7 +2906,7 @@ static void handle_exception(struct fsg_dev *fsg)
|
|||||||
/* Wait until everything is idle */
|
/* Wait until everything is idle */
|
||||||
for (;;) {
|
for (;;) {
|
||||||
num_active = fsg->intreq_busy;
|
num_active = fsg->intreq_busy;
|
||||||
for (i = 0; i < FSG_NUM_BUFFERS; ++i) {
|
for (i = 0; i < fsg_num_buffers; ++i) {
|
||||||
bh = &fsg->buffhds[i];
|
bh = &fsg->buffhds[i];
|
||||||
num_active += bh->inreq_busy + bh->outreq_busy;
|
num_active += bh->inreq_busy + bh->outreq_busy;
|
||||||
}
|
}
|
||||||
@ -2927,7 +2928,7 @@ static void handle_exception(struct fsg_dev *fsg)
|
|||||||
* state, and the exception. Then invoke the handler. */
|
* state, and the exception. Then invoke the handler. */
|
||||||
spin_lock_irq(&fsg->lock);
|
spin_lock_irq(&fsg->lock);
|
||||||
|
|
||||||
for (i = 0; i < FSG_NUM_BUFFERS; ++i) {
|
for (i = 0; i < fsg_num_buffers; ++i) {
|
||||||
bh = &fsg->buffhds[i];
|
bh = &fsg->buffhds[i];
|
||||||
bh->state = BUF_STATE_EMPTY;
|
bh->state = BUF_STATE_EMPTY;
|
||||||
}
|
}
|
||||||
@ -3157,7 +3158,7 @@ static void /* __init_or_exit */ fsg_unbind(struct usb_gadget *gadget)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Free the data buffers */
|
/* Free the data buffers */
|
||||||
for (i = 0; i < FSG_NUM_BUFFERS; ++i)
|
for (i = 0; i < fsg_num_buffers; ++i)
|
||||||
kfree(fsg->buffhds[i].buf);
|
kfree(fsg->buffhds[i].buf);
|
||||||
|
|
||||||
/* Free the request and buffer for endpoint 0 */
|
/* Free the request and buffer for endpoint 0 */
|
||||||
@ -3445,7 +3446,7 @@ static int __init fsg_bind(struct usb_gadget *gadget)
|
|||||||
req->complete = ep0_complete;
|
req->complete = ep0_complete;
|
||||||
|
|
||||||
/* Allocate the data buffers */
|
/* Allocate the data buffers */
|
||||||
for (i = 0; i < FSG_NUM_BUFFERS; ++i) {
|
for (i = 0; i < fsg_num_buffers; ++i) {
|
||||||
struct fsg_buffhd *bh = &fsg->buffhds[i];
|
struct fsg_buffhd *bh = &fsg->buffhds[i];
|
||||||
|
|
||||||
/* Allocate for the bulk-in endpoint. We assume that
|
/* Allocate for the bulk-in endpoint. We assume that
|
||||||
@ -3456,7 +3457,7 @@ static int __init fsg_bind(struct usb_gadget *gadget)
|
|||||||
goto out;
|
goto out;
|
||||||
bh->next = bh + 1;
|
bh->next = bh + 1;
|
||||||
}
|
}
|
||||||
fsg->buffhds[FSG_NUM_BUFFERS - 1].next = &fsg->buffhds[0];
|
fsg->buffhds[fsg_num_buffers - 1].next = &fsg->buffhds[0];
|
||||||
|
|
||||||
/* This should reflect the actual gadget power source */
|
/* This should reflect the actual gadget power source */
|
||||||
usb_gadget_set_selfpowered(gadget);
|
usb_gadget_set_selfpowered(gadget);
|
||||||
@ -3572,7 +3573,9 @@ static int __init fsg_alloc(void)
|
|||||||
{
|
{
|
||||||
struct fsg_dev *fsg;
|
struct fsg_dev *fsg;
|
||||||
|
|
||||||
fsg = kzalloc(sizeof *fsg, GFP_KERNEL);
|
fsg = kzalloc(sizeof *fsg +
|
||||||
|
fsg_num_buffers * sizeof *(fsg->buffhds), GFP_KERNEL);
|
||||||
|
|
||||||
if (!fsg)
|
if (!fsg)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
spin_lock_init(&fsg->lock);
|
spin_lock_init(&fsg->lock);
|
||||||
@ -3590,6 +3593,10 @@ static int __init fsg_init(void)
|
|||||||
int rc;
|
int rc;
|
||||||
struct fsg_dev *fsg;
|
struct fsg_dev *fsg;
|
||||||
|
|
||||||
|
rc = fsg_num_buffers_validate();
|
||||||
|
if (rc != 0)
|
||||||
|
return rc;
|
||||||
|
|
||||||
if ((rc = fsg_alloc()) != 0)
|
if ((rc = fsg_alloc()) != 0)
|
||||||
return rc;
|
return rc;
|
||||||
fsg = the_fsg;
|
fsg = the_fsg;
|
||||||
|
@ -52,6 +52,12 @@
|
|||||||
* characters rather then a pointer to void.
|
* characters rather then a pointer to void.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* When USB_GADGET_DEBUG_FILES is defined the module param num_buffers
|
||||||
|
* sets the number of pipeline buffers (length of the fsg_buffhd array).
|
||||||
|
* The valid range of num_buffers is: num >= 2 && num <= 4.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
#include <linux/usb/storage.h>
|
#include <linux/usb/storage.h>
|
||||||
#include <scsi/scsi.h>
|
#include <scsi/scsi.h>
|
||||||
@ -264,8 +270,31 @@ static struct fsg_lun *fsg_lun_from_dev(struct device *dev)
|
|||||||
#define EP0_BUFSIZE 256
|
#define EP0_BUFSIZE 256
|
||||||
#define DELAYED_STATUS (EP0_BUFSIZE + 999) /* An impossibly large value */
|
#define DELAYED_STATUS (EP0_BUFSIZE + 999) /* An impossibly large value */
|
||||||
|
|
||||||
/* Number of buffers we will use. 2 is enough for double-buffering */
|
#ifdef CONFIG_USB_GADGET_DEBUG_FILES
|
||||||
#define FSG_NUM_BUFFERS 2
|
|
||||||
|
static unsigned int fsg_num_buffers = CONFIG_USB_GADGET_STORAGE_NUM_BUFFERS;
|
||||||
|
module_param_named(num_buffers, fsg_num_buffers, uint, S_IRUGO);
|
||||||
|
MODULE_PARM_DESC(num_buffers, "Number of pipeline buffers");
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Number of buffers we will use.
|
||||||
|
* 2 is usually enough for good buffering pipeline
|
||||||
|
*/
|
||||||
|
#define fsg_num_buffers CONFIG_USB_GADGET_STORAGE_NUM_BUFFERS
|
||||||
|
|
||||||
|
#endif /* CONFIG_USB_DEBUG */
|
||||||
|
|
||||||
|
/* check if fsg_num_buffers is within a valid range */
|
||||||
|
static inline int fsg_num_buffers_validate(void)
|
||||||
|
{
|
||||||
|
if (fsg_num_buffers >= 2 && fsg_num_buffers <= 4)
|
||||||
|
return 0;
|
||||||
|
pr_err("fsg_num_buffers %u is out of range (%d to %d)\n",
|
||||||
|
fsg_num_buffers, 2 ,4);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
/* Default size of buffer length. */
|
/* Default size of buffer length. */
|
||||||
#define FSG_BUFLEN ((u32)16384)
|
#define FSG_BUFLEN ((u32)16384)
|
||||||
|
Loading…
Reference in New Issue
Block a user