nbd
2021-10-21
Network Block Device server library.
nbd
This is a Network Block Device (aka nbd) library. This implements most of the NBD protocol in order to let you write custom NBD servers.
Example
A simple example is shown in simple-in-memory/. It is showing an in-memory NBD server for a 100MB block device.
A minimal example would be:
(defvar *export-name* "test") (defvar *export-size* (* 500 1024 1024)) ;; The actual block device data! (defvar *data* (make-array *export-size* :element-type '(unsigned-byte 8))) (defun main (path) (nbd/api:wait (nbd/api:start #'on-request path *export-name* *export-size* nil))) (defun on-request (type flags handle offset length stream start-reply) (declare (ignore flags handle)) (case type (:read (funcall start-reply) (write-sequence *data* stream :start offset :end (+ offset length))) (:write (read-sequence *data* stream :start offset :end (+ offset length)) (funcall start-reply))) (finish-output stream))
After running (main "/tmp/test.sock")
, you can use nbd-client -unix /tmp/test.sock /dev/nbd0 -name test
to setup the NBD device.
[root@host ~]# nbd-client -unix /tmp/test.sock /dev/nbd0 -name test
Negotiation: ..size = 500MB
Connected /dev/nbd0
[root@host ~]# fdisk -l /dev/nbd0
Disk /dev/nbd0: 500 MiB, 524288000 bytes, 1024000 sectors
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 4096 bytes / 4096 bytes
Performance has not been optimized but is not terrible:
[root@host ~]# dd if=/dev/zero of=/dev/nbd0 bs=4k count=100000
100000+0 records in
100000+0 records out
409600000 bytes (410 MB, 391 MiB) copied, 2.04382 s, 200 MB/s
Current limitations
- Only works on SBCL, because USOCKET doesn't support UNIX sockets.
- Only UNIX sockets are currently supported.
- Not all flags are supported, i.e. only READ/WRITE/DISCONNECT/FLUSH/TRIM requests can occur.
- lisp-binary currently has a breaking bug that needs to be locally fixed before this library works.
Reference
The nbd/api
package exposes a few symbols:
start
: Function. Starts the NBD server. This spawns a thread to accept incoming connections, and a thread for each client connection.wait
: Function. Waits for the NBD server thread to finish.
The signature of start
is as follows:
(defun start (on-request path export-name export-size supported-flags &optional export-description (backlog 100) (min-block-size 1) (preferred-block-size 4096) (max-block-size nil)))
on-request
: callback called for each NBD request. The arguments are:- The type of the request, currently one of
:read
,:write
,:flush
,:trim
, or:disc
. - The flags of the request, in a list. The only valid value so
far is
:flag-fua
. - The handle of the request, aka an arbitrary number identifying the request. Servers can reply in arbitrary order, so this is helpful to identify concurrent threads.
- Offset of the device at which the request is starting.
- Length of the requested data.
- Stream to write to in order to respond.
- Function writing to the stream to start the response. The
handle is written to the stream by this function. Accepts an
error code as its only argument, one of
:ok
,:eperm
,:eio
,:enomem
,:einval
,:enospc
,:eoverflow
,:enotsup
, or:eshutdown
. The argument defaults to:ok
if not provided.
- The type of the request, currently one of
path
: UNIX domain socket path to listen on.export-name
: export name to expose.export-size
: size of the export to expose.supported-flags
: list of the supported features. See below for the list of flags.export-description
: export description to expose. Defaults to the export name.backlog
: listen backlog of the socket.min-block-size
: minimum block size exposed.preferred-block-size
: preferred block size exposed.max-block-size
: maximum block size exposed. Defaults to export-size.
The supported flags must be passed as a list, e.g. (list nbd/api:*read-only*)
. The full list of currently supported flags is:
*read-only*
: set the block device as read-only.:write
and:trim
commands will return:eperm
.*send-flush*
: the server can receive:flush
commands.*send-fua*
: the server can receive the:flag-fua
as part of command flags.*rotational*
: the block device has rotational disk characteristics. This is a hint for the client that random accesses are not performant.*send-trim*
: the server can receive:trim
commands.
Roadmap (one day...)
- Support for TCP sockets using USOCKET. Unix sockets need SBCL as USOCKET doesn't support them.
- Add support for supported flags present in NBD/LIB/TRANSMISSION-FLAGS.
License
MIT License.