[PATCH] usb-storage: add US_FL_MAX_SECTORS_2048 and quirk for USB Disk 2.0 (346d:5678)
From: Herman Semenoff
Date: Mon Nov 02 2026 - 22:11:40 EST
In drivers/usb/storage/scsiglue.c, the host template sets
.max_sectors = 240 (120 KB) for USB Mass Storage devices. While USB3
devices are granted up to 2048 sectors (1024 KB), USB 2.0 devices
unconditionally inherit the conservative 240 sectors limit dating back
to early IDE bridge quirks.
For modern high-speed USB 2.0 flash drives such as 346d:5678
(USB Disk 2.0), this 120 KB ceiling unnecessarily fragments I/O
requests. For example, a 1 MB sequential transfer is broken into 9
separate SCSI commands and URBs, incurring excessive interrupt overhead
and preventing flash controllers from utilizing larger erase/page write
buffers.
Introduce the US_FL_MAX_SECTORS_2048 quirk flag (and quirk character 'z')
which allows setting max_hw_sectors to 2048 (1024 KB) on verified
capable devices. Add an unusual_devs entry for ID 346d:5678.
Non-quirked USB 2.0 devices continue to use their default 240 sectors
limit, and USB 3.0 devices continue to receive 2048 sectors via their
speed check, ensuring no performance impact or regression on other
devices.
Im tested on very common cheap USB 2.0 128GB (chipset FC2279 BA7 - price 4$)
Benchmark fio results on actual device (346d:5678) on Linux kernel:
- Sequential write (1M bs): 10.2 MiB/s -> 13.2 MiB/s (+29.4% improvement)
- Sequential read (1M bs): 24.4 MiB/s -> 26.4 MiB/s (+8.2% improvement)
- Average write latency: 97.8 ms -> 75.9 ms (-22.4% reduction)
- I/O requests / interrupts: 556 -> 60 (~89% reduction)
- 4K random read / write: unaffected, zero regression
Signed-off-by: Herman Semenoff <GermanAizek@xxxxxxxxxxxxx>
Signed-off-by: Herman Semenoff <GermanAizek@xxxxxxxxx>
---
drivers/usb/storage/scsiglue.c | 6 ++++--
drivers/usb/storage/unusual_devs.h | 7 +++++++
drivers/usb/storage/usb.c | 6 +++++-
include/linux/usb_usual.h | 2 ++
4 files changed, 18 insertions(+), 3 deletions(-)
diff --git a/drivers/usb/storage/scsiglue.c b/drivers/usb/storage/scsiglue.c
index 97de28e85562..2a732e6354e5 100644
--- a/drivers/usb/storage/scsiglue.c
+++ b/drivers/usb/storage/scsiglue.c
@@ -111,10 +111,12 @@ static int sdev_configure(struct scsi_device
*sdev, struct queue_limits *lim)
* let the queue segment size sort out the real limit.
*/
lim->max_hw_sectors = 0x7FFFFF;
- } else if (us->pusb_dev->speed >= USB_SPEED_SUPER) {
+ } else if (us->pusb_dev->speed >= USB_SPEED_SUPER ||
+ (us->fflags & US_FL_MAX_SECTORS_2048)) {
/*
* USB3 devices will be limited to 2048 sectors. This gives us
- * better throughput on most devices.
+ * better throughput on most devices. High-speed devices with
+ * the MAX_SECTORS_2048 quirk also use 2048 sectors.
*/
lim->max_hw_sectors = 2048;
}
diff --git a/drivers/usb/storage/unusual_devs.h
b/drivers/usb/storage/unusual_devs.h
index ac22fa318734..b3ff758d4e9c 100644
--- a/drivers/usb/storage/unusual_devs.h
+++ b/drivers/usb/storage/unusual_devs.h
@@ -2418,6 +2418,13 @@ UNUSUAL_DEV( 0x3340, 0xffff, 0x0000, 0x0000,
USB_SC_DEVICE,USB_PR_DEVICE,NULL,
US_FL_MAX_SECTORS_64 ),
+/* Reported by Herman Semenoff <GermanAizek@xxxxxxxxxxxxx>
<GermanAizek@xxxxxxxxx> */
+UNUSUAL_DEV(0x346d, 0x5678, 0x0000, 0x9999,
+ "USB",
+ "Disk 2.0",
+ USB_SC_DEVICE, USB_PR_DEVICE, NULL,
+ US_FL_MAX_SECTORS_2048),
+
/* Reported by Cyril Roelandt <tipecaml@xxxxxxxxx> */
UNUSUAL_DEV( 0x357d, 0x7788, 0x0114, 0x0114,
"JMicron",
diff --git a/drivers/usb/storage/usb.c b/drivers/usb/storage/usb.c
index 823a4b2e9033..2577db523208 100644
--- a/drivers/usb/storage/usb.c
+++ b/drivers/usb/storage/usb.c
@@ -570,7 +570,8 @@ void usb_stor_adjust_quirks(struct usb_device
*udev, u64 *fflags)
US_FL_INITIAL_READ10 | US_FL_WRITE_CACHE |
US_FL_NO_ATA_1X | US_FL_NO_REPORT_OPCODES |
US_FL_MAX_SECTORS_240 | US_FL_NO_REPORT_LUNS |
- US_FL_ALWAYS_SYNC | US_FL_NO_SAME);
+ US_FL_ALWAYS_SYNC | US_FL_NO_SAME |
+ US_FL_MAX_SECTORS_2048);
p = quirks;
while (*p) {
@@ -659,6 +660,9 @@ void usb_stor_adjust_quirks(struct usb_device
*udev, u64 *fflags)
case 'y':
f |= US_FL_ALWAYS_SYNC;
break;
+ case 'z':
+ f |= US_FL_MAX_SECTORS_2048;
+ break;
/* Ignore unrecognized flag characters */
}
}
diff --git a/include/linux/usb_usual.h b/include/linux/usb_usual.h
index 712363c7a2e8..cd246a42e603 100644
--- a/include/linux/usb_usual.h
+++ b/include/linux/usb_usual.h
@@ -88,6 +88,8 @@
/* Cannot handle WRITE_SAME */ \
US_FLAG(SENSE_AFTER_SYNC, 0x80000000) \
/* Do REQUEST_SENSE after SYNCHRONIZE_CACHE */ \
+ US_FLAG(MAX_SECTORS_2048, 0x0000000100000000ULL) \
+ /* Sets max_sectors to 2048 */ \
#define US_FLAG(name, value) US_FL_##name = value ,
enum { US_DO_ALL_FLAGS };
--
2.55.0
base-commit: af5f12805e5cefa4fe68d6127c7e1fb78cd5535c