Tune bdflush parameters for USB - write to disk within 2 seconds.
[tomato.git] / release / src / router / rc / usb.c
blob3eee2313a66a5cd4c7ae491cc7a063b38106d2ed
1 /*
3 USB Support
5 */
6 #include "rc.h"
8 #include <sys/types.h>
9 #include <unistd.h>
10 #include <stdio.h>
11 #include <stdlib.h>
12 #include <arpa/inet.h>
13 #include <time.h>
14 #include <sys/time.h>
15 #include <errno.h>
17 #include <sys/mount.h>
18 #include <mntent.h>
19 #include <dirent.h>
20 #include <sys/file.h>
23 void usb_lock_init(void)
25 simple_unlock("usbhp");
28 void usb_lock(void)
30 /* serialize using breakable file lock */
31 simple_lock("usbhp");
34 void usb_unlock(void)
36 simple_unlock("usbhp");
40 /* Adjust bdflush parameters.
41 * Do this here, because Tomato doesn't have the sysctl command.
42 * With these values, a disk block should be written to disk within 2 seconds.
44 #if 0
45 #define SET_PARM(n) (n * 2 | 1)
47 void tune_bdflush()
49 bdflush(SET_PARM(5), 100);
50 bdflush(SET_PARM(6), 100);
51 bdflush(SET_PARM(8), 0);
53 #else
54 /* Store values in nvram for customization */
55 void tune_bdflush(void)
57 unsigned int v[9];
58 const char *p;
60 p = nvram_safe_get("usb_bdflush");
61 // nvram default: 30 500 0 0 100 100 60 0 0
62 if (sscanf(p, "%u%u%u%u%u%u%u%u%u",
63 &v[0], &v[1], &v[2], &v[3], &v[4], &v[5], &v[6], &v[7], &v[8]) == 9) { // lightly verify
64 f_write_string("/proc/sys/vm/bdflush", p, 0, 0);
67 #endif
70 void start_usb(void)
72 _dprintf("%s\n", __FUNCTION__);
73 tune_bdflush();
75 if (nvram_match("usb_enable", "1")) {
76 // led(LED_AOSS, LED_ON);
77 modprobe("usbcore");
79 /* if enabled, force USB2 before USB1.1 */
80 if (nvram_match("usb_usb2", "1")) {
81 modprobe("ehci-hcd");
84 if (nvram_match("usb_uhci", "1")) {
85 modprobe("usb-uhci");
88 if (nvram_match("usb_ohci", "1")) {
89 modprobe("usb-ohci");
92 /* mount usb device filesystem */
93 mount("usbdevfs", "/proc/bus/usb", "usbdevfs", MS_MGC_VAL, NULL);
95 if (nvram_match("usb_storage", "1")) {
96 modprobe("scsi_mod");
97 modprobe("sd_mod");
98 modprobe("usb-storage");
100 if (nvram_match("usb_fs_ext3", "1")) {
101 modprobe("ext2");
102 modprobe("jbd");
103 modprobe("ext3");
106 if (nvram_match("usb_fs_fat", "1")) {
107 modprobe("fat");
108 modprobe("vfat");
112 if (nvram_match("usb_printer", "1")) {
113 modprobe("printer");
114 // start printer server
115 xstart("p910nd",
116 nvram_match("usb_printer_bidirect", "1") ? "-b" : "", //bidirectional
117 "-f", "/dev/usb/lp0", // device
118 "0" // listen port
120 symlink("/dev/usb/lp0", "/dev/printers/0");
123 else {
124 // led(LED_AOSS, LED_OFF);
128 void stop_usb(void)
130 // Only stop printing service here, since there might be mounted USB partitions
131 int i;
132 char s[32];
133 char pid[] = "/var/run/p9100d.pid";
135 // only find and kill the printer server we started (port 0)
136 if (f_read_string(pid, s, sizeof(s)) > 0) {
137 if ((i = atoi(s)) > 1) {
138 kill(i, SIGTERM);
139 sleep(1);
140 unlink(pid);
144 modprobe_r("printer");
148 char *detect_fs_type(char *device)
150 int fd;
151 unsigned char buf[4096];
153 if ((fd = open(device, O_RDONLY)) < 0)
154 return NULL;
156 if (read(fd, buf, sizeof(buf)) != sizeof(buf))
158 close(fd);
159 return NULL;
162 close(fd);
164 /* first check for mbr */
165 if (*device && device[strlen(device) - 1] > '9' &&
166 buf[510] == 0x55 && buf[511] == 0xAA && /* signature */
167 ((buf[0x1be] | buf[0x1ce] | buf[0x1de] | buf[0x1ee]) & 0x7f) == 0) /* boot flags */
169 return "mbr";
171 /* detect swap */
172 else if (memcmp(buf + 4086, "SWAPSPACE2", 10) == 0 ||
173 memcmp(buf + 4086, "SWAP-SPACE", 10) == 0)
175 return "swap";
177 /* detect ext2/3 */
178 else if (buf[0x438] == 0x53 && buf[0x439] == 0xEF)
180 return ((buf[0x460] & 0x0008 /* JOURNAL_DEV */) != 0 ||
181 (buf[0x45c] & 0x0004 /* HAS_JOURNAL */) != 0) ? "ext3" : "ext2";
183 /* detect ntfs */
184 else if (buf[510] == 0x55 && buf[511] == 0xAA && /* signature */
185 memcmp(buf + 3, "NTFS ", 8) == 0)
187 return "ntfs";
189 /* detect vfat */
190 else if (buf[510] == 0x55 && buf[511] == 0xAA && /* signature */
191 buf[11] == 0 && buf[12] >= 1 && buf[12] <= 8 /* sector size 512 - 4096 */ &&
192 buf[13] != 0 && (buf[13] & (buf[13] - 1)) == 0) /* sectors per cluster */
194 return "vfat";
197 return NULL;
201 #define MOUNT_VAL_FAIL 0
202 #define MOUNT_VAL_RONLY 1
203 #define MOUNT_VAL_RW 2
204 #define MOUNT_VAL_EXIST 3
206 int mount_r(char *mnt_dev, char *mnt_dir, char *type)
208 struct mntent *mnt;
209 int ret;
210 char options[40];
211 char flagfn[128];
212 int dir_made;
214 if ((mnt = findmntent(mnt_dev))) {
215 syslog(LOG_INFO, "USB partition at %s already mounted on %s",
216 mnt_dev, mnt->mnt_dir);
217 return MOUNT_VAL_EXIST;
220 options[0] = 0;
222 if (type)
224 unsigned long flags = MS_NOATIME | MS_NODEV;
226 if (strcmp(type, "swap") == 0 || strcmp(type, "mbr") == 0) {
227 /* not a mountable partition */
228 flags = 0;
230 if (strcmp(type, "ext2") == 0 || strcmp(type, "ext3") == 0) {
232 #ifdef TCONFIG_SAMBASRV
233 else if (strcmp(type, "vfat") == 0) {
234 if (nvram_invmatch("smbd_cset", ""))
235 sprintf(options, "iocharset=%s%s",
236 isdigit(nvram_get("smbd_cset")[0]) ? "cp" : "",
237 nvram_get("smbd_cset"));
238 if (nvram_invmatch("smbd_cpage", "")) {
239 char *cp = nvram_get("smbd_cpage");
240 sprintf(options + strlen(options), ",codepage=%s" + (options[0] ? 0 : 1), cp);
241 sprintf(flagfn, "nls_cp%s", cp);
243 cp = nvram_get("smbd_nlsmod");
244 if ((cp) && (*cp != 0) && (strcmp(cp, flagfn) != 0))
245 modprobe_r(cp);
247 modprobe(flagfn);
248 nvram_set("smbd_nlsmod", flagfn);
251 #endif
252 else if (strcmp(type, "ntfs") == 0)
254 flags = MS_RDONLY;
255 #ifdef TCONFIG_SAMBASRV
256 if (nvram_invmatch("smbd_cset", ""))
257 sprintf(options, "iocharset=%s%s",
258 isdigit(nvram_get("smbd_cset")[0]) ? "cp" : "",
259 nvram_get("smbd_cset"));
260 #endif
263 if (flags) {
264 if ((dir_made = mkdir_if_none(mnt_dir))) {
265 /* Create the flag file for remove the directory on dismount. */
266 sprintf(flagfn, "%s/.autocreated-dir", mnt_dir);
267 f_write(flagfn, NULL, 0, 0, 0);
270 ret = mount(mnt_dev, mnt_dir, type, flags, options[0] ? options : "");
271 if (ret != 0) /* give it another try - guess fs */
272 ret = eval("mount", "-o", "noatime", mnt_dev, mnt_dir);
274 if (ret == 0) {
275 syslog(LOG_INFO, "USB %s%s fs at %s mounted on %s",
276 type, (flags & MS_RDONLY) ? " (ro)" : "", mnt_dev, mnt_dir);
277 return (flags & MS_RDONLY) ? MOUNT_VAL_RONLY : MOUNT_VAL_RW;
280 if (dir_made) {
281 unlink(flagfn);
282 rmdir(mnt_dir);
286 return MOUNT_VAL_FAIL;
290 /* Check if the UFD is still connected because the links created in /dev/discs
291 * are not removed when the UFD is unplugged.
292 * The file to read is: /proc/scsi/usb-storage-#/#, where # is the host no.
293 * We are looking for "Attached: Yes".
295 static int usb_ufd_connected(int host_no)
297 char proc_file[128], line[256];
298 FILE *fp;
300 sprintf(proc_file, "%s/%s-%d/%d", PROC_SCSI_ROOT, USB_STORAGE, host_no, host_no);
301 fp = fopen(proc_file, "r");
303 if (!fp) {
304 /* try the way it's implemented in newer kernels: /proc/scsi/usb-storage/[host] */
305 sprintf(proc_file, "%s/%s/%d", PROC_SCSI_ROOT, USB_STORAGE, host_no);
306 fp = fopen(proc_file, "r");
309 if (fp) {
310 while (fgets(line, sizeof(line), fp) != NULL) {
311 if (strstr(line, "Attached: Yes")) {
312 fclose(fp);
313 return 1;
316 fclose(fp);
319 return 0;
323 #ifndef MNT_DETACH
324 #define MNT_DETACH 0x00000002 /* from linux/fs.h - just detach from the tree */
325 #endif
327 /* Unmount this partition.
328 * If the special flagfile is now revealed, delete it and [attempt to] delete the directory.
330 int umount_partition(char *dev_name, int host_num, int disc_num, int part_num, uint flags)
332 struct mntent *mnt;
333 int ret = 1, count;
334 char flagfn[128];
336 if (flags & EFH_HUNKNOWN) {
337 /* EFH_HUNKNOWN flag is passed if the host was unknown.
338 * Only unmount disconnected drives in this case.
340 if (usb_ufd_connected(host_num))
341 return(0);
344 mnt = findmntent(dev_name);
345 if (mnt) {
346 sync(); /* This won't matter if the device is unplugged, though. */
347 sprintf(flagfn, "%s/.autocreated-dir", mnt->mnt_dir);
348 count = 0;
349 while ((ret = umount(mnt->mnt_dir)) && (count < 2)) {
350 count++;
351 sleep(1);
354 if (!ret)
355 syslog(LOG_INFO, "USB partition unmounted from %s", mnt->mnt_dir);
357 if (ret && ((flags & EFH_USER) == 0)) {
358 /* Make one more try to do a lazy unmount unless it's an unmount
359 * request from the Web GUI.
360 * MNT_DETACH will expose the underlying mountpoint directory to all
361 * except whatever has cd'ed to the mountpoint (thereby making it busy).
362 * So the unmount can't actually fail. It disappears from the ken of
363 * everyone else immediately, and from the ken of whomever is keeping it
364 * busy until they move away from it. And then it disappears for real.
366 ret = umount2(mnt->mnt_dir, MNT_DETACH);
367 syslog(LOG_INFO, "USB partition busy - will unmount ASAP from %s", mnt->mnt_dir);
370 if (!ret) {
371 if ((unlink(flagfn) == 0)) {
372 // Only delete the directory if it was auto-created
373 rmdir(mnt->mnt_dir);
377 return(!ret);
381 /* Mount this partition on this disc.
382 * If the device is already mounted on any mountpoint, don't mount it again.
383 * If this is a swap partition, try swapon -a.
384 * If this is a regular partition, try mount -a.
386 * Before we mount any partitions:
387 * If the type is swap and /etc/fstab exists, do "swapon -a"
388 * If /etc/fstab exists, do "mount -a".
389 * We delay invoking mount because mount will probe all the partitions
390 * to read the labels, and we don't want it to do that early on.
391 * We don't invoke swapon until we actually find a swap partition.
393 int mount_partition(char *dev_name, int host_num, int disc_num, int part_num, uint flags)
395 char the_label[128], mountpoint[128];
396 int ret = MOUNT_VAL_FAIL;
397 char *type;
398 char *argv[] = { NULL, "-a", NULL };
400 if ((type = detect_fs_type(dev_name)) == NULL)
401 return(0);
403 if (f_exists("/etc/fstab")) {
404 if (strcmp(type, "swap") == 0) {
405 argv[0] = "swapon";
406 _eval(argv, NULL, 0, NULL);
407 return(0);
409 argv[0] = "mount";
410 _eval(argv, NULL, 0, NULL);
413 if (find_label(dev_name, the_label)) {
414 sprintf(mountpoint, "%s/%s", MOUNT_ROOT, the_label);
415 if ((ret = mount_r(dev_name, mountpoint, type)))
416 return(ret != MOUNT_VAL_EXIST);
419 /* Can't mount to /mnt/LABEL, so try mounting to /mnt/discDN_PN */
420 sprintf(mountpoint, "%s/disc%d_%d", MOUNT_ROOT, disc_num, part_num);
421 ret = mount_r(dev_name, mountpoint, type);
422 return(ret != MOUNT_VAL_FAIL && ret != MOUNT_VAL_EXIST);
426 /* Mount or unmount all partitions on this controller.
427 * Parameter: action_add:
428 * 0 = unmount
429 * >0 = mount only if automount config option is enabled.
430 * <0 = mount regardless of config option.
432 void hotplug_usb_storage_device(int host_no, int action_add, uint flags)
434 if (!nvram_match("usb_enable", "1"))
435 return;
436 _dprintf("%s: host %d action: %d\n", __FUNCTION__, host_no, action_add);
438 if (action_add) {
439 if (nvram_match("usb_storage", "1") && (nvram_match("usb_automount", "1") || action_add < 0)) {
440 exec_for_host(host_no, 0x01, flags, mount_partition);
441 restart_nas_services(1); // restart all NAS applications
442 run_nvscript("script_usbmount", NULL, 3);
445 else {
446 if (nvram_match("usb_storage", "1") || ((flags & EFH_USER) == 0)) {
447 /* When unplugged, unmount the device even if
448 * usb storage is disabled in the GUI.
450 if (flags & EFH_USER) {
451 /* Unmount from Web. Run user pre-unmount script if any.
453 run_nvscript("script_usbumount", NULL, 3);
455 /* Kill all NAS applications here
456 * so they are not keeping the device busy.
458 restart_nas_services(0);
459 exec_for_host(host_no, 0x02, flags, umount_partition);
460 /* Restart NAS applications */
461 restart_nas_services(1);
467 /* This gets called at reboot or upgrade. The system is stopping. */
468 void remove_storage_main(void)
470 if (nvram_match("usb_enable", "1") && nvram_match("usb_storage", "1")) {
471 if (nvram_match("usb_automount", "1")) {
472 // run pre-unmount script if any
473 run_nvscript("script_usbumount", NULL, 3);
476 restart_nas_services(0);
477 /* Unmount all partitions */
478 exec_for_host(-1, 0x02, 0, umount_partition);
482 /******* KLUDGE to try to workaround a kernel concurrency bug. *******/
484 * There is no reason that we should have to serialize this code, but
485 * we have to. Otherwise we will occasionally lock up when
486 * we repeatedly plug and unplug a hub with several USB storage devices
487 * connected to it. This is probably be a bug in the kernel. Like some
488 * critical data structure that isn't protected by a lock. There's a
489 * lot of kernel threads that are doing things to the USB device.
491 * Oh well-----serializing this code seems to avoid the problem.
493 * Notes on testing the position of the lock.
494 * Spawning 5 hotplug threads. 2 for hub, 3 scsi hosts.
495 * 6 total partitions on 3 hosts. (1 & 2 & 3)
496 * 1st for add, 2nd for remove, loop 20+ times.
497 * No /etc/fstab.
498 * "fails" = router locks up
500 * -- around everything: works
501 * -- around the (*func) call fails
502 * -- around all of exec_for_host works
503 * -- Around the "when to update" fails
504 * -- The when-to-update's & func code works
505 * -- Around the /proc/parts code fails
507 * It appears to crash when we try to do the mount (?? maybe BLKRRPART) of one
508 * disk at the same time as the kernel is recognizing/processing another disk.
509 * This can happen on bootup, or when a daisy-chained hub is plugged in.
510 * And other times, when mounting lots of disks at once via the admin
511 * interface.
513 * The problem isn't _this_ drive. The problem is another drive that the
514 * kernel is recognizing/processing (in another thread) _after_ this drive.
515 * It we ask the kernel to do something with _this_ drive at the same time it is
516 * working on _that_ later drive, then it tends to crash.
517 * The work-around is to stall our processing of _this_ drive until after
518 * the kernel has finished its work on _that_ drive. Kinda hard for us to do,
519 * since we don't yet know that _that_ drive even exists!
520 * I've tried all sorts of things to try to detect/avoid the problem.
521 * Thw worst time is when a hub & discs are first detected (bootup or initial
522 * plugin. Because the kernel automatically reads the partition table as the
523 * disks are detected.
527 /* Get the size. It'll change as USB devices are added, detected, & removed. */
528 unsigned int get_size(int host)
530 char bfr[1024];
531 int fd;
532 int i = -1;
533 unsigned int total = 0;
535 fd = open("/proc/partitions", O_RDONLY);
536 if (fd >= 0) {
537 while ((i = read(fd, bfr, sizeof(bfr))) > 0) {
538 total += i;
540 close(fd);
542 //syslog(LOG_INFO, "**** Size: %4d HN: %5i: %d errno: %d\n", total, host, i, errno);
543 return(total);
547 /* Sleep for minimum of X seconds, waiting for the known partition
548 * information to stabilize. Each time we find that the
549 * partition setup has changed, extend the time.
550 * The part info will change whenever new partitions are discovered
551 * or old ones are deleted.
552 * This will happen with automatically by the kernel, or
553 * explicitly by a user program doing an ioctl BLKRRPART.
555 void wait_for_stabilize(int tm, int host_num)
557 int n = get_size(host_num);
558 int m;
560 while (--tm > 0) {
561 sleep(1);
562 m = get_size(host_num);
563 if (m != n)
564 ++tm;
565 n = m;
570 /* Plugging or removing usb device
572 * On an occurrance, multiple hotplug events may be fired off.
573 * For example, if a hub is plugged or unplugged, an event
574 * will be generated for everything downstream of it, plus one for
575 * the hub itself. These are fired off simultaneously, not serially.
576 * This means that many many hotplug processes will be running at
577 * the same time.
579 * The hotplug event generated by the kernel gives us several pieces
580 * of information:
581 * PRODUCT is vendorid/productid/rev#.
582 * DEVICE is /proc/bus/usb/bus#/dev#
583 * ACTION is add or remove
584 * SCSI_HOST is the host (controller) number (this relies on the custom kernel patch)
586 * Note that when we get a hotplug add event, the USB susbsystem may
587 * or may not have yet tried to read the partition table of the
588 * device. For a new controller that has never been seen before,
589 * generally yes. For a re-plug of a controller that has been seen
590 * before, generally no.
592 * On a remove, the partition info has not yet been expunged. The
593 * partitions show up as /dev/discs/disc#/part#, and /proc/partitions.
594 * It appears that doing a "stat" for a non-existant partition will
595 * causes the kernel to re-validate the device and update the
596 * partition table info. However, it won't re-validate if the disc is
597 * mounted--you'll get a "Device busy for revalidation (usage=%d)" in
598 * syslog.
600 * The $INTERFACE is "class/subclass/protocol"
601 * Some interesting classes:
602 * 8 = mass storage
603 * 7 = printer
604 * 3 = HID. 3/1/2 = mouse.
605 * 6 = still image (6/1/1 = Digital camera Camera)
606 * 9 = Hub
608 * Observed:
609 * Hub seems to have no INTERFACE (null), and TYPE of "9/0/0"
610 * Flash disk seems to have INTERFACE of "8/6/80", and TYPE of "0/0/0"
612 * When a hub is unplugged, a hotplug event is generated for it and everything
613 * downstream from it. You cannot depend on getting these events in any
614 * particular order, since there will be many hotplug programs all fired off
615 * at almost the same time.
616 * On a remove, don't try to access the downstream devices right away, give the
617 * kernel time to finish cleaning up all the data structures, which will be
618 * in the process of being torn down.
620 * On the initial plugin, the first time the kernel usb-storage subsystem sees
621 * the host (identified by GUID), it automatically reads the partition table.
622 * On subsequent plugins, it does not.
624 * Special values for Web Administration to unmount or remount
625 * all partitions of the host:
626 * INTERFACE=TOMATO/...
627 * ACTION=add/remove
628 * SCSI_HOST=<host_no>
629 * If host_no is negative, we unmount all partions of *all* hosts.
631 void hotplug_usb(void)
633 int add;
634 int host = -1;
635 char *interface = getenv("INTERFACE");
636 char *action = getenv("ACTION");
637 char *product = getenv("PRODUCT");
638 char *scsi_host = getenv("SCSI_HOST");
640 _dprintf("USB hotplug INTERFACE=%s ACTION=%s PRODUCT=%s HOST=%s\n", interface, action, product, scsi_host);
642 if (!nvram_match("usb_enable", "1")) return;
643 if (!interface || !action || !product) /* Hubs bail out here. */
644 return;
646 if (scsi_host)
647 host = atoi(scsi_host);
649 add = (strcmp(action, "add") == 0);
650 if (add && (strncmp(interface, "TOMATO/", 7) != 0)) {
651 /* Give the kernel time to settle down. */
652 syslog(LOG_INFO, "usb-hotplug: waiting for device to settle before scanning");
653 wait_for_stabilize(4, host);
655 usb_lock();
657 if (strncmp(interface, "TOMATO/", 7) == 0) { /* web admin */
658 if (scsi_host == NULL)
659 host = atoi(product); // for backward compatibility
660 /* If host is negative, unmount all partitions of *all* hosts.
661 * This feature can be used in custom scripts as following:
663 * # INTERFACE=TOMATO/1 ACTION=remove PRODUCT=-1 SCSI_HOST=-1 hotplug usb
665 * PRODUCT is required to pass the env variables verification.
667 /* Unmount or remount all partitions of the host. */
668 hotplug_usb_storage_device(host, add ? -1 : 0, EFH_USER);
670 else if (strncmp(interface, "8/", 2) == 0) { /* usb storage */
671 if (add)
672 exec_for_host(host, 0x01, 0, (host_exec) NULL); /* so the user's hotplug script mount can work. */
673 run_nvscript("script_usbhotplug", NULL, 2);
674 hotplug_usb_storage_device(host, add, host < 0 ? EFH_HUNKNOWN : 0);
676 else { /* It's some other type of USB device, not storage. */
677 /* Do nothing. The user's hotplug script must do it all. */
678 run_nvscript("script_usbhotplug", NULL, 2);
681 usb_unlock();