minix/test/blocktest/blocktest.c
David van Moolenbroek db63c22446 work-3.2.1 squash commit
Squashed commit of the following:

commit 8fcdd34f87fe9daaac235315d3b6e1d4986ea0ed
Author: David van Moolenbroek <david@minix3.org>
Date:   Tue Nov 5 14:26:57 2013 +0100

    Fix for ramdisk generation (?)

    Change-Id: If7fd55d9a146d7d7b621da274ac0472a5b246c36

commit 5ffd24ad49bff556adceb050c7cbfddd774ed200
Author: David van Moolenbroek <david@minix3.org>
Date:   Tue Nov 5 13:16:56 2013 +0100

    Rebase fixes

    Too lazy to merge this into the appropriate patch.

    Change-Id: I66505d6a0d65330656e78ec9615a026f892b8609

commit 0240220770e6cc18fe16bd0c82e6e5e2c88a4301
Author: David van Moolenbroek <david@minix3.org>
Date:   Sun Oct 6 15:58:54 2013 +0200

    VFS: further cleanup of device code

    - all TTY-related exceptions have now been merged into the regular
      code paths, allowing non-TTY drivers to expose TTY-like devices;
    - as part of this, CTTY_MAJOR is now fully managed by VFS instead of
      being an ugly stepchild of the TTY driver;
    - device styles have become completely obsolete, support for them has
      been removed throughout the system; same for device flags, which had
      already become useless a while ago;
    - device map open/close and I/O function pointers have lost their use,
      thus finally making the VFS device code actually readable;
    - the device-unrelated pm_setsid has been moved to misc.c;
    - some other small cleanup-related changes.

    Change-Id: If90b10d1818e98a12139da3e94a15d250c9933da

commit 07d10dc91955475340005fa86adf71aa981712c8
Author: David van Moolenbroek <david@minix3.org>
Date:   Sat Oct 5 14:59:59 2013 +0200

    IS: dump number of in-use FDs for VFS

    Change-Id: If0e2092d5a8c384c31b1f44cc0591bb119c6d8de

commit ced39723e3e7c39a4a2ef516834702a36a6a26c8
Author: David van Moolenbroek <david@minix3.org>
Date:   Sat Oct 5 01:40:58 2013 +0200

    TTY: fix earlier PTY select "improvement"

    It was just plain wrong.

    Change-Id: Ieab4b4f01d9461e05e0d0ba6427a99d863d6b98d

commit a4a63c15cc5f9daf2e149b84ec930834fe24a983
Author: David van Moolenbroek <david@minix3.org>
Date:   Sat Oct 5 16:31:35 2013 +0200

    Extend dupfrom(2) into copyfd(2)

    This single function allows copying file descriptors from and to
    processes, and closing a previously copied remote file descriptor.
    This function replaces the five FD-related UDS backcalls. While it
    limits the total number of in-flight file descriptors to OPEN_MAX,
    this change greatly improves crash recovery support of UDS, since all
    in-flight file descriptors will be closed instead of keeping them
    open indefinitely (causing VFS to crash on system shutdown). With the
    new copyfd call, UDS becomes simpler, and the concept of filps is no
    longer exposed outside of VFS.

    This patch also moves the checkperms(2) stub into libminlib, thus
    fully abstracting away message details of VFS communication from UDS.

    Change-Id: Idd32ad390a566143c8ef66955e5ae2c221cff966

commit 663e0abb01990da768f1701df6366a4d3bbd67c1
Author: David van Moolenbroek <david@minix3.org>
Date:   Sat Oct 5 12:50:44 2013 +0200

    VFS: better dupfrom(2) deadlock detection

    Change-Id: I29f00075698888c7c8ca60b47ab82fba8c606f4e

commit 54c509663718cc109b0f11ab0adc196bfa82015b
Author: David van Moolenbroek <david@minix3.org>
Date:   Fri Oct 4 21:10:01 2013 +0200

    UDS: sendmsg/recvmsg fixes

    - sendmsg: the accumulation of multiple in-flight file descriptors was
      already described in the comments; now the code actually does what
      the comments say :) -- also, added robustness in case of a failure;
    - recvmsg: only create a socket rights message if there are file
      descriptors pending at all;
    - recvmsg: copy back the control message length;
    - recvmsg: use CMSG_SPACE instead of CMSG_LEN to compute sizes.

    Not sure if all of this is now working according to specification,
    but at least tmux seems to be happy with it.

    Change-Id: I8d076c14c3ff3220b7fea730e0f08f4b4254ede5

commit b0a891c329d721eb84e06ecac803789fe8197306
Author: David van Moolenbroek <david@minix3.org>
Date:   Fri Oct 4 18:41:21 2013 +0200

    UDS: add support for FIONREAD

    Change-Id: I50030012b408242a86f8c55017429acdadff49d1

commit e81d59c874d57d0d0010d3b1ace2351d01f66766
Author: David van Moolenbroek <david@minix3.org>
Date:   Fri Oct 4 18:00:42 2013 +0200

    UDS: align struct sockaddr_un with NetBSD

    Well, make a start, anyway. Our copy was missing a legacy field from
    the structure, that could very well cause applications to fail trying
    to set, clear, or check it. As a consequence, SUN_LEN now yields the
    same result as on NetBSD.

    Change-Id: I80f6aff7769be402b3bd3959f64d314509ed138c

commit 66f8ad9ac7f0e008cdf416877eeab63ed599477d
Author: David van Moolenbroek <david@minix3.org>
Date:   Fri Oct 4 17:57:42 2013 +0200

    UDS: support for nonblocking sockets

    This patch includes several other fixes, which are now tested in the
    test56 test set.

    Change-Id: I9535d5a6c072abf966252838522c5f65b353c6c2

commit 9b4a7ba7ced0b40bf650451ec7baf2935b2ccfe7
Author: David van Moolenbroek <david@minix3.org>
Date:   Fri Oct 4 16:46:18 2013 +0200

    UDS: clean up source code

    - move VFS calls to a separate source file;
    - solve a few subtle bugs, mostly in error handling;
    - simplify debug reporting code;
    - make a few definitions more independent;
    - restyle to something closer to KNF.

    Change-Id: I7b0537adfccac8b92b5cc3e78dac9f5ce3c79f03

commit 881641da1fa711d2d4bf8687cb5beb843801daf8
Author: David van Moolenbroek <david@minix3.org>
Date:   Fri Oct 4 16:29:40 2013 +0200

    UDS: split off from PFS

    Change-Id: I769cbd64aa6e5e85a797caf0f8bbb4c20e145263

commit 2baef79e569c4cebda1a3f831b8e329295e449e6
Author: David van Moolenbroek <david@minix3.org>
Date:   Wed Oct 2 19:37:00 2013 +0200

    at_wini: PCI-only now; one controller per instance

    - remove non-PCI support, since all supported platforms with at_wini
      devices also have PCI support by now;
    - correspondingly, stop using information from the BIOS altogether;
    - limit each driver instance to one controller, to be in line with
      the general MINIX3 one-instance-per-controller driver model; this
      limits the number of disks per at_wini instance to four;
    - go through the controllers by the order of their occurrence in the
      PCI table, thus removing the exception for compatibility devices;
    - let the second at_wini instance shut down silently if there is only
      one IDE controller;
    - clean up some extra code we don't need anymore, and resolve some
      WARNS=5 level warnings.

    Overall, these changes should simplify automatic loading of the right
    disk drivers at boot time in the future.

    Change-Id: Ia64d08cfbeb9916abd68c9c2941baeb87d02a806

commit f7aaabe7e0ae93b439b02d3c21c38f11bae7bbcf
Author: David van Moolenbroek <david@minix3.org>
Date:   Tue Oct 1 00:42:41 2013 +0200

    system.conf: subsystem VID/DID matching support

    - change "vid/did" to "vid:did", old form still supported for now;
    - allow "vid:did/subvid:subdid" specification in system.conf, in
      which case a device will be visible to a driver if the subsystem
      VID/DID also match.

    Change-Id: I7aef54da1b0bc81e24b5d98f1a28416f38f8b266

commit 178d896a32140285e7e9f00dba7bb4a871752f60
Author: Lionel Sambuc <lionel@minix3.org>
Date:   Tue Oct 8 19:00:16 2013 +0200

    usr.bin/man: Update

    Change-Id: I0c5d2115ba384687032f7b2af50d99dedc323b7a

commit 33e8e00217bbf38300f266dda5a494ad4e51bebd
Author: Lionel Sambuc <lionel@minix3.org>
Date:   Tue Oct 8 18:05:29 2013 +0200

    external/bsd/mdocml: Update

    Change-Id: I17b54e52e8322676d83ed4386f586f8ef3029f72

commit e28c0794f18eae77faf3055dadbe76a963242c4e
Author: Lionel Sambuc <lionel@minix3.org>
Date:   Thu Oct 3 18:26:21 2013 +0200

    Adapting build system to call MAKEDEV for /dev

     * Remove static proto.dev
     * Update releasetools/*image.sh not to use proto.dev, as well as
       minor comments cleanup
     * Add TOOL_TOPROTO

    Change-Id: If7dc16d4ebb3b0c4e859786fad25d4af000c999f

commit d26b8149f47461aac67078b8a63ddae21ecdf4f8
Author: Lionel Sambuc <lionel@minix3.org>
Date:   Thu Oct 3 13:54:24 2013 +0200

    MAKEDEV: Add mtree output, and ramdisk set.

    Change-Id: I36cb7e9451960189a33a04a5c2e3ddb19c7be75e

commit 8dc3249c2ec209689922abb306b0e3d15e6a0347
Author: David van Moolenbroek <david@minix3.org>
Date:   Sat Oct 5 20:24:25 2013 +0200

    3.3.0 sync fixes

    Keeping this version at 3.2.1 though, mainly for pkgsrc.

    Change-Id: Ic91e326623f6dbad58754152c4a632e4406f1117

commit f61a525630a33734481bfd8f0994ae33fef6c3fb
Author: David van Moolenbroek <david@minix3.org>
Date:   Wed Oct 2 13:37:49 2013 +0200

    keymaps: improve keypad slash support

    Now that the keymaps can distinguish between the regular slash key
    and the slash key on the numeric keypad, we can avoid localization
    of the latter.

    Change-Id: I20ead7d26a9baa82f5a522562524fd75d44efb42

commit 81e1e0e8e10c08459af9b7d24f51eff41225b62d
Author: Mikal Villa <mikal.villa@gmail.com>
Date:   Wed Oct 2 12:30:03 2013 +0200

    Norwegian keymap

    Change-Id: I181234afc8f1a058e92af6c1fe88979463aaff45

commit c9fc72ef1769f677ad6c932e84959f8756b32264
Author: Thomas Cort <tcort@minix3.org>
Date:   Fri Sep 6 21:40:42 2013 -0400

    Importing usr.bin/uname

    Change-Id: I4c316221e288edd839e26a2af4cb59f28bf722c1

commit 320e18fd3269219d4b68ba0df3ff8f8434a24a09
Author: Thomas Cort <tcort@minix3.org>
Date:   Fri Sep 6 21:40:31 2013 -0400

    uname: normalize release and version

    Most systems provide the full version number in the
    'release' field and the kernel version in 'version'.
    Minix used to split the full version number between
    release and version which caused problems for pkgsrc
    and other applications. This patch brings Minix's
    uname in line with other systems such as NetBSD.
    It also brings the getty banner in line with NetBSD.

    Old Minix uname:
    	sysname->Minix
    	nodename->10.0.2.15
    	release->3
    	version->2.1
    	machine->i686

    New Minix uname:
    	sysname->Minix
    	nodename->10.0.2.15
    	release->3.2.1
    	version->Minix 3.2.1 (GENERIC)
    	machine->i686

    Change-Id: I966633dfdcf2f9485966bb0d0d042afc45bbeb7d

commit 3840323699856b0ab67b3c6c3b79695acc25ffcb
Author: Lionel Sambuc <lionel@minix3.org>
Date:   Thu Sep 19 10:57:10 2013 +0200

    Replacing timer_t by netbsd's timer_t

     * Renamed struct timer to struct minix_timer
     * Renamed timer_t to minix_timer_t
     * Ensured all the code uses the minix_timer_t typedef
     * Removed ifdef around _BSD_TIMER_T
     * Removed include/timers.h and merged it into include/minix/timers.h
     * Resolved prototype conflict by renaming kernel's (re)set_timer
       to (re)set_kernel_timer.

    Change-Id: I56f0f30dfed96e1a0575d92492294cf9a06468a5

commit 23906ce75094c89071c04c6a2a2da780d5d1d9d1
Author: Lionel Sambuc <lionel@minix3.org>
Date:   Wed Oct 2 10:56:24 2013 +0200

    ARM serial driver: Comment termios_baud_rate.

    The B0-B115200 defines are flags, and not the actual speed they
    represent.

    This fixes an incoherency for B0 handling, and documents why it is
    required to call the function again after changing the speed flag.

    DFL_BAUD is set to one of the flag, so to translate it to an actual
    speed, the function calls itself again, which will always be able to
    finish without inducing another recursive call.

    Change-Id: I04ebfaefee31a88d05f0b726352d1581a966147b

commit 92db382b054926bc494cf369731e67af91033cc8
Author: David van Moolenbroek <david@minix3.org>
Date:   Tue Oct 1 23:25:01 2013 +0200

    TTY: skip /dev/log checks if console is serial

    It is unclear why /dev/log has its own open/close rules, but those
    rules conflict with serial console redirection.  This does not solve
    the root of the problem, but it puts back in place more or less the
    same workaround that was already in place before the TTY overhaul.

    Change-Id: Ib53abbc28a76c1f2b0befc8448aeed0173bc96a5

commit f1b6f94f744e82b0a4f0fbe8967a4986e079c7cd
Author: David van Moolenbroek <david@minix3.org>
Date:   Sat Oct 5 19:45:42 2013 +0200

    Corrections to keep VFS ABI-compatible

    Change-Id: Id8a53a687577c1b6f83c11c2e5fb0b8ed1cd2a08

commit 1d9f12eec8edb0ed8b9bc3cb05eb342d6e4ffb03
Author: David van Moolenbroek <david@minix3.org>
Date:   Sat Oct 5 19:35:11 2013 +0200

    Remove support for obsolete 3.2.1 ABI

    Change-Id: I76b4960bda41f55d9c42f8c99c5beae3424ca851

commit 93267010fcf5a6de5bc4ea511e1edab09c2362aa
Author: David van Moolenbroek <david@minix3.org>
Date:   Sat Aug 31 22:59:44 2013 +0200

    Fix various file system warnings

    Change-Id: Ied10498c3ae14f9f2fd06914f23239df330fa296

commit 42a4fca69efd41f11d28e5b4a4df45c3a14ea91f
Author: David van Moolenbroek <david@minix3.org>
Date:   Sat Oct 5 19:22:13 2013 +0200

    VFS/FS: replace protocol version with flag field

    The main motivation for this change is that only Loris supports
    multithreading, and Loris supports dynamic thread allocation, so the
    number of supported threads can be implemented as a bit flag (i.e.,
    either 1 or "at least as many as VFS has"). The ABI break obviates the
    need to support file system versioning at this time, and several
    other aspects are better implemented as flags as well. Other changes:

    - replace peek/bpeek test upon mount with FS flag as well;
    - mark libsffs as 64-bit file size capable;
    - remove old (3.2.1) getdents support.

    Change-Id: I313eace9c50ed816656c31cd47d969033d952a03

commit 6237d4580b2e7f4ca37222581c44f3898a9f2985
Author: Lionel Sambuc <lionel@minix3.org>
Date:   Fri Apr 19 13:16:51 2013 +0200

    usr.bin/stat Update

    Change-Id: I029160c73baab1b3465bc5397a36c55886db225b

commit 9e7f974e9fa61e0e1d9cb519a69155eb71826f9d
Author: Lionel Sambuc <lionel@minix3.org>
Date:   Fri Apr 19 09:54:51 2013 +0200

    almost aligned ioctl prototype

    Change-Id: I7f3eaa99d2a9767f71e8387cea5c7f56dcb28f99

commit b1a9440566584280bd07c6e646bb18d5b8133786
Author: Lionel Sambuc <lionel@minix3.org>
Date:   Thu Apr 18 14:42:15 2013 +0200

    32 to 64 bits fsblkcnt_t and fsfilcnt_t.

    Change-Id: I432229143c85cd178262b802a76ac606801ac59a

commit 8dea53df7843cf4a96601e019f941e7d13a878c0
Author: Lionel Sambuc <lionel@minix3.org>
Date:   Thu Apr 18 11:08:16 2013 +0200

    moving prototypes to lib.h

    Change-Id: If53d3f5ee761b10e0f3d4346a0c5b39ba7901c65

commit 0d00df150181bcc7ee20726801bbc34ebe978a29
Author: Lionel Sambuc <lionel@minix3.org>
Date:   Thu Apr 18 11:07:44 2013 +0200

    struct uucred

    Change-Id: Ia97cb6c38bb566be30d568a252ae7b76142a21dd

commit 1e355e9d217877943e01c5b8859d66258e54b3e5
Author: Lionel Sambuc <lionel@minix3.org>
Date:   Tue Apr 16 11:48:54 2013 +0200

    Alignement on netbsd types, part 1

    The following types are modified (old -> new):
     * _BSD_USECONDS_T_ int       -> unsigned int
     * __socklen_t      __int32_t -> __uint32_t
     * blksize_t        uint32_t  -> int32_t
     * rlim_t           uint32_t  -> uint64_t
    On ARM:
     * _BSD_CLOCK_T_    int       -> unsigned int
    On Intel:
     * _BSD_CLOCK_T_    int       -> unsigned long

    bin/cat is also updated in order to fix warnings.

    _BSD_TIMER_T_ has still to be aligned.

    Change-Id: I2b4fda024125a19901120546c4e22e443ba5e9d7

commit e7b39757eac1ab7aac5a59d7aa124fc316a6e01f
Author: Lionel Sambuc <lionel@minix3.org>
Date:   Fri Aug 23 20:27:27 2013 +0200

    Adapt the type used for adjtime_delta

    clock_t is currently a signed type, but in NetBSD this is not the
    case. As we plan on aligning our types we have to change this as this
    prevents negative delta from being correctly used.

    Change-Id: I9bccdee2b41626b0262471dc1900de505a1991a7

commit 5a551dbe663b22760632c5232326e4101360b330
Author: David van Moolenbroek <david@minix3.org>
Date:   Sat Oct 5 19:01:19 2013 +0200

    Butchered version of "Bumping version.."

    Change-Id: Ieb5f31fe037bcf45cc86a906f983a0ab3f636468

commit 6d601ae1d7072a8fb6cd908ff1d24ea4ca9bf1b0
Author: David van Moolenbroek <david@minix3.org>
Date:   Sat Oct 5 18:57:04 2013 +0200

    Trimmed version of "VFS: use 64-bit file..."

    Change-Id: I1b53ef05e939f6afed97f507d443a1d60ed81a21

commit 7adaa6556051b3800670399ee26d246fa9f32486
Author: David van Moolenbroek <david@minix3.org>
Date:   Sat Oct 5 18:37:39 2013 +0200

    Trimmed version of "stat.h: remove some big_ types"

    Change-Id: I6c17d6e5e9d2ab4c1bbf33e461cec4c37b870266

commit 3da9bd17116b66416e8e10ce148599b8ea723680
Author: Thomas Veerman <thomas@minix3.org>
Date:   Thu Mar 7 14:46:21 2013 +0000

    Define protocol version of {mode,ino,uid,gid}_t

    Change-Id: Ia2027749f2ce55a561d19eb895a5618505e9a2ac

commit 6ca17cd644c3b3e4f318faf7d1242855af473b6f
Author: Thomas Veerman <thomas@minix3.org>
Date:   Mon Mar 25 21:09:10 2013 +0000

    VFS-FS protocol: add versioning

    Change-Id: Ice6fbfd4b535b7435653fa08b27a3378d1cfdbf8

commit a7ff601df7d61c0925241ce10aee3a7e99343b3a
Author: David van Moolenbroek <david@minix3.org>
Date:   Sat Sep 28 14:46:21 2013 +0200

    Input infrastructure, INPUT server, PCKBD driver

    This commit separates the low-level keyboard driver from TTY, putting
    it in a separate driver (PCKBD). The commit also separates management
    of raw input devices from TTY, and puts it in a separate server
    (INPUT). All keyboard and mouse input from hardware is sent by drivers
    to the INPUT server, which either sends it to a process that has
    opened a raw input device, or otherwise forwards it to TTY for
    standard processing.

    Design by Dirk Vogt. Prototype by Uli Kastlunger.

    Additional changes made to the prototype:

    - the event communication is now based on USB HID codes; all input
      drivers have to use USB codes to describe events;
    - all TTY keymaps have been converted to USB format, with the effect
      that a single keymap covers all keys; there is no (static) escaped
      keymap anymore;
    - further keymap tweaks now allow remapping of literally all keys;
    - input device renumbering and protocol rewrite;
    - INPUT server rewrite, with added support for cancel and select;
    - PCKBD reimplementation, including PC/AT-to-USB translation;
    - support for manipulating keyboard LEDs has been added;
    - keyboard and mouse multiplexer devices have been added to INPUT,
      primarily so that an X server need only open two devices;
    - a new "libinputdriver" library abstracts away protocol details from
      input drivers, and should be used by all future input drivers;
    - both INPUT and PCKBD can be restarted;
    - TTY is now scheduled by KERNEL, so that it won't be punished for
      running a lot; without this, simply running "yes" on the console
      kills the system;
    - the KIOCBELL IOCTL has been moved to /dev/console;
    - support for the SCANCODES termios setting has been removed;
    - obsolete keymap compression has been removed;
    - the obsolete Olivetti M24 keymap has been removed.

    Change-Id: I3a672fb8c4fd566734e4b46d3994b4b7fc96d578

commit 0a3e629c0d4101a33c8b2e79c2e52d312978d34b
Author: David van Moolenbroek <david@minix3.org>
Date:   Fri Sep 27 11:56:29 2013 +0000

    TTY: allow selecting on translated minors

    Due to the existence of /dev/console and /dev/log, and the new
    "console=" setting, it is now possible that a single non-PTY object
    (e.g. serial) is accessible through two different minor numbers.  This
    poses a problem when sending late select replies (CDEV_SEL2_REPLY),
    because the object's minor number can not be used to identify the
    device.  Since selecting on such objects through translated minor
    numbers is actually required, we now save the minor number used to
    initiate the select query in order to send a late reply.

    The solution is suboptimal, as it is not possible to use two different
    minors to select on the same object at once.  In the future, there
    should be at least one select record for each minor that can be used
    with each object.

    Change-Id: I4d39681d2ffd68b4047daf933d45b7bafe3c885e

commit fe2167a148dd17e15502d98e64057239fa122dd5
Author: David van Moolenbroek <david@minix3.org>
Date:   Sat Sep 21 17:35:15 2013 +0200

    Take LOG out of the boot image

    Change-Id: Id2629776b53aae46629b04a42c15cbbacac9b949

commit 81e138bb5ba168322eb780978ed4a90fc1ab7945
Author: David van Moolenbroek <david@minix3.org>
Date:   Sat Sep 21 15:03:20 2013 +0200

    Kernel: make SIGKMESS target process list dynamic

    The set of processes to which a SIGKMESS signal is sent whenever new
    diagnostics messages are added to the kernel's message buffer, is now
    no longer hardcoded. Instead, processes can (un)register themselves
    to receive such notifications, by means of sys_diagctl().

    Change-Id: I9d6ac006a5d9bbfad2757587a068fc1ec3cc083e

commit 9afa18ec208646a9244048a1d444c474ceb47c73
Author: David van Moolenbroek <david@minix3.org>
Date:   Sat Sep 21 00:58:46 2013 +0200

    Rename SYSCTL kernel call to DIAGCTL

    Change-Id: I1b17373f01808d887dcbeab493838946fbef4ef6

commit f82f1ec4b06240e96ed8e64e4bfdf5b06d688f89
Author: David van Moolenbroek <david@minix3.org>
Date:   Wed Sep 18 20:13:59 2013 +0200

    Prevent Jenkins from breaking on old df(1)

    Change-Id: Ie8e1758bd9de4d5c95c597302f80d568058fbb68

commit 8a430b673dc97767306ddc9cb1a5367f333b7602
Author: David van Moolenbroek <david@minix3.org>
Date:   Wed Sep 18 14:09:47 2013 +0200

    Add testvnd.sh test script

    As part of this, change the "run" script to allow certain scripts to
    be run as root only.

    Change-Id: I846e41037f9d4f6c7fc0b5ea8250303a7bd72f5d

commit fd57902664f398edd9274c2ab1d87daa0b4f4584
Author: David van Moolenbroek <david@minix3.org>
Date:   Wed Sep 18 14:06:46 2013 +0200

    Import NetBSD vndconfig(8)

    The tool has been changed heavily to match our VND driver model.

    NetBSD is in the process of renaming it from vnconfig(8) to
    vndconfig(8). To keep things in sync, we have to play along.

    Change-Id: Ie86df184f03ab00573ea76b43c9caa0412e8321d

commit 6809baec22122124a04d8bf8f4f8b5fc7778cb3d
Author: David van Moolenbroek <david@minix3.org>
Date:   Wed Sep 18 14:02:17 2013 +0200

    Add VND driver, providing loopback devices

    Change-Id: I40fa695e28c67477a75383e6f1550e451afcab41

commit 4cb7efbfd17b934f914f633c8bebf5bec7934e5d
Author: David van Moolenbroek <david@minix3.org>
Date:   Wed Sep 18 13:55:15 2013 +0200

    VFS: add dupfrom(2) call

    This call copies a file descriptor from a remote process into the
    calling process. The call is for the VND driver only, and in the
    future, ACLs will prevent any other process from using this call.

    Change-Id: Ib16fdd1f1a12cb38a70d7e441dad91bc86898f6d

commit 378f893b1e710563e2ef2eee457255a6f3037885
Author: David van Moolenbroek <david@minix3.org>
Date:   Wed Sep 18 11:46:08 2013 +0200

    tests: do not skip installed shell tests

    When installed, the test scripts lose their ".sh" suffix, causing them
    to be skipped by the "run" script. With this patch, the tests are no
    longer specified with ".sh" suffix in the run script, and the suffix
    is added automatically as necessary.

    Change-Id: I0b72312e79992b9818559c6546a0e52cd95184c2

commit ecdd53c03077c3cceb29485aa56021d99485d6a5
Author: David van Moolenbroek <david@minix3.org>
Date:   Wed Sep 18 13:41:56 2013 +0200

    blocktest: prepare to be run as part of tests

    - fail SEF initialization if any of the subtests failed, so that the
      party invoking the "service up" can tell whether the test succeeded;
    - add "nocontig" option, because VM isn't particularly good at
      allocating contiguous memory;
    - add "silent" option, because it floods the console otherwise;
    - allow the device size to be smaller than the maximum transfer size;
    - install files to installed test directory.

    Change-Id: I45c818f817c11d90c5f94ae26a2fc49e36e6761e

commit 848ef711847b7adcf20dce333435802c4e1018ea
Author: David van Moolenbroek <david@minix3.org>
Date:   Wed Sep 18 13:38:36 2013 +0200

    Enable devname(3)

    There is no support for a device name database yet, so this call is
    expected to be fairly slow.

    Change-Id: I73aa5f267e2b6921b7d3bbdcc4beac463931132c

commit b84199849ad72030993bcf3f943da1509be15ded
Author: David van Moolenbroek <david@minix3.org>
Date:   Sun Sep 15 13:09:00 2013 +0200

    libbdev: be less noisy about clean driver restarts

    Change-Id: Ie02a459c9b544d361ab00bac431ef99de53b0c5f

commit 4f01352a2a6c82fc11465bc555c25e0344c5b970
Author: David van Moolenbroek <david@minix3.org>
Date:   Sat Sep 14 14:43:53 2013 +0200

    Straighten ioctl.h

    - include all ioctl subheaders, properly listing all letters;
    - change FBD's ioctl calls to use 'B' instead of 'F', in
      preparation of the VND driver.
    Change-Id: Ia718979568cc057f47cf505a89238d5b3b6695d4

commit 0ee3f3aeb3d634133a9cd8c3b81589772235c617
Author: David van Moolenbroek <david@minix3.org>
Date:   Sun Sep 15 18:55:42 2013 +0200

    VM: readd support for forgetting cached FS blocks

    Not all services involved in block I/O go through VM to access the
    blocks they need.  As a result, the blocks in VM may become stale,
    possibly causing corruption when the stale copy is restored by a
    service that does go through VM later on.  This patch restores support
    for forgetting cached blocks that belong to a particular device, and
    makes the relevant file systems use this functionality 1) when
    requested by VFS through REQ_FLUSH, and 2) upon unmount.

    Change-Id: I0758c5ed8fe4b5ba81d432595d2113175776aff8

commit c1652705b625a978ea2af169a21349352bd6575f
Author: David van Moolenbroek <david@minix3.org>
Date:   Wed Sep 11 14:50:18 2013 +0200

    filter: use libblockdriver

    Change-Id: Ifbca2482e996ddca58036d45f557165e636fb3fa

commit ad99ccdb45b897afa7573f75b1a29c4a19767870
Author: David van Moolenbroek <david@minix3.org>
Date:   Tue Sep 10 20:25:01 2013 +0200

    Rewrite character driver protocol

    As a side effect, remove the clone style, as the normal device style
    supports device cloning now.

    Change-Id: Ie82d1ef0385514a04a8faa139129a617895780b5

commit 4f91091ea26022877fd8c29094008d8072cf5e2b
Author: David van Moolenbroek <david@minix3.org>
Date:   Tue Sep 10 16:06:37 2013 +0200

    Remove support for reopening character devices

    Previously, VFS would reopen a character device after a driver crash
    if the associated file descriptor was opened with the O_REOPEN flag.
    This patch removes support for this feature. The code was complex,
    full of uncovered corner cases, and hard to test. Moreover, it did not
    actually hide the crash from user applications: they would get an
    error code to indicate that something went wrong, and have to decide
    based on the nature of the underlying device how to continue.

    - remove support for O_REOPEN, and make playwave(1) reopen its device;
    - remove support for the DEV_REOPEN protocol message;
    - remove all code in VFS related to reopening character devices;
    - no longer change VFS filp reference count and FD bitmap upon filp
      invalidation; instead, make get_filp* fail all calls on invalidated
      FDs except when obtained with the locktype VNODE_OPCL which is used
      by close_fd only;
    - remove the VFS fproc file descriptor bitmap entirely, returning to
      the situation that a FD is in use if its slot points to a filp; use
      FILP_CLOSED as single means of marking a filp as invalidated.

    Change-Id: I34f6bc69a036b3a8fc667c1f80435ff3af56558f

commit 8dfbb8fbe4f37642ac9700cc46ad0588687aa3a4
Author: David van Moolenbroek <david@minix3.org>
Date:   Tue Sep 10 12:19:08 2013 +0200

    VFS: rework device code

    - block the calling thread on character device close;
    - fully separate block and character open/close routines;
    - reuse generic open/close code for the cloning case;
    - zero all messages to drivers before filling them;
    - use appropriate types for major/minor device numbers.

    Change-Id: Ia90e6fe5688f212f835c5ee1bfca831cb249cf51

commit 9fabdb8b4edf637b47f1530bea145d68a028c319
Author: David van Moolenbroek <david@minix3.org>
Date:   Mon Sep 9 00:04:12 2013 +0200

    Make PFS backcalls regular VFS calls

    - prefix them with VFS_ as they are going to VFS;
    - give these calls normal call numbers;
    - give them their own set of message field aliases;
    - also make do_mapdriver a regular call.

    Change-Id: I2140439f288b06d699a1f65438bd8306509b259e

commit 8fbd5d36fa175e2c31ed4a239be3f785e2995aef
Author: David van Moolenbroek <david@minix3.org>
Date:   Wed Sep 11 12:48:10 2013 +0200

    TTY: use libchardriver; clean up

    - writing to a PTY master side blocks if there is not already a
      blocked reader on the slave side, and select now reflects this;
    - internally, TTY now uses a test based on "caller != NONE" rather
      than "grant != GRANT_INVALID" to identify whether a call is
      currently ongoing;
    - "offset" fields have been removed as they equal the corresponding
      "cum" fields;
    - improved variable typing and function naming here and there;
    - various other small fixes.

    Change-Id: I6b51452888942e864b4e034e8c8490576184a23e

commit 53df30e2b876c03b26f8f52abeafcf8f601e6fd2
Author: David van Moolenbroek <david@minix3.org>
Date:   Wed Sep 11 01:13:59 2013 +0200

    VFS: select(2) fixes

    - check each file descriptor's open access mode (filp_mode);
    - treat an error returned by a character driver as a select error;
    - check all filps in each set before finishing select;
    - do not copy back file descriptor sets if an error occurred;
    - remove the hardcoded list of supported character major devices,
      since all drivers should now be capable of responding properly;
    - add tests to test40 and fix its error count aggregation.

    Change-Id: I57ef58d3afb82640fc50b59c859ee4b25f02db17

commit f08c75810f26adf75a757b5b65fdfa2589cf701d
Author: David van Moolenbroek <david@minix3.org>
Date:   Wed Sep 11 01:07:28 2013 +0200

    Retire EBADIOCTL in favor of ENOTTY

    Change-Id: I6bd0e301d21ab7f2336e350e7e6e15d238c2c93d

commit 91b7de8257c92283648c796d882a9095a1f64707
Author: David van Moolenbroek <david@minix3.org>
Date:   Wed Sep 4 15:42:36 2013 +0000

    libnetsock: use libchardriver

    Change-Id: Ia5b780cad0b0c636db9bd866c7223da0d38ef6ea

commit 6a3a64d9669c5b1d6ca9acc12f02cd2f21f22d0f
Author: David van Moolenbroek <david@minix3.org>
Date:   Wed Sep 11 00:50:36 2013 +0200

    LWIP: move chardev message parsing into libnetsock

    Change-Id: Ie23fd003c9fa35811548f388c8e9b55e8d9de8d7

commit 39aa2794e04ad34aecc6dcf607c0987dad13c1ef
Author: David van Moolenbroek <david@minix3.org>
Date:   Mon Sep 9 14:53:28 2013 +0000

    INET: use libchardriver

    Change-Id: Icf8a1a5769ce0aede1cc28da99b9daf7d328182c

commit 7c0d3f508d8e840a2d30da4d3e3af696b9ad2abc
Author: David van Moolenbroek <david@minix3.org>
Date:   Tue Sep 3 02:00:20 2013 +0200

    PFS: use libchardriver; clean up

    - simplify and repair UDS request handling state machine;
    - simplify interface used between internal modules;
    - implement missing support for nonblocking I/O;
    - fix select implementation;
    - clean up global variables.

    Change-Id: Ia82c5c6f05cc3f0a498efc9a26de14b1cde6eace

commit 5598aedb79b34fd02b3b81427d683014a3303e0c
Author: David van Moolenbroek <david@minix3.org>
Date:   Tue Sep 3 01:59:20 2013 +0200

    libaudiodriver: use libchardriver

    Change-Id: I299d58d110ad14b69076276ba46c4325875c34ca

commit 2aadc0aa253427d18c1e3db597c33dd06d9c380e
Author: David van Moolenbroek <david@minix3.org>
Date:   Tue Sep 3 01:57:41 2013 +0200

    printer: use libchardriver

    Change-Id: Ifa3cabeada74c32df2613b8279c00f86e831c775

commit aeaa28b7c68aced329d9cf5b4a01af07b2f3638e
Author: David van Moolenbroek <david@minix3.org>
Date:   Tue Sep 3 01:49:38 2013 +0200

    libchardriver: full API rewrite

    The new API now covers the entire character driver protocol, while
    hiding all the message details. It should therefore be used by all
    new character drivers. All existing drivers that already made use of
    libchardriver have been changed to use the new API.

    As one of the most important API changes, support for scatter and
    gather transfers has been removed, as several key drivers already
    did not support this, and it could be supported at the safecopy
    level instead (for a future readv/writev).

    Additional changes include:

    - respond to block device open requests to avoid hanging VFS threads;
    - add support for sef_cancel.

    Change-Id: I1bab6c1cb66916c71b87aeb1db54a9bdf171fe6b

commit 6fbd39f8a65178d8fafa260dc42fde5341eb9097
Author: David van Moolenbroek <david@minix3.org>
Date:   Thu Aug 1 18:20:33 2013 +0200

    blocktest: add support for no alignment

    Some block drivers do not impose any alignment requirements, and this
    patch allows such block drivers to pass the test set. As a side effect,
    minimal support for min_write is added, but this part of blocktest is
    in need of further improvement.

commit d70dc11950991506fa08d144067de3fe86b17136
Author: David van Moolenbroek <david@minix3.org>
Date:   Wed Apr 18 00:04:28 2012 +0200

    libutil: let opendisk(3) try /dev

    If a device node is given without path, and opening the node fails
    initially, prepend "/dev/" to the node name and try opening again.
    This is more in line with NetBSD behavior.

commit 4b6bf57bc77973874b041239c5a7809448e4d857
Author: David van Moolenbroek <david@minix3.org>
Date:   Tue Sep 10 23:57:32 2013 +0200

    Block drivers: make IOCTL request unsigned long

    The block driver protocol and libblockdriver's bdr_ioctl hook are
    changed, as well as the users of this hook. Other parts of the system
    are expected to change accordingly eventually, since the ioctl(2)
    prototype has been aligned with NetBSD's.

    Change-Id: Ide46245b22cfa89ed267a38088fb0ab7696eba92

commit d74ea98bdfd90605dd5b0c57e12390dc4dba56e6
Author: David van Moolenbroek <david@minix3.org>
Date:   Sat Jul 27 00:49:49 2013 +0200

    Block protocol: add user endpoint to IOCTL request

    I/O control requests now come with the endpoint of the user process
    that initiated the ioctl(2) call. It is stored in a new BDEV_USER
    field, which is an alias for BDEV_FLAGS. The contents of this field
    are to be used only in highly specific situations. It should be
    preserved (not replaced!) by services that forward IOCTL requests,
    and may be set to NONE for service-initiated IOCTL requests.

    Change-Id: I68a01b9ce43eca00e61b985a9cf87f55ba683de4

commit 15afe7ddf85235f00e5a5d0e825d2894ac58549d
Author: David van Moolenbroek <david@minix3.org>
Date:   Sat Jul 27 00:49:49 2013 +0200

    Block protocol: use own [RW]_BIT definitions

    The original R_BIT and W_BIT definitions have nothing to do with the
    way these bits are used. Their distinct usage is more apparent when
    they have different names.

    Change-Id: Ia984457f900078b2e3502ceed565fead4e5bb965

commit c4c9435b0df31b36c11f27370475694043c5b8e0
Author: David van Moolenbroek <david@minix3.org>
Date:   Tue Sep 10 23:35:15 2013 +0200

    libblockdriver: expose BLOCKDRIVER_MAX_DEVICES

    This constant determines the range of valid device_id_t values that
    a block driver can return from the bdr_device hook: a value between
    0 and (BLOCKDRIVER_MAX_DEVICES - 1) inclusive.

    Change-Id: I80fac469e88ac13d4b869007e6f2c2f7569da433

commit 2c5bd1565b5d9b487899dee7a023a641343e65a9
Author: David van Moolenbroek <david@minix3.org>
Date:   Wed Sep 11 13:33:00 2013 +0200

    libblockdriver: various updates

    - internal structure rearrangement;
    - respond to char device open requests to avoid hanging VFS threads;
    - make drivers use designated initializers;
    - use devminor_t for all minor device numbers;
    - change bdr_other hook to take ipc_status and return nothing;
    - fix default geometry computation;
    - add support for sef_cancel.

    Change-Id: Ia063a136a3ddb2b78de36180feda870605753d70

commit b1cfc3123b03d5a8c9452ae251892593dc328357
Author: David van Moolenbroek <david@minix3.org>
Date:   Sun Sep 1 14:34:17 2013 +0200

    Block drivers: reply ENOTTY to unknown IOCTLs

    Change-Id: Ie2e82d2491d546f4dd73b009100646e249a147b5

commit b5eab0554511ace3d7fae40883be0da139b0e2ab
Author: David van Moolenbroek <david@minix3.org>
Date:   Sun Jul 28 14:03:07 2013 +0200

    Move SUB_PER_DRIVE definition into minix/drvlib.h

commit 20c8aedc6737fc935ccea0b394616851ff4fdece
Author: David van Moolenbroek <david@minix3.org>
Date:   Mon Sep 2 17:34:44 2013 +0200

    VFS: update filp_pos on chardev I/O (workaround)

    Previously, reading from or writing to a character device would not
    update the file position on the corresponding filp object.  Performing
    this update correctly is not trivial: during and after the I/O
    operation, the filp object must not be locked.  Ideally, read/write
    requests on a filp that is already involved in a read/write operation,
    should be queued.  For now, we optimistically update the file position
    at the start of the I/O; this works under the assumptions listed in
    the corresponding comment.

    Change-Id: I172a61781850423709924390ae3df1f2d1f94707

commit dbef7bae2308a2e4449b8f274898ed3b60b2add2
Author: David van Moolenbroek <david@minix3.org>
Date:   Mon Sep 2 13:45:02 2013 +0200

    I2C: change BUSC_I2C_xxx to use own protocol

    Previously it would use bits of the character driver protocol, which
    will change heavily.  In the new situation, the BUSC_I2C_xxx requests
    use a protocol more in line with the PCI protocol, with the reply code
    in m_type.

    Change-Id: I51597b3f191078c8178ce17372de123031f7a4c4

commit 7cdb9683ec718247b03cbae3c827d9834c484e91
Author: David van Moolenbroek <david@minix3.org>
Date:   Sat Aug 31 16:13:37 2013 +0200

    tests: add test77 for opening/closing PTYs

    Change-Id: I30e3418f75137aa08037fa6581ff3d4cce32a114

commit ed9ee8b9cd5a647130a81bd70af48cee4f6dca15
Author: David van Moolenbroek <david@minix3.org>
Date:   Sat Aug 31 16:08:25 2013 +0200

    TTY: fix for PTY open/close logic

    Opening and closing the master side of a pseudo terminal without
    opening the slave side would result in the pseudo terminal becoming
    permanently unavailable.  In addition, reopening the slave side
    would be possible but not allow for I/O.  Finally, attempting to
    open an in-use master would wipe its I/O state.  These issues have
    been resolved.

    Change-Id: I9235e3d9aba321803f9280b86b6b5e3646ad5ef3

commit cd14d52a99df7fb6dd7c6c48a034e4bf7c170789
Author: David van Moolenbroek <david@minix3.org>
Date:   Fri Aug 30 18:48:56 2013 +0200

    tests: remove select subdirectory

    This test set has been obsoleted by test40.

    Change-Id: I55439152824906778ad07d409dfb327ac10bea70

commit 5ab6f6e7c2f4618648f163af6bd705e5ef2fafb8
Author: David van Moolenbroek <david@minix3.org>
Date:   Fri Aug 30 18:43:23 2013 +0200

    tests: add test76 for interrupting VFS operations

    Change-Id: Ic436cac61de8c42e0c7ee2d442c647528654cde9

commit ad548d2c9f78e2f2dc2108c50b6c7ed6755a53f1
Author: David van Moolenbroek <david@minix3.org>
Date:   Wed Aug 28 13:08:16 2013 +0200

    VFS: fix interruption of blocking pipe operations

    POSIX states that when interrupted, partially successful pipe
    operations should return the partial result rather than EINTR. VFS
    previously wouldn't look at the partial result, and not clear it
    either, which would result in a panic upon the next pipe operation.

    Change-Id: Ia1eb72b4b77394051444e63a1390d49bb315eb04

commit 813198ce7b0ce171f79529e8f6c5a94ab26ce1cb
Author: David van Moolenbroek <david@minix3.org>
Date:   Fri Aug 30 00:57:16 2013 +0200

    libmthread: do not dump stack for free threads

    Change-Id: Ic438a252f5bddaf1513f554c71173e6fffb0c674

commit 53b6001312428a1ac7379718616cb640614f53e6
Author: David van Moolenbroek <david@minix3.org>
Date:   Fri Aug 30 14:00:50 2013 +0200

    VFS: worker thread model overhaul

    The main purpose of this patch is to fix handling of unpause calls
    from PM while another call is ongoing. The solution to this problem
    sparked a full revision of the threading model, consisting of a large
    number of related changes:

    - all active worker threads are now always associated with a process,
      and every process has at most one active thread working for it;
    - the process lock is always held by a process's worker thread;
    - a process can now have both normal work and postponed PM work
      associated to it;
    - timer expiry and non-postponed PM work is done from the main thread;
    - filp garbage collection is done from a thread associated with VFS;
    - reboot calls from PM are now done from a thread associated with PM;
    - the DS events handler is protected from starting multiple threads;
    - support for a system worker thread has been removed;
    - the deadlock recovery thread has been replaced by a parameter to the
      worker_start() function; the number of worker threads has
      consequently been increased by one;
    - saving and restoring of global but per-thread variables is now
      centralized in worker_suspend() and worker_resume(); err_code is now
      saved and restored in all cases;
    - the concept of jobs has been removed, and job_m_in now points to a
      message stored in the worker thread structure instead;
    - the PM lock has been removed;
    - the separate exec lock has been replaced by a lock on the VM
      process, which was already being locked for exec calls anyway;
    - PM_UNPAUSE is now processed as a postponed PM request, from a thread
      associated with the target process;
    - the FP_DROP_WORK flag has been removed, since it is no longer more
      than just an optimization and only applied to processes operating on
      a pipe when getting killed;
    - assignment to "fp" now takes place only when obtaining new work in
      the main thread or a worker thread, when resuming execution of a
      thread, and in the special case of exiting processes during reboot;
    - there are no longer special cases where the yield() call is used to
      force a thread to run.

    Change-Id: I7a97b9b95c2450454a9b5318dfa0e6150d4e6858

commit 6fbb7f4167510fb1a71fdad2b965dc2f7417aef4
Author: David van Moolenbroek <david@minix3.org>
Date:   Wed Aug 28 15:58:30 2013 +0200

    Retire ptrace(T_DUMPCORE), dumpcore(1), gcore(1)

    The T_DUMPCORE implementation was not only broken - it would currently
    produce a coredump of the tracer process rather than the traced
    process - but also deeply flawed, and fixing it would require serious
    alteration of PM's internal state machine. It should be possible to
    implement the same functionality in userland, and that is now the
    suggested way forward. For now, also remove the (identical) utilities
    using T_DUMPCORE: dumpcore(1) and gcore(1).

    Change-Id: I1d51be19c739362b8a5833de949b76382a1edbcc

commit fde44a0523a26b5bad5a0e3fc2921b1b5821d5d4
Author: David van Moolenbroek <david@minix3.org>
Date:   Fri Aug 30 13:42:51 2013 +0200

    VFS: process char driver replies from main thread

    Previously, processing of some replies coming from character drivers
    could block on locks, and therefore, such processing was done from
    threads that were associated to the character driver process. The
    hidden consequence of this was that if all threads were in use, VFS
    could drop replies coming from the driver. This patch returns VFS to
    a situation where the replies from character drivers are processed
    instantly from the main thread, by removing the situations that may
    cause VFS to block while handling those replies.

    - change the locking model for select, so that it will never block
      on any processing that happens after the select call has been set
      up, in particular processing of character driver select replies;
    - clearly mark all select routines that may never block;
    - protect against race conditions in do_select as result of the
      locking that still does happen there (as is required for pipes);
    - also handle select timers from the main thread;
    - move processing of character driver replies into device.c.

    Change-Id: I4dc8e69f265cbd178de0fbf321d35f58f067cc57

commit d5d67d293d4a7a2669eb179621a1c6d3e2796d97
Author: David van Moolenbroek <david@minix3.org>
Date:   Sun Aug 25 00:26:38 2013 +0200

    VFS: properly cancel select queries on unpause

    Change-Id: I16e71db3f5c1bcc7ba6045bc9f02b13d71dc31eb

commit d0371a4fbdaddbf7bc543a8046bb91454e09283d
Author: David van Moolenbroek <david@minix3.org>
Date:   Fri Aug 30 13:33:56 2013 +0200

    VFS: remove support for sync char driver protocol

    Change-Id: I57cc870a053b813b3a3fc45da46606ea84fe4cb1

commit 2b12ad511fabd5a1019f736221f2940bfe7e2781
Author: David van Moolenbroek <david@minix3.org>
Date:   Fri Aug 30 13:00:44 2013 +0200

    VFS: remove FP_BLOCKED_ON_DOPEN

    These days, DEV_OPEN calls to character drivers block the calling
    thread until completion or failure, and thus never return SUSPEND to
    the caller. The same already applied to BDEV_OPEN calls to block
    drivers. It has thus become impossible for a process to enter a state
    of being blocked on a device open call.

    There is currently no support for restarting device open calls to
    restarted character drivers. This support was present in the _DOPEN
    logic, but was already no longer triggering. In the future, this case
    should be handled by the thread performing the open request.

    Change-Id: I6cc1e7b4c9ed116c6ce160b315e6e060124dce00

commit 0eee377a337f5800760d2f5611d203c158ac6099
Author: David van Moolenbroek <david@minix3.org>
Date:   Fri Aug 30 12:18:27 2013 +0200

    PFS: remember request information for IOCTLs

    Not doing so caused PFS to commit protocol violations by relying on
    stale information when sending replies. This stale information always
    happened to be correct, which is why the problem went unnoticed.
    Change-Id: Ia42ca670718d6e731193cd2c34a3ff455f8a94d3

commit 3272c645aca015b6778a8fe945e014a0a5acc023
Author: David van Moolenbroek <david@minix3.org>
Date:   Fri Aug 30 11:14:03 2013 +0200

    Retire the synchronous character driver protocol

    - change all sync char drivers into async drivers;
    - retire support for the sync protocol in libchardev;
    - remove async dev style, as this is now the default;
    - remove dev_status from VFS;
    - clean up now-unused protocol messages.

    Change-Id: I6aacff712292f6b29f2ccd51bc1e7d7003723e87

commit 831d825d5a6891b79e97002e1a2d396dcb47bea6
Author: David van Moolenbroek <david@minix3.org>
Date:   Fri Aug 30 10:48:34 2013 +0200

    Sync char protocol: add nonblocking transfer flag

    The async char protocol already has this, so this patch closes the
    gap between the two protocols a bit. Support for this flag has been
    added to all sync char drivers that support CANCEL at all.

    The LOG driver was already using the asynchronous protocol, but it
    did not support the nonblocking transfer flag. This has been fixed
    as well.

    Change-Id: Ia55432c9f102765b59ad3feb45a8bd47a782c93f

commit 4f5aec7328128b9d035ff1158f0e6a0f7bd393e4
Author: David van Moolenbroek <david@minix3.org>
Date:   Sat Aug 24 12:29:39 2013 +0200

    VFS: set w_drv_sendrec only when needed

    As with w_task, this ensures that the field remains cleared if it is
    not used. Without this, worker_stop could mistakenly identify a thread
    as talking to a device driver rather than a (crashed) file server.

    Change-Id: I7d3ebed3efc3cd4f5c891f61c67a6463109b6376

commit 9bbf923154733f1b9ba52a063872ccdbaa7ff0f0
Author: Thomas Veerman <thomas@minix3.org>
Date:   Sat Aug 24 12:23:41 2013 +0200

    VFS: set w_task only when needed

    It was always set, but not always cleared, when talking to asynchronous
    drivers. This could cause erratic behavior upon a driver crash.

    Normally, a worker thread's w_task field is set when it's about to
    communicate with a driver or FS. Then upon receiving a reply we can
    do sanity checks (that the thread we want to wake up was actually
    waiting for a reply). Also, when a driver/FS crashes, we can identify
    which worker threads were talking to the crashed endpoint and handle
    the error gracefully.

    Asynchronous drivers are a bit special, though. In most cases, the
    sender of the request is not interested in the reply (the sender was
    suspended and only wants to know whether the request was successfully
    caried out or not). However, the open request is special, as the reply
    carries information needed by the sender. This is the only request
    where a worker thread actually yields and waits for the result. This is
    also the only case where we're interested in setting w_task for
    asynchronous drivers.

    Change-Id: Ia1ce2747937df376122b5e13b6a069de27fcc379

commit 2a293ca6703354cd7be8eb18147f7c4d18894132
Author: David van Moolenbroek <david@minix3.org>
Date:   Mon Aug 19 20:34:15 2013 +0200

    Import NetBSD df(1)

    Change-Id: Ia60f8b23b961e4132efece1e7e38f0d63597c13b

commit c35fc992ccc052e1f3741be0db9b676682f68bf8
Author: David van Moolenbroek <david@minix3.org>
Date:   Tue Aug 20 01:33:43 2013 +0200

    Enable getmntinfo(3)

    Change-Id: Id9d11a67cdcad331a52030127f7c7ead3c847919

commit 907b40860983a73fe22f45aa8b175b27cb7dc1d7
Author: David van Moolenbroek <david@minix3.org>
Date:   Tue Aug 20 00:49:07 2013 +0200

    test55: add tests for getvfsstat(2)

    Change-Id: Iad4567068a82bb0e780891a478ba5a06b63f1d48

commit 3297eb8d103545690e9f2cb6c63fdd1ce3452c6d
Author: David van Moolenbroek <david@minix3.org>
Date:   Tue Aug 20 01:28:23 2013 +0200

    Implement support for [f]statvfs1(2)

    The [f]statvfs(3) calls now use [f]statvfs1(2).

    Change-Id: I56c92fe1c70670f476631c4a50e47351562712c6

commit 4f576aff9f046276974c4a769427a1cb8c2470a0
Author: David van Moolenbroek <david@minix3.org>
Date:   Tue Aug 20 01:39:47 2013 +0200

    Implement support for getvfsstat(2)

    Change-Id: I4680c071b94fa855edfb6220b03cff6b20f3c1c9

commit 53485a9b35ec2e27ec0df232d8c80db1a829246b
Author: David van Moolenbroek <david@minix3.org>
Date:   Tue Aug 20 01:37:18 2013 +0200

    Redo mount(2)/umount(2) ABI

    - pass in file system type through mount(2), and return this type in
      statvfs structures as generated by [f]statvfs(2);
    - align mount flags field with NetBSD's, splitting out service flags
      which are not to be passed to VFS;
    - remove limitation of mount ABI to 16-byte labels, so that labels
      can be made larger in the future;
    - introduce new m11 message union type for mount(2) as side effect.

    Change-Id: Ia9ed4566d88af5239749c57274312be26bb61448

commit 6ebff29307cd1bacba965d1e8dfb465b6684ccb7
Author: David van Moolenbroek <david@minix3.org>
Date:   Tue Aug 20 01:35:35 2013 +0200

    Align "struct statvfs" with NetBSD

    This is a requirement for implementing calls such as getmntinfo(3).
    VFS is now responsible for filling in some of the structure's fields.

    Change-Id: I457b463c70ea2f326b9c50de954a91cfefba0065

commit 9b5871fd21d37b0533b6546c408ff4ad4ae02952
Author: David van Moolenbroek <david@minix3.org>
Date:   Tue Aug 20 00:55:49 2013 +0200

    VFS/FS: remove fstatfs(2) and REQ_FSTATFS

    The fstatfs(3) call now uses fstatvfs(2).

    Change-Id: Ic6bbb040e1a8f01aaffe1a6a809dd4c7a2eae2f6

Change-Id: I44109a83fd9155f15a8b580e9471ed1aff952673
2013-11-05 18:25:04 +01:00

2728 lines
74 KiB
C

/* Block Device Driver Test driver, by D.C. van Moolenbroek */
#include <stdlib.h>
#include <stdarg.h>
#include <minix/blockdriver.h>
#include <minix/drvlib.h>
#include <minix/ds.h>
#include <minix/optset.h>
#include <sys/ioc_disk.h>
#include <sys/mman.h>
#include <assert.h>
enum {
RESULT_OK, /* exactly as expected */
RESULT_DEATH, /* driver died */
RESULT_COMMFAIL, /* communication failed */
RESULT_BADTYPE, /* bad type in message */
RESULT_BADID, /* bad request ID in message */
RESULT_BADSTATUS, /* bad/unexpected status in message */
RESULT_TRUNC, /* request truncated unexpectedly */
RESULT_CORRUPT, /* buffer touched erroneously */
RESULT_MISSING, /* buffer left untouched erroneously */
RESULT_OVERFLOW, /* area around buffer touched */
RESULT_BADVALUE /* bad/unexpected return value */
};
typedef struct {
int type;
ssize_t value;
} result_t;
static char driver_label[32] = ""; /* driver DS label */
static dev_t driver_minor = -1; /* driver's partition minor to use */
static endpoint_t driver_endpt; /* driver endpoint */
static int may_write = FALSE; /* may we write to the device? */
static int sector_size = 512; /* size of a single disk sector */
static int min_read = 512; /* minimum total size of read req */
static int min_write = 0; /* minimum total size of write req */
static int element_size = 512; /* minimum I/O vector element size */
static int max_size = 131072; /* maximum total size of any req */
/* Note that we do not test exceeding the max_size limit, so it is safe to set
* it to a value lower than the driver supports.
*/
/* These settings are used for automated test runs. */
static int contig = TRUE; /* allocate contiguous DMA memory? */
static int silent = FALSE; /* do not produce console output? */
static struct part_geom part; /* base and size of target partition */
#define NR_OPENED 10 /* maximum number of opened devices */
static dev_t opened[NR_OPENED]; /* list of currently opened devices */
static int nr_opened = 0; /* current number of opened devices */
static int total_tests = 0; /* total number of tests performed */
static int failed_tests = 0; /* number of tests that failed */
static int failed_groups = 0; /* nr of groups that had failures */
static int group_failure; /* has this group had a failure yet? */
static int driver_deaths = 0; /* number of restarts that we saw */
/* Options supported by this driver. */
static struct optset optset_table[] = {
{ "label", OPT_STRING, driver_label, sizeof(driver_label) },
{ "minor", OPT_INT, &driver_minor, 10 },
{ "rw", OPT_BOOL, &may_write, TRUE },
{ "ro", OPT_BOOL, &may_write, FALSE },
{ "sector", OPT_INT, &sector_size, 10 },
{ "element", OPT_INT, &element_size, 10 },
{ "min_read", OPT_INT, &min_read, 10 },
{ "min_write", OPT_INT, &min_write, 10 },
{ "max", OPT_INT, &max_size, 10 },
{ "nocontig", OPT_BOOL, &contig, FALSE },
{ "silent", OPT_BOOL, &silent, TRUE },
{ NULL, 0, NULL, 0 }
};
static void output(char *fmt, ...)
{
/* Print debugging information, unless configured to be silent.
*/
va_list argp;
if (silent)
return;
va_start(argp, fmt);
vprintf(fmt, argp);
va_end(argp);
}
static void *alloc_dma_memory(size_t size)
{
/* Allocate memory that may be used for direct DMA. For most drivers,
* this means that the memory has to be physically contiguous. For some
* drivers (e.g. VND) we allow non-contiguous allocation, because VM is
* currently flaky and does not always manage to provide contiguous
* memory even when it should, thus causing needless test failures.
*/
void *ptr;
if (contig)
ptr = alloc_contig(size, 0, NULL);
else
ptr = minix_mmap(NULL, size, PROT_READ | PROT_WRITE,
MAP_PREALLOC | MAP_ANON, -1, 0);
if (ptr == MAP_FAILED)
panic("unable to allocate %d bytes of memory", size);
return ptr;
}
static void free_dma_memory(void *ptr, size_t size)
{
/* Free memory previously allocated for direct DMA. */
if (contig)
free_contig(ptr, size);
else
minix_munmap(ptr, size);
}
static int set_result(result_t *res, int type, ssize_t value)
{
/* Set the result to the given result type and with the given optional
* extra value. Return the type.
*/
res->type = type;
res->value = value;
return type;
}
static int accept_result(result_t *res, int type, ssize_t value)
{
/* If the result is of the given type and value, reset it to a success
* result. This allows for a logical OR on error codes. Return whether
* the result was indeed reset.
*/
if (res->type == type && res->value == value) {
set_result(res, RESULT_OK, 0);
return TRUE;
}
return FALSE;
}
static void got_result(result_t *res, char *desc)
{
/* Process the result of a test. Keep statistics.
*/
static int i = 0;
total_tests++;
if (res->type != RESULT_OK) {
failed_tests++;
if (group_failure == FALSE) {
failed_groups++;
group_failure = TRUE;
}
}
output("#%02d: %-38s\t[%s]\n", ++i, desc,
(res->type == RESULT_OK) ? "PASS" : "FAIL");
switch (res->type) {
case RESULT_DEATH:
output("- driver died\n");
break;
case RESULT_COMMFAIL:
output("- communication failed; sendrec returned %d\n",
res->value);
break;
case RESULT_BADTYPE:
output("- bad type %d in reply message\n", res->value);
break;
case RESULT_BADID:
output("- mismatched ID %d in reply message\n", res->value);
break;
case RESULT_BADSTATUS:
output("- bad or unexpected status %d in reply message\n",
res->value);
break;
case RESULT_TRUNC:
output("- result size not as expected (%u bytes left)\n",
res->value);
break;
case RESULT_CORRUPT:
output("- buffer has been modified erroneously\n");
break;
case RESULT_MISSING:
output("- buffer has been left untouched erroneously\n");
break;
case RESULT_OVERFLOW:
output("- area around target buffer modified\n");
break;
case RESULT_BADVALUE:
output("- bad or unexpected return value %d from call\n",
res->value);
break;
}
}
static void test_group(char *name, int exec)
{
/* Start a new group of tests.
*/
output("Test group: %s%s\n", name, exec ? "" : " (skipping)");
group_failure = FALSE;
}
static void reopen_device(dev_t minor)
{
/* Reopen a device after we were notified that the driver has died.
* Explicitly ignore any errors here; this is a feeble attempt to get
* ourselves back into business again.
*/
message m;
memset(&m, 0, sizeof(m));
m.m_type = BDEV_OPEN;
m.BDEV_MINOR = minor;
m.BDEV_ACCESS = (may_write) ? (BDEV_R_BIT | BDEV_W_BIT) : BDEV_R_BIT;
m.BDEV_ID = 0;
(void) sendrec(driver_endpt, &m);
}
static int sendrec_driver(message *m_ptr, ssize_t exp, result_t *res)
{
/* Make a call to the driver, and perform basic checks on the return
* message. Fill in the result structure, wiping out what was in there
* before. If the driver dies in the process, attempt to recover but
* fail the request.
*/
message m_orig;
endpoint_t last_endpt;
int i, r;
m_orig = *m_ptr;
r = sendrec(driver_endpt, m_ptr);
if (r == EDEADSRCDST) {
/* The driver has died. Find its new endpoint, and reopen all
* devices that we opened earlier. Then return failure.
*/
output("WARNING: driver has died, attempting to proceed\n");
driver_deaths++;
/* Keep trying until we get a new endpoint. */
last_endpt = driver_endpt;
for (;;) {
r = ds_retrieve_label_endpt(driver_label,
&driver_endpt);
if (r == OK && last_endpt != driver_endpt)
break;
micro_delay(100000);
}
for (i = 0; i < nr_opened; i++)
reopen_device(opened[i]);
return set_result(res, RESULT_DEATH, 0);
}
if (r != OK)
return set_result(res, RESULT_COMMFAIL, r);
if (m_ptr->m_type != BDEV_REPLY)
return set_result(res, RESULT_BADTYPE, m_ptr->m_type);
if (m_ptr->BDEV_ID != m_orig.BDEV_ID)
return set_result(res, RESULT_BADID, m_ptr->BDEV_ID);
if ((exp < 0 && m_ptr->BDEV_STATUS >= 0) ||
(exp >= 0 && m_ptr->BDEV_STATUS < 0))
return set_result(res, RESULT_BADSTATUS, m_ptr->BDEV_STATUS);
return set_result(res, RESULT_OK, 0);
}
static void raw_xfer(dev_t minor, u64_t pos, iovec_s_t *iovec, int nr_req,
int write, ssize_t exp, result_t *res)
{
/* Perform a transfer with a safecopy iovec already supplied.
*/
cp_grant_id_t grant;
message m;
int r;
assert(nr_req <= NR_IOREQS);
assert(!write || may_write);
if ((grant = cpf_grant_direct(driver_endpt, (vir_bytes) iovec,
sizeof(*iovec) * nr_req, CPF_READ)) == GRANT_INVALID)
panic("unable to allocate grant");
memset(&m, 0, sizeof(m));
m.m_type = write ? BDEV_SCATTER : BDEV_GATHER;
m.BDEV_MINOR = minor;
m.BDEV_POS_LO = ex64lo(pos);
m.BDEV_POS_HI = ex64hi(pos);
m.BDEV_COUNT = nr_req;
m.BDEV_GRANT = grant;
m.BDEV_ID = lrand48();
r = sendrec_driver(&m, exp, res);
if (cpf_revoke(grant) != OK)
panic("unable to revoke grant");
if (r != RESULT_OK)
return;
if (m.BDEV_STATUS == exp)
return;
if (exp < 0)
set_result(res, RESULT_BADSTATUS, m.BDEV_STATUS);
else
set_result(res, RESULT_TRUNC, exp - m.BDEV_STATUS);
}
static void vir_xfer(dev_t minor, u64_t pos, iovec_t *iovec, int nr_req,
int write, ssize_t exp, result_t *res)
{
/* Perform a transfer, creating and revoking grants for the I/O vector.
*/
iovec_s_t iov_s[NR_IOREQS];
int i;
assert(nr_req <= NR_IOREQS);
for (i = 0; i < nr_req; i++) {
iov_s[i].iov_size = iovec[i].iov_size;
if ((iov_s[i].iov_grant = cpf_grant_direct(driver_endpt,
(vir_bytes) iovec[i].iov_addr, iovec[i].iov_size,
write ? CPF_READ : CPF_WRITE)) == GRANT_INVALID)
panic("unable to allocate grant");
}
raw_xfer(minor, pos, iov_s, nr_req, write, exp, res);
for (i = 0; i < nr_req; i++) {
iovec[i].iov_size = iov_s[i].iov_size;
if (cpf_revoke(iov_s[i].iov_grant) != OK)
panic("unable to revoke grant");
}
}
static void simple_xfer(dev_t minor, u64_t pos, u8_t *buf, size_t size,
int write, ssize_t exp, result_t *res)
{
/* Perform a transfer involving a single buffer.
*/
iovec_t iov;
iov.iov_addr = (vir_bytes) buf;
iov.iov_size = size;
vir_xfer(minor, pos, &iov, 1, write, exp, res);
}
static void alloc_buf_and_grant(u8_t **ptr, cp_grant_id_t *grant,
size_t size, int perms)
{
/* Allocate a buffer suitable for DMA (i.e. contiguous) and create a
* grant for it with the requested CPF_* grant permissions.
*/
*ptr = alloc_dma_memory(size);
if ((*grant = cpf_grant_direct(driver_endpt, (vir_bytes) *ptr, size,
perms)) == GRANT_INVALID)
panic("unable to allocate grant");
}
static void free_buf_and_grant(u8_t *ptr, cp_grant_id_t grant, size_t size)
{
/* Revoke a grant and free a buffer.
*/
cpf_revoke(grant);
free_dma_memory(ptr, size);
}
static void bad_read1(void)
{
/* Test various illegal read transfer requests, part 1.
*/
message mt, m;
iovec_s_t iovt, iov;
cp_grant_id_t grant, grant2, grant3;
u8_t *buf_ptr;
vir_bytes buf_size;
result_t res;
test_group("bad read requests, part one", TRUE);
#define BUF_SIZE 4096
buf_size = BUF_SIZE;
alloc_buf_and_grant(&buf_ptr, &grant2, buf_size, CPF_WRITE);
if ((grant = cpf_grant_direct(driver_endpt, (vir_bytes) &iov,
sizeof(iov), CPF_READ)) == GRANT_INVALID)
panic("unable to allocate grant");
/* Initialize the defaults for some of the tests.
* This is a legitimate request for the first block of the partition.
*/
memset(&mt, 0, sizeof(mt));
mt.m_type = BDEV_GATHER;
mt.BDEV_MINOR = driver_minor;
mt.BDEV_POS_LO = 0L;
mt.BDEV_POS_HI = 0L;
mt.BDEV_COUNT = 1;
mt.BDEV_GRANT = grant;
mt.BDEV_ID = lrand48();
memset(&iovt, 0, sizeof(iovt));
iovt.iov_grant = grant2;
iovt.iov_size = buf_size;
/* Test normal request. */
m = mt;
iov = iovt;
sendrec_driver(&m, OK, &res);
if (res.type == RESULT_OK && m.BDEV_STATUS != (ssize_t) iov.iov_size) {
res.type = RESULT_TRUNC;
res.value = m.BDEV_STATUS;
}
got_result(&res, "normal request");
/* Test zero iovec elements. */
m = mt;
iov = iovt;
m.BDEV_COUNT = 0;
sendrec_driver(&m, EINVAL, &res);
got_result(&res, "zero iovec elements");
/* Test bad iovec grant. */
m = mt;
m.BDEV_GRANT = GRANT_INVALID;
sendrec_driver(&m, EINVAL, &res);
got_result(&res, "bad iovec grant");
/* Test revoked iovec grant. */
m = mt;
iov = iovt;
if ((grant3 = cpf_grant_direct(driver_endpt, (vir_bytes) &iov,
sizeof(iov), CPF_READ)) == GRANT_INVALID)
panic("unable to allocate grant");
cpf_revoke(grant3);
m.BDEV_GRANT = grant3;
sendrec_driver(&m, EINVAL, &res);
accept_result(&res, RESULT_BADSTATUS, EPERM);
got_result(&res, "revoked iovec grant");
/* Test normal request (final check). */
m = mt;
iov = iovt;
sendrec_driver(&m, OK, &res);
if (res.type == RESULT_OK && m.BDEV_STATUS != (ssize_t) iov.iov_size) {
res.type = RESULT_TRUNC;
res.value = m.BDEV_STATUS;
}
got_result(&res, "normal request");
/* Clean up. */
free_buf_and_grant(buf_ptr, grant2, buf_size);
cpf_revoke(grant);
}
static u32_t get_sum(u8_t *ptr, size_t size)
{
/* Compute a checksum over the given buffer.
*/
u32_t sum;
for (sum = 0; size > 0; size--, ptr++)
sum = sum ^ (sum << 5) ^ *ptr;
return sum;
}
static u32_t fill_rand(u8_t *ptr, size_t size)
{
/* Fill the given buffer with random data. Return a checksum over the
* resulting data.
*/
size_t i;
for (i = 0; i < size; i++)
ptr[i] = lrand48() % 256;
return get_sum(ptr, size);
}
static void test_sum(u8_t *ptr, size_t size, u32_t sum, int should_match,
result_t *res)
{
/* If the test succeeded so far, check whether the given buffer does
* or does not match the given checksum, and adjust the test result
* accordingly.
*/
u32_t sum2;
if (res->type != RESULT_OK)
return;
sum2 = get_sum(ptr, size);
if ((sum == sum2) != should_match) {
res->type = should_match ? RESULT_CORRUPT : RESULT_MISSING;
res->value = 0; /* not much that's useful here */
}
}
static void bad_read2(void)
{
/* Test various illegal read transfer requests, part 2.
*
* Consider allowing this test to be run twice, with different buffer
* sizes. It appears that we can make at_wini misbehave by making the
* size exceed the per-operation size (128KB ?). On the other hand, we
* then need to start checking partition sizes, possibly.
*/
u8_t *buf_ptr, *buf2_ptr, *buf3_ptr, c1, c2;
size_t buf_size, buf2_size, buf3_size;
cp_grant_id_t buf_grant, buf2_grant, buf3_grant, grant;
u32_t buf_sum, buf2_sum, buf3_sum;
iovec_s_t iov[3], iovt[3];
result_t res;
test_group("bad read requests, part two", TRUE);
buf_size = buf2_size = buf3_size = BUF_SIZE;
alloc_buf_and_grant(&buf_ptr, &buf_grant, buf_size, CPF_WRITE);
alloc_buf_and_grant(&buf2_ptr, &buf2_grant, buf2_size, CPF_WRITE);
alloc_buf_and_grant(&buf3_ptr, &buf3_grant, buf3_size, CPF_WRITE);
iovt[0].iov_grant = buf_grant;
iovt[0].iov_size = buf_size;
iovt[1].iov_grant = buf2_grant;
iovt[1].iov_size = buf2_size;
iovt[2].iov_grant = buf3_grant;
iovt[2].iov_size = buf3_size;
/* Test normal vector request. */
memcpy(iov, iovt, sizeof(iovt));
buf_sum = fill_rand(buf_ptr, buf_size);
buf2_sum = fill_rand(buf2_ptr, buf2_size);
buf3_sum = fill_rand(buf3_ptr, buf3_size);
raw_xfer(driver_minor, 0ULL, iov, 3, FALSE,
buf_size + buf2_size + buf3_size, &res);
test_sum(buf_ptr, buf_size, buf_sum, FALSE, &res);
test_sum(buf2_ptr, buf2_size, buf2_sum, FALSE, &res);
test_sum(buf3_ptr, buf3_size, buf3_sum, FALSE, &res);
got_result(&res, "normal vector request");
/* Test zero sized iovec element. */
memcpy(iov, iovt, sizeof(iovt));
iov[1].iov_size = 0;
buf_sum = fill_rand(buf_ptr, buf_size);
buf2_sum = fill_rand(buf2_ptr, buf2_size);
buf3_sum = fill_rand(buf3_ptr, buf3_size);
raw_xfer(driver_minor, 0ULL, iov, 3, FALSE, EINVAL, &res);
test_sum(buf_ptr, buf_size, buf_sum, TRUE, &res);
test_sum(buf2_ptr, buf2_size, buf2_sum, TRUE, &res);
test_sum(buf3_ptr, buf3_size, buf3_sum, TRUE, &res);
got_result(&res, "zero size in iovec element");
/* Test negative sized iovec element. */
memcpy(iov, iovt, sizeof(iovt));
iov[1].iov_size = (vir_bytes) LONG_MAX + 1;
raw_xfer(driver_minor, 0ULL, iov, 3, FALSE, EINVAL, &res);
test_sum(buf_ptr, buf_size, buf_sum, TRUE, &res);
test_sum(buf2_ptr, buf2_size, buf2_sum, TRUE, &res);
test_sum(buf3_ptr, buf3_size, buf3_sum, TRUE, &res);
got_result(&res, "negative size in iovec element");
/* Test iovec with negative total size. */
memcpy(iov, iovt, sizeof(iovt));
iov[0].iov_size = LONG_MAX / 2 - 1;
iov[1].iov_size = LONG_MAX / 2 - 1;
raw_xfer(driver_minor, 0ULL, iov, 3, FALSE, EINVAL, &res);
test_sum(buf_ptr, buf_size, buf_sum, TRUE, &res);
test_sum(buf2_ptr, buf2_size, buf2_sum, TRUE, &res);
test_sum(buf3_ptr, buf3_size, buf3_sum, TRUE, &res);
got_result(&res, "negative total size");
/* Test iovec with wrapping total size. */
memcpy(iov, iovt, sizeof(iovt));
iov[0].iov_size = LONG_MAX - 1;
iov[1].iov_size = LONG_MAX - 1;
raw_xfer(driver_minor, 0ULL, iov, 3, FALSE, EINVAL, &res);
test_sum(buf_ptr, buf_size, buf_sum, TRUE, &res);
test_sum(buf2_ptr, buf2_size, buf2_sum, TRUE, &res);
test_sum(buf3_ptr, buf3_size, buf3_sum, TRUE, &res);
got_result(&res, "wrapping total size");
/* Test word-unaligned iovec element size. */
memcpy(iov, iovt, sizeof(iovt));
iov[1].iov_size--;
buf_sum = fill_rand(buf_ptr, buf_size);
buf2_sum = fill_rand(buf2_ptr, buf2_size);
buf3_sum = fill_rand(buf3_ptr, buf3_size);
c1 = buf2_ptr[buf2_size - 1];
raw_xfer(driver_minor, 0ULL, iov, 3, FALSE, BUF_SIZE * 3 - 1,
&res);
if (accept_result(&res, RESULT_BADSTATUS, EINVAL)) {
/* Do not test the first buffer, as it may contain a partial
* result.
*/
test_sum(buf2_ptr, buf2_size, buf2_sum, TRUE, &res);
test_sum(buf3_ptr, buf3_size, buf3_sum, TRUE, &res);
} else {
test_sum(buf_ptr, buf_size, buf_sum, FALSE, &res);
test_sum(buf2_ptr, buf2_size, buf2_sum, FALSE, &res);
test_sum(buf3_ptr, buf3_size, buf3_sum, FALSE, &res);
if (c1 != buf2_ptr[buf2_size - 1])
set_result(&res, RESULT_CORRUPT, 0);
}
got_result(&res, "word-unaligned size in iovec element");
/* Test invalid grant in iovec element. */
memcpy(iov, iovt, sizeof(iovt));
iov[1].iov_grant = GRANT_INVALID;
fill_rand(buf_ptr, buf_size);
buf2_sum = fill_rand(buf2_ptr, buf2_size);
buf3_sum = fill_rand(buf3_ptr, buf3_size);
raw_xfer(driver_minor, 0ULL, iov, 3, FALSE, EINVAL, &res);
/* Do not test the first buffer, as it may contain a partial result. */
test_sum(buf2_ptr, buf2_size, buf2_sum, TRUE, &res);
test_sum(buf3_ptr, buf3_size, buf3_sum, TRUE, &res);
got_result(&res, "invalid grant in iovec element");
/* Test revoked grant in iovec element. */
memcpy(iov, iovt, sizeof(iovt));
if ((grant = cpf_grant_direct(driver_endpt, (vir_bytes) buf2_ptr,
buf2_size, CPF_WRITE)) == GRANT_INVALID)
panic("unable to allocate grant");
cpf_revoke(grant);
iov[1].iov_grant = grant;
buf_sum = fill_rand(buf_ptr, buf_size);
buf2_sum = fill_rand(buf2_ptr, buf2_size);
buf3_sum = fill_rand(buf3_ptr, buf3_size);
raw_xfer(driver_minor, 0ULL, iov, 3, FALSE, EINVAL, &res);
accept_result(&res, RESULT_BADSTATUS, EPERM);
/* Do not test the first buffer, as it may contain a partial result. */
test_sum(buf2_ptr, buf2_size, buf2_sum, TRUE, &res);
test_sum(buf3_ptr, buf3_size, buf3_sum, TRUE, &res);
got_result(&res, "revoked grant in iovec element");
/* Test read-only grant in iovec element. */
memcpy(iov, iovt, sizeof(iovt));
if ((grant = cpf_grant_direct(driver_endpt, (vir_bytes) buf2_ptr,
buf2_size, CPF_READ)) == GRANT_INVALID)
panic("unable to allocate grant");
iov[1].iov_grant = grant;
buf_sum = fill_rand(buf_ptr, buf_size);
buf2_sum = fill_rand(buf2_ptr, buf2_size);
buf3_sum = fill_rand(buf3_ptr, buf3_size);
raw_xfer(driver_minor, 0ULL, iov, 3, FALSE, EINVAL, &res);
accept_result(&res, RESULT_BADSTATUS, EPERM);
/* Do not test the first buffer, as it may contain a partial result. */
test_sum(buf2_ptr, buf2_size, buf2_sum, TRUE, &res);
test_sum(buf3_ptr, buf3_size, buf3_sum, TRUE, &res);
got_result(&res, "read-only grant in iovec element");
cpf_revoke(grant);
/* Test word-unaligned iovec element buffer. */
memcpy(iov, iovt, sizeof(iovt));
if ((grant = cpf_grant_direct(driver_endpt, (vir_bytes) (buf2_ptr + 1),
buf2_size - 2, CPF_WRITE)) == GRANT_INVALID)
panic("unable to allocate grant");
iov[1].iov_grant = grant;
iov[1].iov_size = buf2_size - 2;
buf_sum = fill_rand(buf_ptr, buf_size);
buf2_sum = fill_rand(buf2_ptr, buf2_size);
buf3_sum = fill_rand(buf3_ptr, buf3_size);
c1 = buf2_ptr[0];
c2 = buf2_ptr[buf2_size - 1];
raw_xfer(driver_minor, 0ULL, iov, 3, FALSE, BUF_SIZE * 3 - 2, &res);
if (accept_result(&res, RESULT_BADSTATUS, EINVAL)) {
/* Do not test the first buffer, as it may contain a partial
* result.
*/
test_sum(buf2_ptr, buf2_size, buf2_sum, TRUE, &res);
test_sum(buf3_ptr, buf3_size, buf3_sum, TRUE, &res);
} else {
test_sum(buf_ptr, buf_size, buf_sum, FALSE, &res);
test_sum(buf2_ptr, buf2_size, buf2_sum, FALSE, &res);
test_sum(buf3_ptr, buf3_size, buf3_sum, FALSE, &res);
if (c1 != buf2_ptr[0] || c2 != buf2_ptr[buf2_size - 1])
set_result(&res, RESULT_CORRUPT, 0);
}
got_result(&res, "word-unaligned buffer in iovec element");
cpf_revoke(grant);
/* Test word-unaligned position. */
/* Only perform this test if the minimum read size is not 1, in which
* case it is safe to assume that the driver expects no position
* alignment either. These tests are indeed not exhaustive yet. For now
* we assume that if no alignment is required at all, the driver does
* not implement special logic to achieve this, so we don't need to
* test all possible positions and sizes either (yes, laziness..).
*/
if (min_read > 1) {
memcpy(iov, iovt, sizeof(iovt));
buf_sum = fill_rand(buf_ptr, buf_size);
buf2_sum = fill_rand(buf2_ptr, buf2_size);
buf3_sum = fill_rand(buf3_ptr, buf3_size);
raw_xfer(driver_minor, 1ULL, iov, 3, FALSE, EINVAL, &res);
test_sum(buf_ptr, buf_size, buf_sum, TRUE, &res);
test_sum(buf2_ptr, buf2_size, buf2_sum, TRUE, &res);
test_sum(buf3_ptr, buf3_size, buf3_sum, TRUE, &res);
got_result(&res, "word-unaligned position");
}
/* Test normal vector request (final check). */
memcpy(iov, iovt, sizeof(iovt));
buf_sum = fill_rand(buf_ptr, buf_size);
buf2_sum = fill_rand(buf2_ptr, buf2_size);
buf3_sum = fill_rand(buf3_ptr, buf3_size);
raw_xfer(driver_minor, 0ULL, iov, 3, FALSE,
buf_size + buf2_size + buf3_size, &res);
test_sum(buf_ptr, buf_size, buf_sum, FALSE, &res);
test_sum(buf2_ptr, buf2_size, buf2_sum, FALSE, &res);
test_sum(buf3_ptr, buf3_size, buf3_sum, FALSE, &res);
got_result(&res, "normal vector request");
/* Clean up. */
free_buf_and_grant(buf3_ptr, buf3_grant, buf3_size);
free_buf_and_grant(buf2_ptr, buf2_grant, buf2_size);
free_buf_and_grant(buf_ptr, buf_grant, buf_size);
}
static void bad_write(void)
{
/* Test various illegal write transfer requests, if writing is allowed.
* If handled correctly, these requests will not actually write data.
* This part of the test set is in need of further expansion.
*/
u8_t *buf_ptr, *buf2_ptr, *buf3_ptr;
size_t buf_size, buf2_size, buf3_size, sector_unalign;
cp_grant_id_t buf_grant, buf2_grant, buf3_grant;
cp_grant_id_t grant;
u32_t buf_sum, buf2_sum, buf3_sum;
iovec_s_t iov[3], iovt[3];
result_t res;
test_group("bad write requests", may_write);
if (!may_write)
return;
buf_size = buf2_size = buf3_size = BUF_SIZE;
alloc_buf_and_grant(&buf_ptr, &buf_grant, buf_size, CPF_READ);
alloc_buf_and_grant(&buf2_ptr, &buf2_grant, buf2_size, CPF_READ);
alloc_buf_and_grant(&buf3_ptr, &buf3_grant, buf3_size, CPF_READ);
iovt[0].iov_grant = buf_grant;
iovt[0].iov_size = buf_size;
iovt[1].iov_grant = buf2_grant;
iovt[1].iov_size = buf2_size;
iovt[2].iov_grant = buf3_grant;
iovt[2].iov_size = buf3_size;
/* Only perform write alignment tests if writes require alignment. */
if (min_write == 0)
min_write = sector_size;
if (min_write > 1) {
/* If min_write is larger than 2, use 2 as sector-unaligned
* size, as word-unaligned values (e.g., 1) may be filtered out
* on another code path.
*/
sector_unalign = (min_write > 2) ? 2 : 1;
/* Test sector-unaligned write position. */
memcpy(iov, iovt, sizeof(iovt));
buf_sum = fill_rand(buf_ptr, buf_size);
buf2_sum = fill_rand(buf2_ptr, buf2_size);
buf3_sum = fill_rand(buf3_ptr, buf3_size);
raw_xfer(driver_minor, (u64_t)sector_unalign, iov, 3, TRUE,
EINVAL, &res);
test_sum(buf_ptr, buf_size, buf_sum, TRUE, &res);
test_sum(buf2_ptr, buf2_size, buf2_sum, TRUE, &res);
test_sum(buf3_ptr, buf3_size, buf3_sum, TRUE, &res);
got_result(&res, "sector-unaligned write position");
/* Test sector-unaligned write size. */
memcpy(iov, iovt, sizeof(iovt));
iov[1].iov_size -= sector_unalign;
buf_sum = fill_rand(buf_ptr, buf_size);
buf2_sum = fill_rand(buf2_ptr, buf2_size);
buf3_sum = fill_rand(buf3_ptr, buf3_size);
raw_xfer(driver_minor, 0ULL, iov, 3, TRUE, EINVAL, &res);
test_sum(buf_ptr, buf_size, buf_sum, TRUE, &res);
test_sum(buf2_ptr, buf2_size, buf2_sum, TRUE, &res);
test_sum(buf3_ptr, buf3_size, buf3_sum, TRUE, &res);
got_result(&res, "sector-unaligned write size");
}
/* Test write-only grant in iovec element. */
memcpy(iov, iovt, sizeof(iovt));
if ((grant = cpf_grant_direct(driver_endpt, (vir_bytes) buf2_ptr,
buf2_size, CPF_WRITE)) == GRANT_INVALID)
panic("unable to allocate grant");
iov[1].iov_grant = grant;
buf_sum = fill_rand(buf_ptr, buf_size);
buf2_sum = fill_rand(buf2_ptr, buf2_size);
buf3_sum = fill_rand(buf3_ptr, buf3_size);
raw_xfer(driver_minor, 0ULL, iov, 3, TRUE, EINVAL, &res);
accept_result(&res, RESULT_BADSTATUS, EPERM);
test_sum(buf_ptr, buf_size, buf_sum, TRUE, &res);
test_sum(buf2_ptr, buf2_size, buf2_sum, TRUE, &res);
test_sum(buf3_ptr, buf3_size, buf3_sum, TRUE, &res);
got_result(&res, "write-only grant in iovec element");
cpf_revoke(grant);
/* Clean up. */
free_buf_and_grant(buf3_ptr, buf3_grant, buf3_size);
free_buf_and_grant(buf2_ptr, buf2_grant, buf2_size);
free_buf_and_grant(buf_ptr, buf_grant, buf_size);
}
static void vector_and_large_sub(size_t small_size)
{
/* Check whether large vectored requests, and large single requests,
* succeed.
*/
size_t large_size, buf_size, buf2_size;
u8_t *buf_ptr, *buf2_ptr;
iovec_t iovec[NR_IOREQS];
u64_t base_pos;
result_t res;
int i;
base_pos = (u64_t)sector_size;
large_size = small_size * NR_IOREQS;
buf_size = large_size + sizeof(u32_t) * 2;
buf2_size = large_size + sizeof(u32_t) * (NR_IOREQS + 1);
buf_ptr = alloc_dma_memory(buf_size);
buf2_ptr = alloc_dma_memory(buf2_size);
/* The first buffer has one large chunk with dword-sized guards on each
* side. LPTR(n) points to the start of the nth small data chunk within
* the large chunk. The second buffer contains several small chunks. It
* has dword-sized guards before each chunk and after the last chunk.
* SPTR(n) points to the start of the nth small chunk.
*/
#define SPTR(n) (buf2_ptr + sizeof(u32_t) + (n) * (sizeof(u32_t) + small_size))
#define LPTR(n) (buf_ptr + sizeof(u32_t) + small_size * (n))
/* Write one large chunk, if writing is allowed. */
if (may_write) {
fill_rand(buf_ptr, buf_size); /* don't need the checksum */
iovec[0].iov_addr = (vir_bytes) (buf_ptr + sizeof(u32_t));
iovec[0].iov_size = large_size;
vir_xfer(driver_minor, base_pos, iovec, 1, TRUE, large_size,
&res);
got_result(&res, "large write");
}
/* Read back in many small chunks. If writing is not allowed, do not
* check checksums.
*/
for (i = 0; i < NR_IOREQS; i++) {
* (((u32_t *) SPTR(i)) - 1) = 0xDEADBEEFL + i;
iovec[i].iov_addr = (vir_bytes) SPTR(i);
iovec[i].iov_size = small_size;
}
* (((u32_t *) SPTR(i)) - 1) = 0xFEEDFACEL;
vir_xfer(driver_minor, base_pos, iovec, NR_IOREQS, FALSE, large_size,
&res);
if (res.type == RESULT_OK) {
for (i = 0; i < NR_IOREQS; i++) {
if (* (((u32_t *) SPTR(i)) - 1) != 0xDEADBEEFL + i)
set_result(&res, RESULT_OVERFLOW, 0);
}
if (* (((u32_t *) SPTR(i)) - 1) != 0xFEEDFACEL)
set_result(&res, RESULT_OVERFLOW, 0);
}
if (res.type == RESULT_OK && may_write) {
for (i = 0; i < NR_IOREQS; i++) {
test_sum(SPTR(i), small_size,
get_sum(LPTR(i), small_size), TRUE, &res);
}
}
got_result(&res, "vectored read");
/* Write new data in many small chunks, if writing is allowed. */
if (may_write) {
fill_rand(buf2_ptr, buf2_size); /* don't need the checksum */
for (i = 0; i < NR_IOREQS; i++) {
iovec[i].iov_addr = (vir_bytes) SPTR(i);
iovec[i].iov_size = small_size;
}
vir_xfer(driver_minor, base_pos, iovec, NR_IOREQS, TRUE,
large_size, &res);
got_result(&res, "vectored write");
}
/* Read back in one large chunk. If writing is allowed, the checksums
* must match the last write; otherwise, they must match the last read.
* In both cases, the expected content is in the second buffer.
*/
* (u32_t *) buf_ptr = 0xCAFEBABEL;
* (u32_t *) (buf_ptr + sizeof(u32_t) + large_size) = 0xDECAFBADL;
iovec[0].iov_addr = (vir_bytes) (buf_ptr + sizeof(u32_t));
iovec[0].iov_size = large_size;
vir_xfer(driver_minor, base_pos, iovec, 1, FALSE, large_size, &res);
if (res.type == RESULT_OK) {
if (* (u32_t *) buf_ptr != 0xCAFEBABEL)
set_result(&res, RESULT_OVERFLOW, 0);
if (* (u32_t *) (buf_ptr + sizeof(u32_t) + large_size) !=
0xDECAFBADL)
set_result(&res, RESULT_OVERFLOW, 0);
}
if (res.type == RESULT_OK) {
for (i = 0; i < NR_IOREQS; i++) {
test_sum(SPTR(i), small_size,
get_sum(LPTR(i), small_size), TRUE, &res);
}
}
got_result(&res, "large read");
#undef LPTR
#undef SPTR
/* Clean up. */
free_dma_memory(buf2_ptr, buf2_size);
free_dma_memory(buf_ptr, buf_size);
}
static void vector_and_large(void)
{
/* Check whether large vectored requests, and large single requests,
* succeed. These are request patterns commonly used by MFS and the
* filter driver, respectively. We try the same test twice: once with
* a common block size, and once to push against the max request size.
*/
size_t max_block;
/* Make sure that the maximum size does not exceed the target device
* size, minus the margins we need for testing here and there.
*/
if (max_size > part.size - sector_size * 4)
max_size = part.size - sector_size * 4;
/* Compute the largest sector multiple which, when multiplied by
* NR_IOREQS, is no more than the maximum transfer size. Note that if
* max_size is not a multiple of sector_size, we're not going up to the
* limit entirely this way.
*/
max_block = max_size / NR_IOREQS;
max_block -= max_block % sector_size;
#define COMMON_BLOCK_SIZE 4096
test_group("vector and large, common block", TRUE);
vector_and_large_sub(COMMON_BLOCK_SIZE);
if (max_block != COMMON_BLOCK_SIZE) {
test_group("vector and large, large block", TRUE);
vector_and_large_sub(max_block);
}
}
static void open_device(dev_t minor)
{
/* Open a partition or subpartition. Remember that it has been opened,
* so that we can reopen it later in the event of a driver crash.
*/
message m;
result_t res;
memset(&m, 0, sizeof(m));
m.m_type = BDEV_OPEN;
m.BDEV_MINOR = minor;
m.BDEV_ACCESS = may_write ? (BDEV_R_BIT | BDEV_W_BIT) : BDEV_R_BIT;
m.BDEV_ID = lrand48();
sendrec_driver(&m, OK, &res);
/* We assume that this call is supposed to succeed. We pretend it
* always succeeds, so that close_device() won't get confused later.
*/
assert(nr_opened < NR_OPENED);
opened[nr_opened++] = minor;
got_result(&res, minor == driver_minor ? "opening the main partition" :
"opening a subpartition");
}
static void close_device(dev_t minor)
{
/* Close a partition or subpartition. Remove it from the list of opened
* devices.
*/
message m;
result_t res;
int i;
memset(&m, 0, sizeof(m));
m.m_type = BDEV_CLOSE;
m.BDEV_MINOR = minor;
m.BDEV_ID = lrand48();
sendrec_driver(&m, OK, &res);
assert(nr_opened > 0);
for (i = 0; i < nr_opened; i++) {
if (opened[i] == minor) {
opened[i] = opened[--nr_opened];
break;
}
}
got_result(&res, minor == driver_minor ? "closing the main partition" :
"closing a subpartition");
}
static int vir_ioctl(dev_t minor, int req, void *ptr, ssize_t exp,
result_t *res)
{
/* Perform an I/O control request, using a local buffer.
*/
cp_grant_id_t grant;
message m;
int r, perm;
assert(!_MINIX_IOCTL_BIG(req)); /* not supported */
perm = 0;
if (_MINIX_IOCTL_IOR(req)) perm |= CPF_WRITE;
if (_MINIX_IOCTL_IOW(req)) perm |= CPF_READ;
if ((grant = cpf_grant_direct(driver_endpt, (vir_bytes) ptr,
_MINIX_IOCTL_SIZE(req), perm)) == GRANT_INVALID)
panic("unable to allocate grant");
memset(&m, 0, sizeof(m));
m.m_type = BDEV_IOCTL;
m.BDEV_MINOR = minor;
m.BDEV_REQUEST = req;
m.BDEV_GRANT = grant;
m.BDEV_USER = NONE;
m.BDEV_ID = lrand48();
r = sendrec_driver(&m, exp, res);
if (cpf_revoke(grant) != OK)
panic("unable to revoke grant");
return r;
}
static void misc_ioctl(void)
{
/* Test some ioctls.
*/
result_t res;
int openct;
test_group("test miscellaneous ioctls", TRUE);
/* Retrieve the main partition's base and size. Save for later. */
vir_ioctl(driver_minor, DIOCGETP, &part, OK, &res);
got_result(&res, "ioctl to get partition");
/* The other tests do not check whether there is sufficient room. */
if (res.type == RESULT_OK && part.size < (u64_t)max_size * 2)
output("WARNING: small partition, some tests may fail\n");
/* Test retrieving global driver open count. */
openct = 0x0badcafe;
vir_ioctl(driver_minor, DIOCOPENCT, &openct, OK, &res);
/* We assume that we're the only client to the driver right now. */
if (res.type == RESULT_OK && openct != 1) {
res.type = RESULT_BADVALUE;
res.value = openct;
}
got_result(&res, "ioctl to get open count");
/* Test increasing and re-retrieving open count. */
open_device(driver_minor);
openct = 0x0badcafe;
vir_ioctl(driver_minor, DIOCOPENCT, &openct, OK, &res);
if (res.type == RESULT_OK && openct != 2) {
res.type = RESULT_BADVALUE;
res.value = openct;
}
got_result(&res, "increased open count after opening");
/* Test decreasing and re-retrieving open count. */
close_device(driver_minor);
openct = 0x0badcafe;
vir_ioctl(driver_minor, DIOCOPENCT, &openct, OK, &res);
if (res.type == RESULT_OK && openct != 1) {
res.type = RESULT_BADVALUE;
res.value = openct;
}
got_result(&res, "decreased open count after closing");
}
static void read_limits(dev_t sub0_minor, dev_t sub1_minor, size_t sub_size)
{
/* Test reads up to, across, and beyond partition limits.
*/
u8_t *buf_ptr;
size_t buf_size;
u32_t sum, sum2, sum3;
result_t res;
test_group("read around subpartition limits", TRUE);
buf_size = sector_size * 3;
buf_ptr = alloc_dma_memory(buf_size);
/* Read one sector up to the partition limit. */
fill_rand(buf_ptr, buf_size);
simple_xfer(sub0_minor, (u64_t)sub_size - sector_size, buf_ptr,
sector_size, FALSE, sector_size, &res);
sum = get_sum(buf_ptr, sector_size);
got_result(&res, "one sector read up to partition end");
/* Read three sectors up to the partition limit. */
fill_rand(buf_ptr, buf_size);
simple_xfer(sub0_minor, (u64_t)sub_size - buf_size, buf_ptr, buf_size,
FALSE, buf_size, &res);
test_sum(buf_ptr + sector_size * 2, sector_size, sum, TRUE, &res);
sum2 = get_sum(buf_ptr + sector_size, sector_size * 2);
got_result(&res, "multisector read up to partition end");
/* Read three sectors, two up to and one beyond the partition end. */
fill_rand(buf_ptr, buf_size);
sum3 = get_sum(buf_ptr + sector_size * 2, sector_size);
simple_xfer(sub0_minor, (u64_t)sub_size - sector_size * 2, buf_ptr,
buf_size, FALSE, sector_size * 2, &res);
test_sum(buf_ptr, sector_size * 2, sum2, TRUE, &res);
test_sum(buf_ptr + sector_size * 2, sector_size, sum3, TRUE, &res);
got_result(&res, "read somewhat across partition end");
/* Read three sectors, one up to and two beyond the partition end. */
fill_rand(buf_ptr, buf_size);
sum2 = get_sum(buf_ptr + sector_size, sector_size * 2);
simple_xfer(sub0_minor, (u64_t)sub_size - sector_size, buf_ptr,
buf_size, FALSE, sector_size, &res);
test_sum(buf_ptr, sector_size, sum, TRUE, &res);
test_sum(buf_ptr + sector_size, sector_size * 2, sum2, TRUE, &res);
got_result(&res, "read mostly across partition end");
/* Read one sector starting at the partition end. */
sum = fill_rand(buf_ptr, buf_size);
sum2 = get_sum(buf_ptr, sector_size);
simple_xfer(sub0_minor, (u64_t)sub_size, buf_ptr, sector_size, FALSE,
0, &res);
test_sum(buf_ptr, sector_size, sum2, TRUE, &res);
got_result(&res, "one sector read at partition end");
/* Read three sectors starting at the partition end. */
simple_xfer(sub0_minor, (u64_t)sub_size, buf_ptr, buf_size, FALSE, 0,
&res);
test_sum(buf_ptr, buf_size, sum, TRUE, &res);
got_result(&res, "multisector read at partition end");
/* Read one sector beyond the partition end. */
simple_xfer(sub0_minor, (u64_t)sub_size + sector_size, buf_ptr,
buf_size, FALSE, 0, &res);
test_sum(buf_ptr, sector_size, sum2, TRUE, &res);
got_result(&res, "single sector read beyond partition end");
/* Read three sectors way beyond the partition end. */
simple_xfer(sub0_minor, 0x1000000000000000ULL, buf_ptr, buf_size,
FALSE, 0, &res);
test_sum(buf_ptr, buf_size, sum, TRUE, &res);
/* Test negative offsets. This request should return EOF or fail; we
* assume that it return EOF here (because that is what the AHCI driver
* does, to avoid producing errors for requests close to the 2^64 byte
* position limit [yes, this will indeed never happen anyway]). This is
* more or less a bad requests test, but we cannot do it without
* setting up subpartitions first.
*/
simple_xfer(sub1_minor, 0xffffffffffffffffULL - sector_size + 1,
buf_ptr, sector_size, FALSE, 0, &res);
test_sum(buf_ptr, sector_size, sum2, TRUE, &res);
got_result(&res, "read with negative offset");
/* Clean up. */
free_dma_memory(buf_ptr, buf_size);
}
static void write_limits(dev_t sub0_minor, dev_t sub1_minor, size_t sub_size)
{
/* Test writes up to, across, and beyond partition limits. Use the
* first given subpartition to test, and the second to make sure there
* are no overruns. The given size is the size of each of the
* subpartitions. Note that the necessity to check the results using
* readback, makes this more or less a superset of the read test.
*/
u8_t *buf_ptr;
size_t buf_size;
u32_t sum, sum2, sum3, sub1_sum;
result_t res;
test_group("write around subpartition limits", may_write);
if (!may_write)
return;
buf_size = sector_size * 3;
buf_ptr = alloc_dma_memory(buf_size);
/* Write to the start of the second subpartition, so that we can
* reliably check whether the contents have changed later.
*/
sub1_sum = fill_rand(buf_ptr, buf_size);
simple_xfer(sub1_minor, 0ULL, buf_ptr, buf_size, TRUE, buf_size, &res);
got_result(&res, "write to second subpartition");
/* Write one sector, up to the partition limit. */
sum = fill_rand(buf_ptr, sector_size);
simple_xfer(sub0_minor, (u64_t)sub_size - sector_size, buf_ptr,
sector_size, TRUE, sector_size, &res);
got_result(&res, "write up to partition end");
/* Read back to make sure the results have persisted. */
fill_rand(buf_ptr, sector_size * 2);
simple_xfer(sub0_minor, (u64_t)sub_size - sector_size * 2, buf_ptr,
sector_size * 2, FALSE, sector_size * 2, &res);
test_sum(buf_ptr + sector_size, sector_size, sum, TRUE, &res);
got_result(&res, "read up to partition end");
/* Write three sectors, two up to and one beyond the partition end. */
fill_rand(buf_ptr, buf_size);
sum = get_sum(buf_ptr + sector_size, sector_size);
sum3 = get_sum(buf_ptr, sector_size);
simple_xfer(sub0_minor, (u64_t)sub_size - sector_size * 2, buf_ptr,
buf_size, TRUE, sector_size * 2, &res);
got_result(&res, "write somewhat across partition end");
/* Read three sectors, one up to and two beyond the partition end. */
fill_rand(buf_ptr, buf_size);
sum2 = get_sum(buf_ptr + sector_size, sector_size * 2);
simple_xfer(sub0_minor, (u64_t)sub_size - sector_size, buf_ptr,
buf_size, FALSE, sector_size, &res);
test_sum(buf_ptr, sector_size, sum, TRUE, &res);
test_sum(buf_ptr + sector_size, sector_size * 2, sum2, TRUE, &res);
got_result(&res, "read mostly across partition end");
/* Repeat this but with write and read start positions swapped. */
fill_rand(buf_ptr, buf_size);
sum = get_sum(buf_ptr, sector_size);
simple_xfer(sub0_minor, (u64_t)sub_size - sector_size, buf_ptr,
buf_size, TRUE, sector_size, &res);
got_result(&res, "write mostly across partition end");
fill_rand(buf_ptr, buf_size);
sum2 = get_sum(buf_ptr + sector_size * 2, sector_size);
simple_xfer(sub0_minor, (u64_t)sub_size - sector_size * 2, buf_ptr,
buf_size, FALSE, sector_size * 2, &res);
test_sum(buf_ptr, sector_size, sum3, TRUE, &res);
test_sum(buf_ptr + sector_size, sector_size, sum, TRUE, &res);
test_sum(buf_ptr + sector_size * 2, sector_size, sum2, TRUE, &res);
got_result(&res, "read somewhat across partition end");
/* Write one sector at the end of the partition. */
fill_rand(buf_ptr, sector_size);
simple_xfer(sub0_minor, (u64_t)sub_size, buf_ptr, sector_size, TRUE, 0,
&res);
got_result(&res, "write at partition end");
/* Write one sector beyond the end of the partition. */
simple_xfer(sub0_minor, (u64_t)sub_size + sector_size, buf_ptr,
sector_size, TRUE, 0, &res);
got_result(&res, "write beyond partition end");
/* Read from the start of the second subpartition, and see if it
* matches what we wrote into it earlier.
*/
fill_rand(buf_ptr, buf_size);
simple_xfer(sub1_minor, 0ULL, buf_ptr, buf_size, FALSE, buf_size,
&res);
test_sum(buf_ptr, buf_size, sub1_sum, TRUE, &res);
got_result(&res, "read from second subpartition");
/* Test offset wrapping, but this time for writes. */
fill_rand(buf_ptr, sector_size);
simple_xfer(sub1_minor, 0xffffffffffffffffULL - sector_size + 1,
buf_ptr, sector_size, TRUE, 0, &res);
got_result(&res, "write with negative offset");
/* If the last request erroneously succeeded, it would have overwritten
* the last sector of the first subpartition.
*/
simple_xfer(sub0_minor, (u64_t)sub_size - sector_size, buf_ptr,
sector_size, FALSE, sector_size, &res);
test_sum(buf_ptr, sector_size, sum, TRUE, &res);
got_result(&res, "read up to partition end");
/* Clean up. */
free_dma_memory(buf_ptr, buf_size);
}
static void vir_limits(dev_t sub0_minor, dev_t sub1_minor, int part_secs)
{
/* Create virtual, temporary subpartitions through the DIOCSETP ioctl,
* and perform tests on the resulting subpartitions.
*/
struct part_geom subpart, subpart2;
size_t sub_size;
result_t res;
test_group("virtual subpartition limits", TRUE);
/* Open the subpartitions. This is somewhat dodgy; we rely on the
* driver allowing this even if no subpartitions exist. We cannot do
* this test without doing a DIOCSETP on an open subdevice, though.
*/
open_device(sub0_minor);
open_device(sub1_minor);
sub_size = sector_size * part_secs;
/* Set, and check, the size of the first subpartition. */
subpart = part;
subpart.size = (u64_t)sub_size;
vir_ioctl(sub0_minor, DIOCSETP, &subpart, OK, &res);
got_result(&res, "ioctl to set first subpartition");
vir_ioctl(sub0_minor, DIOCGETP, &subpart2, OK, &res);
if (res.type == RESULT_OK && (subpart.base != subpart2.base ||
subpart.size != subpart2.size)) {
res.type = RESULT_BADVALUE;
res.value = 0;
}
got_result(&res, "ioctl to get first subpartition");
/* Set, and check, the base and size of the second subpartition. */
subpart = part;
subpart.base += sub_size;
subpart.size = (u64_t)sub_size;
vir_ioctl(sub1_minor, DIOCSETP, &subpart, OK, &res);
got_result(&res, "ioctl to set second subpartition");
vir_ioctl(sub1_minor, DIOCGETP, &subpart2, OK, &res);
if (res.type == RESULT_OK && (subpart.base != subpart2.base ||
subpart.size != subpart2.size)) {
res.type = RESULT_BADVALUE;
res.value = 0;
}
got_result(&res, "ioctl to get second subpartition");
/* Perform the actual I/O tests. */
read_limits(sub0_minor, sub1_minor, sub_size);
write_limits(sub0_minor, sub1_minor, sub_size);
/* Clean up. */
close_device(sub1_minor);
close_device(sub0_minor);
}
static void real_limits(dev_t sub0_minor, dev_t sub1_minor, int part_secs)
{
/* Create our own subpartitions by writing a partition table, and
* perform tests on the resulting real subpartitions.
*/
u8_t *buf_ptr;
size_t buf_size, sub_size;
struct part_geom subpart;
struct part_entry *entry;
result_t res;
test_group("real subpartition limits", may_write);
if (!may_write)
return;
sub_size = sector_size * part_secs;
/* Technically, we should be using 512 instead of sector_size in
* various places, because even on CD-ROMs, the partition tables are
* 512 bytes and the sector counts are based on 512-byte sectors in it.
* We ignore this subtlety because CD-ROMs are assumed to be read-only
* anyway.
*/
buf_size = sector_size;
buf_ptr = alloc_dma_memory(buf_size);
memset(buf_ptr, 0, buf_size);
/* Write an invalid partition table. */
simple_xfer(driver_minor, 0ULL, buf_ptr, buf_size, TRUE, buf_size,
&res);
got_result(&res, "write of invalid partition table");
/* Get the disk driver to reread the partition table. This should
* happen (at least) when the device is fully closed and then reopened.
* The ioctl test already made sure that we're the only client.
*/
close_device(driver_minor);
open_device(driver_minor);
/* See if our changes are visible. We expect the subpartitions to have
* a size of zero now, indicating that they're not there. For actual
* subpartitions (as opposed to normal partitions), this requires the
* driver to zero them out, because the partition code does not do so.
*/
open_device(sub0_minor);
open_device(sub1_minor);
vir_ioctl(sub0_minor, DIOCGETP, &subpart, 0, &res);
if (res.type == RESULT_OK && subpart.size != 0) {
res.type = RESULT_BADVALUE;
res.value = ex64lo(subpart.size);
}
got_result(&res, "ioctl to get first subpartition");
vir_ioctl(sub1_minor, DIOCGETP, &subpart, 0, &res);
if (res.type == RESULT_OK && subpart.size != 0) {
res.type = RESULT_BADVALUE;
res.value = ex64lo(subpart.size);
}
got_result(&res, "ioctl to get second subpartition");
close_device(sub1_minor);
close_device(sub0_minor);
/* Now write a valid partition table. */
memset(buf_ptr, 0, buf_size);
entry = (struct part_entry *) &buf_ptr[PART_TABLE_OFF];
entry[0].sysind = MINIX_PART;
entry[0].lowsec = part.base / sector_size + 1;
entry[0].size = part_secs;
entry[1].sysind = MINIX_PART;
entry[1].lowsec = entry[0].lowsec + entry[0].size;
entry[1].size = part_secs;
buf_ptr[510] = 0x55;
buf_ptr[511] = 0xAA;
simple_xfer(driver_minor, 0ULL, buf_ptr, buf_size, TRUE, buf_size,
&res);
got_result(&res, "write of valid partition table");
/* Same as above. */
close_device(driver_minor);
open_device(driver_minor);
/* Again, see if our changes are visible. This time the proper base and
* size should be there.
*/
open_device(sub0_minor);
open_device(sub1_minor);
vir_ioctl(sub0_minor, DIOCGETP, &subpart, 0, &res);
if (res.type == RESULT_OK &&
(subpart.base != part.base + sector_size ||
subpart.size != (u64_t)part_secs * sector_size)) {
res.type = RESULT_BADVALUE;
res.value = 0;
}
got_result(&res, "ioctl to get first subpartition");
vir_ioctl(sub1_minor, DIOCGETP, &subpart, 0, &res);
if (res.type == RESULT_OK &&
(subpart.base != part.base + (1 + part_secs) * sector_size ||
subpart.size != (u64_t)part_secs * sector_size)) {
res.type = RESULT_BADVALUE;
res.value = 0;
}
got_result(&res, "ioctl to get second subpartition");
/* Now perform the actual I/O tests. */
read_limits(sub0_minor, sub1_minor, sub_size);
write_limits(sub0_minor, sub1_minor, sub_size);
/* Clean up. */
close_device(sub0_minor);
close_device(sub1_minor);
free_dma_memory(buf_ptr, buf_size);
}
static void part_limits(void)
{
/* Test reads and writes up to, across, and beyond partition limits.
* As a side effect, test reading and writing partition sizes and
* rereading partition tables.
*/
dev_t par, sub0_minor, sub1_minor;
/* First determine the first two subpartitions of the partition that we
* are operating on. If we are already operating on a subpartition, we
* cannot conduct this test.
*/
if (driver_minor >= MINOR_d0p0s0) {
output("WARNING: operating on subpartition, "
"skipping partition tests\n");
return;
}
par = driver_minor % DEV_PER_DRIVE;
if (par > 0) /* adapted from libdriver's drvlib code */
sub0_minor = MINOR_d0p0s0 + ((driver_minor / DEV_PER_DRIVE) *
NR_PARTITIONS + par - 1) * NR_PARTITIONS;
else
sub0_minor = driver_minor + 1;
sub1_minor = sub0_minor + 1;
#define PART_SECS 9 /* sectors in each partition. must be >= 4. */
/* First try the test with temporarily specified subpartitions. */
vir_limits(sub0_minor, sub1_minor, PART_SECS);
/* Then, if we're allowed to write, try the test with real, persisted
* subpartitions.
*/
real_limits(sub0_minor, sub1_minor, PART_SECS - 1);
}
static void unaligned_size_io(u64_t base_pos, u8_t *buf_ptr, size_t buf_size,
u8_t *sec_ptr[2], int sectors, int pattern, u32_t ssum[5])
{
/* Perform a single small-element I/O read, write, readback test.
* The number of sectors and the pattern varies with each call.
* The ssum array has to be updated to reflect the five sectors'
* checksums on disk, if writing is enabled. Note that for
*/
iovec_t iov[3], iovt[3];
u32_t rsum[3];
result_t res;
size_t total_size;
int i, nr_req;
base_pos += sector_size;
total_size = sector_size * sectors;
/* If the limit is two elements per sector, we cannot test three
* elements in a single sector.
*/
if (sector_size / element_size == 2 && sectors == 1 && pattern == 2)
return;
/* Set up the buffers and I/O vector. We use different buffers for the
* elements to minimize the chance that something "accidentally" goes
* right, but that means we have to do memory copying to do checksum
* computation.
*/
fill_rand(sec_ptr[0], sector_size);
rsum[0] =
get_sum(sec_ptr[0] + element_size, sector_size - element_size);
fill_rand(buf_ptr, buf_size);
switch (pattern) {
case 0:
/* First pattern: a small element on the left. */
iovt[0].iov_addr = (vir_bytes) sec_ptr[0];
iovt[0].iov_size = element_size;
iovt[1].iov_addr = (vir_bytes) buf_ptr;
iovt[1].iov_size = total_size - element_size;
rsum[1] = get_sum(buf_ptr + iovt[1].iov_size, element_size);
nr_req = 2;
break;
case 1:
/* Second pattern: a small element on the right. */
iovt[0].iov_addr = (vir_bytes) buf_ptr;
iovt[0].iov_size = total_size - element_size;
rsum[1] = get_sum(buf_ptr + iovt[0].iov_size, element_size);
iovt[1].iov_addr = (vir_bytes) sec_ptr[0];
iovt[1].iov_size = element_size;
nr_req = 2;
break;
case 2:
/* Third pattern: a small element on each side. */
iovt[0].iov_addr = (vir_bytes) sec_ptr[0];
iovt[0].iov_size = element_size;
iovt[1].iov_addr = (vir_bytes) buf_ptr;
iovt[1].iov_size = total_size - element_size * 2;
rsum[1] = get_sum(buf_ptr + iovt[1].iov_size,
element_size * 2);
fill_rand(sec_ptr[1], sector_size);
iovt[2].iov_addr = (vir_bytes) sec_ptr[1];
iovt[2].iov_size = element_size;
rsum[2] = get_sum(sec_ptr[1] + element_size,
sector_size - element_size);
nr_req = 3;
break;
default:
assert(0);
}
/* Perform a read with small elements, and test whether the result is
* as expected.
*/
memcpy(iov, iovt, sizeof(iov));
vir_xfer(driver_minor, base_pos, iov, nr_req, FALSE, total_size, &res);
test_sum(sec_ptr[0] + element_size, sector_size - element_size,
rsum[0], TRUE, &res);
switch (pattern) {
case 0:
test_sum(buf_ptr + iovt[1].iov_size, element_size, rsum[1],
TRUE, &res);
memmove(buf_ptr + element_size, buf_ptr, iovt[1].iov_size);
memcpy(buf_ptr, sec_ptr[0], element_size);
break;
case 1:
test_sum(buf_ptr + iovt[0].iov_size, element_size, rsum[1],
TRUE, &res);
memcpy(buf_ptr + iovt[0].iov_size, sec_ptr[0], element_size);
break;
case 2:
test_sum(buf_ptr + iovt[1].iov_size, element_size * 2, rsum[1],
TRUE, &res);
test_sum(sec_ptr[1] + element_size, sector_size - element_size,
rsum[2], TRUE, &res);
memmove(buf_ptr + element_size, buf_ptr, iovt[1].iov_size);
memcpy(buf_ptr, sec_ptr[0], element_size);
memcpy(buf_ptr + element_size + iovt[1].iov_size, sec_ptr[1],
element_size);
break;
}
for (i = 0; i < sectors; i++)
test_sum(buf_ptr + sector_size * i, sector_size, ssum[1 + i],
TRUE, &res);
got_result(&res, "read with small elements");
/* In read-only mode, we have nothing more to do. */
if (!may_write)
return;
/* Use the same I/O vector to perform a write with small elements.
* This will cause the checksums of the target sectors to change,
* so we need to update those for both verification and later usage.
*/
for (i = 0; i < sectors; i++)
ssum[1 + i] =
fill_rand(buf_ptr + sector_size * i, sector_size);
switch (pattern) {
case 0:
memcpy(sec_ptr[0], buf_ptr, element_size);
memmove(buf_ptr, buf_ptr + element_size, iovt[1].iov_size);
fill_rand(buf_ptr + iovt[1].iov_size, element_size);
break;
case 1:
memcpy(sec_ptr[0], buf_ptr + iovt[0].iov_size, element_size);
fill_rand(buf_ptr + iovt[0].iov_size, element_size);
break;
case 2:
memcpy(sec_ptr[0], buf_ptr, element_size);
memcpy(sec_ptr[1], buf_ptr + element_size + iovt[1].iov_size,
element_size);
memmove(buf_ptr, buf_ptr + element_size, iovt[1].iov_size);
fill_rand(buf_ptr + iovt[1].iov_size, element_size * 2);
break;
}
memcpy(iov, iovt, sizeof(iov));
vir_xfer(driver_minor, base_pos, iov, nr_req, TRUE, total_size, &res);
got_result(&res, "write with small elements");
/* Now perform normal readback verification. */
fill_rand(buf_ptr, sector_size * 3);
simple_xfer(driver_minor, base_pos, buf_ptr, sector_size * 3, FALSE,
sector_size * 3, &res);
for (i = 0; i < 3; i++)
test_sum(buf_ptr + sector_size * i, sector_size, ssum[1 + i],
TRUE, &res);
got_result(&res, "readback verification");
}
static void unaligned_size(void)
{
/* Test sector-unaligned sizes in I/O vector elements. The total size
* of the request, however, has to add up to the sector size.
*/
u8_t *buf_ptr, *sec_ptr[2];
size_t buf_size;
u32_t sum = 0L, ssum[5];
u64_t base_pos;
result_t res;
int i;
test_group("sector-unaligned elements", sector_size != element_size);
/* We can only do this test if the driver allows small elements. */
if (sector_size == element_size)
return;
/* Crashing on bad user input, terrible! */
assert(sector_size % element_size == 0);
/* Establish a baseline by writing and reading back five sectors; or
* by reading only, if writing is disabled.
*/
buf_size = sector_size * 5;
base_pos = (u64_t)sector_size * 2;
buf_ptr = alloc_dma_memory(buf_size);
sec_ptr[0] = alloc_dma_memory(sector_size);
sec_ptr[1] = alloc_dma_memory(sector_size);
if (may_write) {
sum = fill_rand(buf_ptr, buf_size);
for (i = 0; i < 5; i++)
ssum[i] = get_sum(buf_ptr + sector_size * i,
sector_size);
simple_xfer(driver_minor, base_pos, buf_ptr, buf_size, TRUE,
buf_size, &res);
got_result(&res, "write several sectors");
}
fill_rand(buf_ptr, buf_size);
simple_xfer(driver_minor, base_pos, buf_ptr, buf_size, FALSE, buf_size,
&res);
if (may_write) {
test_sum(buf_ptr, buf_size, sum, TRUE, &res);
}
else {
for (i = 0; i < 5; i++)
ssum[i] = get_sum(buf_ptr + sector_size * i,
sector_size);
}
got_result(&res, "read several sectors");
/* We do nine subtests. The first three involve only the second sector;
* the second three involve the second and third sectors, and the third
* three involve all of the middle sectors. Each triplet tests small
* elements at the left, at the right, and at both the left and the
* right of the area. For each operation, we first do an unaligned
* read, and if writing is enabled, an unaligned write and an aligned
* read.
*/
for (i = 0; i < 9; i++) {
unaligned_size_io(base_pos, buf_ptr, buf_size, sec_ptr,
i / 3 + 1, i % 3, ssum);
}
/* If writing was enabled, make sure that the first and fifth sector
* have remained untouched.
*/
if (may_write) {
fill_rand(buf_ptr, buf_size);
simple_xfer(driver_minor, base_pos, buf_ptr, buf_size, FALSE,
buf_size, &res);
test_sum(buf_ptr, sector_size, ssum[0], TRUE, &res);
test_sum(buf_ptr + sector_size * 4, sector_size, ssum[4], TRUE,
&res);
got_result(&res, "check first and last sectors");
}
/* Clean up. */
free_dma_memory(sec_ptr[1], sector_size);
free_dma_memory(sec_ptr[0], sector_size);
free_dma_memory(buf_ptr, buf_size);
}
static void unaligned_pos1(void)
{
/* Test sector-unaligned positions and total sizes for requests. This
* is a read-only test for now. Write support should be added later.
* In the current context, the term "lead" means an unwanted first part
* of a sector, and "trail" means an unwanted last part of a sector.
*/
u8_t *buf_ptr, *buf2_ptr;
size_t buf_size, buf2_size, size;
u32_t sum, sum2;
u64_t base_pos;
result_t res;
test_group("sector-unaligned positions, part one",
min_read != sector_size);
/* We can only do this test if the driver allows small read requests.
*/
if (min_read == sector_size)
return;
assert(sector_size % min_read == 0);
assert(min_read % element_size == 0);
/* Establish a baseline by writing and reading back three sectors; or
* by reading only, if writing is disabled.
*/
buf_size = buf2_size = sector_size * 3;
base_pos = (u64_t)sector_size * 3;
buf_ptr = alloc_dma_memory(buf_size);
buf2_ptr = alloc_dma_memory(buf2_size);
if (may_write) {
sum = fill_rand(buf_ptr, buf_size);
simple_xfer(driver_minor, base_pos, buf_ptr, buf_size, TRUE,
buf_size, &res);
got_result(&res, "write several sectors");
}
fill_rand(buf_ptr, buf_size);
simple_xfer(driver_minor, base_pos, buf_ptr, buf_size, FALSE, buf_size,
&res);
if (may_write)
test_sum(buf_ptr, buf_size, sum, TRUE, &res);
got_result(&res, "read several sectors");
/* Start with a simple test that operates within a single sector,
* first using a lead.
*/
fill_rand(buf2_ptr, sector_size);
sum = get_sum(buf2_ptr + min_read, sector_size - min_read);
simple_xfer(driver_minor, base_pos + sector_size - min_read,
buf2_ptr, min_read, FALSE, min_read, &res);
test_sum(buf2_ptr, min_read, get_sum(buf_ptr + sector_size - min_read,
min_read), TRUE, &res);
test_sum(buf2_ptr + min_read, sector_size - min_read, sum, TRUE,
&res);
got_result(&res, "single sector read with lead");
/* Then a trail. */
fill_rand(buf2_ptr, sector_size);
sum = get_sum(buf2_ptr, sector_size - min_read);
simple_xfer(driver_minor, base_pos, buf2_ptr + sector_size - min_read,
min_read, FALSE, min_read, &res);
test_sum(buf2_ptr + sector_size - min_read, min_read, get_sum(buf_ptr,
min_read), TRUE, &res);
test_sum(buf2_ptr, sector_size - min_read, sum, TRUE, &res);
got_result(&res, "single sector read with trail");
/* And then a lead and a trail, unless min_read is half the sector
* size, in which case this will be another lead test.
*/
fill_rand(buf2_ptr, sector_size);
sum = get_sum(buf2_ptr, min_read);
sum2 = get_sum(buf2_ptr + min_read * 2, sector_size - min_read * 2);
simple_xfer(driver_minor, base_pos + min_read, buf2_ptr + min_read,
min_read, FALSE, min_read, &res);
test_sum(buf2_ptr + min_read, min_read, get_sum(buf_ptr + min_read,
min_read), TRUE, &res);
test_sum(buf2_ptr, min_read, sum, TRUE, &res);
test_sum(buf2_ptr + min_read * 2, sector_size - min_read * 2, sum2,
TRUE, &res);
got_result(&res, "single sector read with lead and trail");
/* Now do the same but with three sectors, and still only one I/O
* vector element. First up: lead.
*/
size = min_read + sector_size * 2;
fill_rand(buf2_ptr, buf2_size);
sum = get_sum(buf2_ptr + size, buf2_size - size);
simple_xfer(driver_minor, base_pos + sector_size - min_read, buf2_ptr,
size, FALSE, size, &res);
test_sum(buf2_ptr, size, get_sum(buf_ptr + sector_size - min_read,
size), TRUE, &res);
test_sum(buf2_ptr + size, buf2_size - size, sum, TRUE, &res);
got_result(&res, "multisector read with lead");
/* Then trail. */
fill_rand(buf2_ptr, buf2_size);
sum = get_sum(buf2_ptr + size, buf2_size - size);
simple_xfer(driver_minor, base_pos, buf2_ptr, size, FALSE, size, &res);
test_sum(buf2_ptr, size, get_sum(buf_ptr, size), TRUE, &res);
test_sum(buf2_ptr + size, buf2_size - size, sum, TRUE, &res);
got_result(&res, "multisector read with trail");
/* Then lead and trail. Use sector size as transfer unit to throw off
* simplistic lead/trail detection.
*/
fill_rand(buf2_ptr, buf2_size);
sum = get_sum(buf2_ptr + sector_size, buf2_size - sector_size);
simple_xfer(driver_minor, base_pos + min_read, buf2_ptr, sector_size,
FALSE, sector_size, &res);
test_sum(buf2_ptr, sector_size, get_sum(buf_ptr + min_read,
sector_size), TRUE, &res);
test_sum(buf2_ptr + sector_size, buf2_size - sector_size, sum, TRUE,
&res);
got_result(&res, "multisector read with lead and trail");
/* Clean up. */
free_dma_memory(buf2_ptr, buf2_size);
free_dma_memory(buf_ptr, buf_size);
}
static void unaligned_pos2(void)
{
/* Test sector-unaligned positions and total sizes for requests, second
* part. This one tests the use of multiple I/O vector elements, and
* tries to push the limits of the driver by completely filling an I/O
* vector and going up to the maximum request size.
*/
u8_t *buf_ptr, *buf2_ptr;
size_t buf_size, buf2_size, max_block;
u32_t sum = 0L, sum2 = 0L, rsum[NR_IOREQS];
u64_t base_pos;
iovec_t iov[NR_IOREQS];
result_t res;
int i;
test_group("sector-unaligned positions, part two",
min_read != sector_size);
/* We can only do this test if the driver allows small read requests.
*/
if (min_read == sector_size)
return;
buf_size = buf2_size = max_size + sector_size;
base_pos = (u64_t)sector_size * 3;
buf_ptr = alloc_dma_memory(buf_size);
buf2_ptr = alloc_dma_memory(buf2_size);
/* First establish a baseline. We need two requests for this, as the
* total area intentionally exceeds the max request size.
*/
if (may_write) {
sum = fill_rand(buf_ptr, max_size);
simple_xfer(driver_minor, base_pos, buf_ptr, max_size, TRUE,
max_size, &res);
got_result(&res, "large baseline write");
sum2 = fill_rand(buf_ptr + max_size, sector_size);
simple_xfer(driver_minor, base_pos + max_size,
buf_ptr + max_size, sector_size, TRUE, sector_size,
&res);
got_result(&res, "small baseline write");
}
fill_rand(buf_ptr, buf_size);
simple_xfer(driver_minor, base_pos, buf_ptr, max_size, FALSE, max_size,
&res);
if (may_write)
test_sum(buf_ptr, max_size, sum, TRUE, &res);
got_result(&res, "large baseline read");
simple_xfer(driver_minor, base_pos + max_size, buf_ptr + max_size,
sector_size, FALSE, sector_size, &res);
if (may_write)
test_sum(buf_ptr + max_size, sector_size, sum2, TRUE, &res);
got_result(&res, "small baseline read");
/* First construct a full vector with minimal sizes. The resulting area
* may well fall within a single sector, if min_read is small enough.
*/
fill_rand(buf2_ptr, buf2_size);
for (i = 0; i < NR_IOREQS; i++) {
iov[i].iov_addr = (vir_bytes) buf2_ptr + i * sector_size;
iov[i].iov_size = min_read;
rsum[i] = get_sum(buf2_ptr + i * sector_size + min_read,
sector_size - min_read);
}
vir_xfer(driver_minor, base_pos + min_read, iov, NR_IOREQS, FALSE,
min_read * NR_IOREQS, &res);
for (i = 0; i < NR_IOREQS; i++) {
test_sum(buf2_ptr + i * sector_size + min_read,
sector_size - min_read, rsum[i], TRUE, &res);
memmove(buf2_ptr + i * min_read, buf2_ptr + i * sector_size,
min_read);
}
test_sum(buf2_ptr, min_read * NR_IOREQS, get_sum(buf_ptr + min_read,
min_read * NR_IOREQS), TRUE, &res);
got_result(&res, "small fully unaligned filled vector");
/* Sneak in a maximum sized request with a single I/O vector element,
* unaligned. If the driver splits up such large requests into smaller
* chunks, this tests whether it does so correctly in the presence of
* leads and trails.
*/
fill_rand(buf2_ptr, buf2_size);
simple_xfer(driver_minor, base_pos + min_read, buf2_ptr, max_size,
FALSE, max_size, &res);
test_sum(buf2_ptr, max_size, get_sum(buf_ptr + min_read, max_size),
TRUE, &res);
got_result(&res, "large fully unaligned single element");
/* Then try with a vector where each element is as large as possible.
* We don't have room to do bounds integrity checking here (we could
* make room, but this may be a lot of memory already).
*/
/* Compute the largest sector multiple which, when multiplied by
* NR_IOREQS, is no more than the maximum transfer size.
*/
max_block = max_size / NR_IOREQS;
max_block -= max_block % sector_size;
fill_rand(buf2_ptr, buf2_size);
for (i = 0; i < NR_IOREQS; i++) {
iov[i].iov_addr = (vir_bytes) buf2_ptr + i * max_block;
iov[i].iov_size = max_block;
}
vir_xfer(driver_minor, base_pos + min_read, iov, NR_IOREQS, FALSE,
max_block * NR_IOREQS, &res);
test_sum(buf2_ptr, max_block * NR_IOREQS, get_sum(buf_ptr + min_read,
max_block * NR_IOREQS), TRUE, &res);
got_result(&res, "large fully unaligned filled vector");
/* Clean up. */
free_dma_memory(buf2_ptr, buf2_size);
free_dma_memory(buf_ptr, buf_size);
}
static void sweep_area(u64_t base_pos)
{
/* Go over an eight-sector area from left (low address) to right (high
* address), reading and optionally writing in three-sector chunks, and
* advancing one sector at a time.
*/
u8_t *buf_ptr;
size_t buf_size;
u32_t sum = 0L, ssum[8];
result_t res;
int i, j;
buf_size = sector_size * 8;
buf_ptr = alloc_dma_memory(buf_size);
/* First (write to, if allowed, and) read from the entire area in one
* go, so that we know the (initial) contents of the area.
*/
if (may_write) {
sum = fill_rand(buf_ptr, buf_size);
simple_xfer(driver_minor, base_pos, buf_ptr, buf_size, TRUE,
buf_size, &res);
got_result(&res, "write to full area");
}
fill_rand(buf_ptr, buf_size);
simple_xfer(driver_minor, base_pos, buf_ptr, buf_size, FALSE, buf_size,
&res);
if (may_write)
test_sum(buf_ptr, buf_size, sum, TRUE, &res);
for (i = 0; i < 8; i++)
ssum[i] = get_sum(buf_ptr + sector_size * i, sector_size);
got_result(&res, "read from full area");
/* For each of the six three-sector subareas, first read from the
* subarea, check its checksum, and then (if allowed) write new content
* to it.
*/
for (i = 0; i < 6; i++) {
fill_rand(buf_ptr, sector_size * 3);
simple_xfer(driver_minor, base_pos + sector_size * i, buf_ptr,
sector_size * 3, FALSE, sector_size * 3, &res);
for (j = 0; j < 3; j++)
test_sum(buf_ptr + sector_size * j, sector_size,
ssum[i + j], TRUE, &res);
got_result(&res, "read from subarea");
if (!may_write)
continue;
fill_rand(buf_ptr, sector_size * 3);
simple_xfer(driver_minor, base_pos + sector_size * i, buf_ptr,
sector_size * 3, TRUE, sector_size * 3, &res);
for (j = 0; j < 3; j++)
ssum[i + j] = get_sum(buf_ptr + sector_size * j,
sector_size);
got_result(&res, "write to subarea");
}
/* Finally, if writing was enabled, do one final readback. */
if (may_write) {
fill_rand(buf_ptr, buf_size);
simple_xfer(driver_minor, base_pos, buf_ptr, buf_size, FALSE,
buf_size, &res);
for (i = 0; i < 8; i++)
test_sum(buf_ptr + sector_size * i, sector_size,
ssum[i], TRUE, &res);
got_result(&res, "readback from full area");
}
/* Clean up. */
free_dma_memory(buf_ptr, buf_size);
}
static void sweep_and_check(u64_t pos, int check_integ)
{
/* Perform an area sweep at the given position. If asked for, get an
* integrity checksum over the beginning of the disk (first writing
* known data into it if that is allowed) before doing the sweep, and
* test the integrity checksum against the disk contents afterwards.
*/
u8_t *buf_ptr;
size_t buf_size;
u32_t sum = 0L;
result_t res;
if (check_integ) {
buf_size = sector_size * 3;
buf_ptr = alloc_dma_memory(buf_size);
if (may_write) {
sum = fill_rand(buf_ptr, buf_size);
simple_xfer(driver_minor, 0ULL, buf_ptr, buf_size,
TRUE, buf_size, &res);
got_result(&res, "write integrity zone");
}
fill_rand(buf_ptr, buf_size);
simple_xfer(driver_minor, 0ULL, buf_ptr, buf_size, FALSE,
buf_size, &res);
if (may_write)
test_sum(buf_ptr, buf_size, sum, TRUE, &res);
else
sum = get_sum(buf_ptr, buf_size);
got_result(&res, "read integrity zone");
}
sweep_area(pos);
if (check_integ) {
fill_rand(buf_ptr, buf_size);
simple_xfer(driver_minor, 0ULL, buf_ptr, buf_size, FALSE,
buf_size, &res);
test_sum(buf_ptr, buf_size, sum, TRUE, &res);
got_result(&res, "check integrity zone");
free_dma_memory(buf_ptr, buf_size);
}
}
static void basic_sweep(void)
{
/* Perform a basic area sweep.
*/
test_group("basic area sweep", TRUE);
sweep_area((u64_t)sector_size);
}
static void high_disk_pos(void)
{
/* Test 64-bit absolute disk positions. This means that after adding
* partition base to the given position, the driver will be dealing
* with a position above 32 bit. We want to test the transition area
* only; if the entire partition base is above 32 bit, we have already
* effectively performed this test many times over. In other words, for
* this test, the partition must start below 4GB and end above 4GB,
* with at least four sectors on each side.
*/
u64_t base_pos;
base_pos = 0x100000000ULL | (sector_size * 4);
base_pos -= base_pos % sector_size;
/* The partition end must exceed 32 bits. */
if (part.base + part.size < base_pos) {
test_group("high disk positions", FALSE);
return;
}
base_pos -= sector_size * 8;
/* The partition start must not. */
if (base_pos < part.base) {
test_group("high disk positions", FALSE);
return;
}
test_group("high disk positions", TRUE);
base_pos -= part.base;
sweep_and_check(base_pos, part.base == 0ULL);
}
static void high_part_pos(void)
{
/* Test 64-bit partition-relative disk positions. In other words, use
* within the current partition a position that exceeds a 32-bit value.
* This requires the partition to be more than 4GB in size; we need an
* additional 4 sectors, to be exact.
*/
u64_t base_pos;
/* If the partition starts at the beginning of the disk, this test is
* no different from the high disk position test.
*/
if (part.base == 0ULL) {
/* don't complain: the test is simply superfluous now */
return;
}
base_pos = 0x100000000ULL | (sector_size * 4);
base_pos -= base_pos % sector_size;
if (part.size < base_pos) {
test_group("high partition positions", FALSE);
return;
}
test_group("high partition positions", TRUE);
base_pos -= sector_size * 8;
sweep_and_check(base_pos, TRUE);
}
static void high_lba_pos1(void)
{
/* Test 48-bit LBA positions, as opposed to *24-bit*. Drivers that only
* support 48-bit LBA ATA transfers, will treat the lower and upper 24
* bits differently. This is again relative to the disk start, not the
* partition start. For 512-byte sectors, the lowest position exceeding
* 24 bit is at 8GB. As usual, we need four sectors more, and fewer, on
* the other side. The partition that we're operating on, must cover
* this area.
*/
u64_t base_pos;
base_pos = (1ULL << 24) * sector_size;
/* The partition end must exceed the 24-bit sector point. */
if (part.base + part.size < base_pos) {
test_group("high LBA positions, part one", FALSE);
return;
}
base_pos -= sector_size * 8;
/* The partition start must not. */
if (base_pos < part.base) {
test_group("high LBA positions, part one", FALSE);
return;
}
test_group("high LBA positions, part one", TRUE);
base_pos -= part.base;
sweep_and_check(base_pos, part.base == 0ULL);
}
static void high_lba_pos2(void)
{
/* Test 48-bit LBA positions, as opposed to *28-bit*. That means sector
* numbers in excess of 28-bit values; the old ATA upper limit. The
* same considerations as above apply, except that we now need a 128+GB
* partition.
*/
u64_t base_pos;
base_pos = (1ULL << 28) * sector_size;
/* The partition end must exceed the 28-bit sector point. */
if (part.base + part.size < base_pos) {
test_group("high LBA positions, part two", FALSE);
return;
}
base_pos -= sector_size * 8;
/* The partition start must not. */
if (base_pos < part.base) {
test_group("high LBA positions, part two", FALSE);
return;
}
test_group("high LBA positions, part two", TRUE);
base_pos -= part.base;
sweep_and_check(base_pos, part.base == 0ULL);
}
static void high_pos(void)
{
/* Check whether the driver deals well with 64-bit positions and
* 48-bit LBA addresses. We test three cases: disk byte position beyond
* what fits in 32 bit, in-partition byte position beyond what fits in
* 32 bit, and disk sector position beyond what fits in 24 bit. With
* the partition we've been given, we may not be able to test all of
* them (or any, for that matter).
*/
/* In certain rare cases, we might be able to perform integrity
* checking on the area that would be affected if a 32-bit/24-bit
* counter were to wrap. More specifically: we can do that if we can
* access the start of the disk. This is why we should be given the
* entire disk as test area if at all possible.
*/
basic_sweep();
high_disk_pos();
high_part_pos();
high_lba_pos1();
high_lba_pos2();
}
static void open_primary(void)
{
/* Open the primary device. This call has its own test group.
*/
test_group("device open", TRUE);
open_device(driver_minor);
}
static void close_primary(void)
{
/* Close the primary device. This call has its own test group.
*/
test_group("device close", TRUE);
close_device(driver_minor);
assert(nr_opened == 0);
}
static void do_tests(void)
{
/* Perform all the tests.
*/
open_primary();
misc_ioctl();
bad_read1();
bad_read2();
/* It is assumed that the driver implementation uses shared
* code paths for read and write for the basic checks, so we do
* not repeat those for writes.
*/
bad_write();
vector_and_large();
part_limits();
unaligned_size();
unaligned_pos1();
unaligned_pos2();
high_pos();
close_primary();
}
static int sef_cb_init_fresh(int UNUSED(type), sef_init_info_t *UNUSED(info))
{
/* Initialize.
*/
int r;
clock_t now;
if (env_argc > 1)
optset_parse(optset_table, env_argv[1]);
if (driver_label[0] == '\0')
panic("no driver label given");
if (ds_retrieve_label_endpt(driver_label, &driver_endpt))
panic("unable to resolve driver label");
if (driver_minor > 255)
panic("invalid or no driver minor given");
if ((r = getticks(&now)) != OK)
panic("unable to get uptime: %d", r);
srand48(now);
output("BLOCKTEST: driver label '%s' (endpt %d), minor %d\n",
driver_label, driver_endpt, driver_minor);
do_tests();
output("BLOCKTEST: summary: %d out of %d tests failed "
"across %d group%s; %d driver deaths\n",
failed_tests, total_tests, failed_groups,
failed_groups == 1 ? "" : "s", driver_deaths);
/* The returned code will determine the outcome of the RS call, and
* thus the entire test. The actual error code does not matter.
*/
return (failed_tests) ? EINVAL : OK;
}
static void sef_local_startup(void)
{
/* Initialize the SEF framework.
*/
sef_setcb_init_fresh(sef_cb_init_fresh);
sef_startup();
}
int main(int argc, char **argv)
{
/* Driver task.
*/
env_setargs(argc, argv);
sef_local_startup();
return 0;
}