Commit a93917d3 authored by Andy Shevchenko's avatar Andy Shevchenko Committed by Greg Kroah-Hartman

USB: gadget: storage: optional SCSI WRITE FUA bit

MS Windows mounts removable storage in "Removal optimized mode" by
default. All the writes to the media are synchronous which is achieved
by setting FUA (Force Unit Access) bit in SCSI WRITE(10,12) commands.
This prevents I/O requests aggregation in block layer dramatically
decreasing performance.

This patch brings an option to accept or ignore mentioned bit
 a) via specifying module parameter "nofua", or
 b) through sysfs entry
	/sys/devices/platform/_UDC_/gadget/gadget-lunX/nofua
	(_UDC_ is the name of the USB Device Controller driver)

Patch is based on the work that was done by Denis Karpov for Maemo 5
platform.
Signed-off-by: default avatarAndy Shevchenko <ext-andriy.shevchenko@nokia.com>
Acked-by: default avatarAlan Stern <stern@rowland.harvard.edu>
Cc: Denis Karpov <ext-denis.2.karpov@nokia.com>
Cc: Adrian Hunter <adrian.hunter@nokia.com>
Cc: David Brownell <dbrownell@users.sourceforge.net>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@suse.de>
parent 8156d158
...@@ -7,3 +7,15 @@ Description: ...@@ -7,3 +7,15 @@ Description:
0 -> resumed 0 -> resumed
(_UDC_ is the name of the USB Device Controller driver) (_UDC_ is the name of the USB Device Controller driver)
What: /sys/devices/platform/_UDC_/gadget/gadget-lunX/nofua
Date: July 2010
Contact: Andy Shevchenko <andy.shevchenko@gmail.com>
Description:
Show or set the reaction on the FUA (Force Unit Access) bit in
the SCSI WRITE(10,12) commands when a gadget in USB Mass
Storage mode.
Possible values are:
1 -> ignore the FUA flag
0 -> obey the FUA flag
...@@ -93,6 +93,8 @@ ...@@ -93,6 +93,8 @@
* removable Default false, boolean for removable media * removable Default false, boolean for removable media
* luns=N Default N = number of filenames, number of * luns=N Default N = number of filenames, number of
* LUNs to support * LUNs to support
* nofua=b[,b...] Default false, booleans for ignore FUA flag
* in SCSI WRITE(10,12) commands
* stall Default determined according to the type of * stall Default determined according to the type of
* USB device controller (usually true), * USB device controller (usually true),
* boolean to permit the driver to halt * boolean to permit the driver to halt
...@@ -112,12 +114,12 @@ ...@@ -112,12 +114,12 @@
* PAGE_CACHE_SIZE) * PAGE_CACHE_SIZE)
* *
* If CONFIG_USB_FILE_STORAGE_TEST is not set, only the "file", "ro", * If CONFIG_USB_FILE_STORAGE_TEST is not set, only the "file", "ro",
* "removable", "luns", "stall", and "cdrom" options are available; default * "removable", "luns", "nofua", "stall", and "cdrom" options are available;
* values are used for everything else. * default values are used for everything else.
* *
* The pathnames of the backing files and the ro settings are available in * The pathnames of the backing files and the ro settings are available in
* the attribute files "file" and "ro" in the lun<n> subdirectory of the * the attribute files "file", "nofua", and "ro" in the lun<n> subdirectory of
* gadget's sysfs directory. If the "removable" option is set, writing to * the gadget's sysfs directory. If the "removable" option is set, writing to
* these files will simulate ejecting/loading the medium (writing an empty * these files will simulate ejecting/loading the medium (writing an empty
* line means eject) and adjusting a write-enable tab. Changes to the ro * line means eject) and adjusting a write-enable tab. Changes to the ro
* setting are not allowed when the medium is loaded or if CD-ROM emulation * setting are not allowed when the medium is loaded or if CD-ROM emulation
...@@ -304,8 +306,10 @@ MODULE_LICENSE("Dual BSD/GPL"); ...@@ -304,8 +306,10 @@ MODULE_LICENSE("Dual BSD/GPL");
static struct { static struct {
char *file[FSG_MAX_LUNS]; char *file[FSG_MAX_LUNS];
int ro[FSG_MAX_LUNS]; int ro[FSG_MAX_LUNS];
int nofua[FSG_MAX_LUNS];
unsigned int num_filenames; unsigned int num_filenames;
unsigned int num_ros; unsigned int num_ros;
unsigned int num_nofuas;
unsigned int nluns; unsigned int nluns;
int removable; int removable;
...@@ -345,6 +349,10 @@ MODULE_PARM_DESC(file, "names of backing files or devices"); ...@@ -345,6 +349,10 @@ MODULE_PARM_DESC(file, "names of backing files or devices");
module_param_array_named(ro, mod_data.ro, bool, &mod_data.num_ros, S_IRUGO); module_param_array_named(ro, mod_data.ro, bool, &mod_data.num_ros, S_IRUGO);
MODULE_PARM_DESC(ro, "true to force read-only"); MODULE_PARM_DESC(ro, "true to force read-only");
module_param_array_named(nofua, mod_data.nofua, bool, &mod_data.num_nofuas,
S_IRUGO);
MODULE_PARM_DESC(nofua, "true to ignore SCSI WRITE(10,12) FUA bit");
module_param_named(luns, mod_data.nluns, uint, S_IRUGO); module_param_named(luns, mod_data.nluns, uint, S_IRUGO);
MODULE_PARM_DESC(luns, "number of LUNs"); MODULE_PARM_DESC(luns, "number of LUNs");
...@@ -1279,7 +1287,8 @@ static int do_write(struct fsg_dev *fsg) ...@@ -1279,7 +1287,8 @@ static int do_write(struct fsg_dev *fsg)
curlun->sense_data = SS_INVALID_FIELD_IN_CDB; curlun->sense_data = SS_INVALID_FIELD_IN_CDB;
return -EINVAL; return -EINVAL;
} }
if (fsg->cmnd[1] & 0x08) { // FUA /* FUA */
if (!curlun->nofua && (fsg->cmnd[1] & 0x08)) {
spin_lock(&curlun->filp->f_lock); spin_lock(&curlun->filp->f_lock);
curlun->filp->f_flags |= O_DSYNC; curlun->filp->f_flags |= O_DSYNC;
spin_unlock(&curlun->filp->f_lock); spin_unlock(&curlun->filp->f_lock);
...@@ -3133,6 +3142,7 @@ static int fsg_main_thread(void *fsg_) ...@@ -3133,6 +3142,7 @@ static int fsg_main_thread(void *fsg_)
/* The write permissions and store_xxx pointers are set in fsg_bind() */ /* The write permissions and store_xxx pointers are set in fsg_bind() */
static DEVICE_ATTR(ro, 0444, fsg_show_ro, NULL); static DEVICE_ATTR(ro, 0444, fsg_show_ro, NULL);
static DEVICE_ATTR(nofua, 0644, fsg_show_nofua, NULL);
static DEVICE_ATTR(file, 0444, fsg_show_file, NULL); static DEVICE_ATTR(file, 0444, fsg_show_file, NULL);
...@@ -3362,6 +3372,10 @@ static int __ref fsg_bind(struct usb_gadget *gadget) ...@@ -3362,6 +3372,10 @@ static int __ref fsg_bind(struct usb_gadget *gadget)
} }
} }
/* Only for removable media? */
dev_attr_nofua.attr.mode = 0644;
dev_attr_nofua.store = fsg_store_nofua;
/* Find out how many LUNs there should be */ /* Find out how many LUNs there should be */
i = mod_data.nluns; i = mod_data.nluns;
if (i == 0) if (i == 0)
...@@ -3387,6 +3401,7 @@ static int __ref fsg_bind(struct usb_gadget *gadget) ...@@ -3387,6 +3401,7 @@ static int __ref fsg_bind(struct usb_gadget *gadget)
curlun->ro = mod_data.cdrom || mod_data.ro[i]; curlun->ro = mod_data.cdrom || mod_data.ro[i];
curlun->initially_ro = curlun->ro; curlun->initially_ro = curlun->ro;
curlun->removable = mod_data.removable; curlun->removable = mod_data.removable;
curlun->nofua = mod_data.nofua[i];
curlun->dev.release = lun_release; curlun->dev.release = lun_release;
curlun->dev.parent = &gadget->dev; curlun->dev.parent = &gadget->dev;
curlun->dev.driver = &fsg_driver.driver; curlun->dev.driver = &fsg_driver.driver;
...@@ -3400,6 +3415,8 @@ static int __ref fsg_bind(struct usb_gadget *gadget) ...@@ -3400,6 +3415,8 @@ static int __ref fsg_bind(struct usb_gadget *gadget)
} }
if ((rc = device_create_file(&curlun->dev, if ((rc = device_create_file(&curlun->dev,
&dev_attr_ro)) != 0 || &dev_attr_ro)) != 0 ||
(rc = device_create_file(&curlun->dev,
&dev_attr_nofua)) != 0 ||
(rc = device_create_file(&curlun->dev, (rc = device_create_file(&curlun->dev,
&dev_attr_file)) != 0) { &dev_attr_file)) != 0) {
device_unregister(&curlun->dev); device_unregister(&curlun->dev);
...@@ -3525,8 +3542,8 @@ static int __ref fsg_bind(struct usb_gadget *gadget) ...@@ -3525,8 +3542,8 @@ static int __ref fsg_bind(struct usb_gadget *gadget)
if (IS_ERR(p)) if (IS_ERR(p))
p = NULL; p = NULL;
} }
LINFO(curlun, "ro=%d, file: %s\n", LINFO(curlun, "ro=%d, nofua=%d, file: %s\n",
curlun->ro, (p ? p : "(error)")); curlun->ro, curlun->nofua, (p ? p : "(error)"));
} }
} }
kfree(pathbuf); kfree(pathbuf);
......
...@@ -284,6 +284,7 @@ struct fsg_lun { ...@@ -284,6 +284,7 @@ struct fsg_lun {
unsigned int prevent_medium_removal:1; unsigned int prevent_medium_removal:1;
unsigned int registered:1; unsigned int registered:1;
unsigned int info_valid:1; unsigned int info_valid:1;
unsigned int nofua:1;
u32 sense_data; u32 sense_data;
u32 sense_data_info; u32 sense_data_info;
...@@ -714,6 +715,14 @@ static ssize_t fsg_show_ro(struct device *dev, struct device_attribute *attr, ...@@ -714,6 +715,14 @@ static ssize_t fsg_show_ro(struct device *dev, struct device_attribute *attr,
: curlun->initially_ro); : curlun->initially_ro);
} }
static ssize_t fsg_show_nofua(struct device *dev, struct device_attribute *attr,
char *buf)
{
struct fsg_lun *curlun = fsg_lun_from_dev(dev);
return sprintf(buf, "%u\n", curlun->nofua);
}
static ssize_t fsg_show_file(struct device *dev, struct device_attribute *attr, static ssize_t fsg_show_file(struct device *dev, struct device_attribute *attr,
char *buf) char *buf)
{ {
...@@ -770,6 +779,25 @@ static ssize_t fsg_store_ro(struct device *dev, struct device_attribute *attr, ...@@ -770,6 +779,25 @@ static ssize_t fsg_store_ro(struct device *dev, struct device_attribute *attr,
return rc; return rc;
} }
static ssize_t fsg_store_nofua(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct fsg_lun *curlun = fsg_lun_from_dev(dev);
unsigned long nofua;
if (strict_strtoul(buf, 2, &nofua))
return -EINVAL;
/* Sync data when switching from async mode to sync */
if (!nofua && curlun->nofua)
fsg_lun_fsync_sub(curlun);
curlun->nofua = nofua;
return count;
}
static ssize_t fsg_store_file(struct device *dev, struct device_attribute *attr, static ssize_t fsg_store_file(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count) const char *buf, size_t count)
{ {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment