All of lore.kernel.org
 help / color / mirror / Atom feed
* fuse_tcmur - access tcmu-runner devices through a fuse mount
@ 2019-07-23 21:01 David Butterfield
  0 siblings, 0 replies; only message in thread
From: David Butterfield @ 2019-07-23 21:01 UTC (permalink / raw)
  To: target-devel

[-- Attachment #1: Type: text/plain, Size: 4159 bytes --]

DESCRIPTION
    fuse_tcmur provides access to tcmu-runner devices through a fuse(8) mount.
    Each tcmu-runner device is represented by a node in the fuse filesystem
    tree.  The devices can be accessed via the nodes, for example with dd(1),
    and can be mounted as filesystems.  

    The diagram shows the components of the fuse_tcmur program (to the right of
    kernel).  fuse_tcmur sits in the middle, providing a main program and the
    translation between fuse operations and tcmu-runner handler operations.

    fuse_tree is a simple tree-structure implemented on libfuse.  Like /proc,
    the existence of nodes in the tree is controlled only by the server, not
    through filesystem operations.  But individual leaf nodes may be readable
    and/or writable through the filesystem depending on permissions.

                                       .--fuse_operations
                                       |
    /tcmur <==> kernel <==> libfuse <==> fuse_tree
                                             ^ |
                                      -errno | | fuse_node_ops
                                              \V
                                        fuse_tcmur
                                            ^  |
                                  cmd->done |  | calls to tcmur_*
                                            |  V
                                          libtcmur <==> tcmur-handler

    tcmur-handler is one of the binaries in /usr/local/lib/tcmu-runner/.

    libtcmur is a usermode API to the loadable tcmu-runner handlers.  LIO, TCMU,
    and tcmur-runner are uninvolved -- libtcmur only loads and uses the
    handlers, and only the block-I/O entry points are called (not handle_cmd).

    fuse_tree(3) and libtcmur(3) are independent -- each usable for its purpose
    without reference to the other.  In the middle is fuse_tcmur, which links
    them together, and consists of three parts:

        The main() program initializes the other parts and calls fuse_main().

        The fuse_tcmur part translates I/O requests between fuse and libtcmur.

        The fuse_tcmur_ctl part interprets commands written to a node in the
        fuse filesystem -- commands can be written with cat(1) or echo(1) to
        /tcmur/dev/tcmur.

FILES
    When a handler is loaded, an empty directory node appears for it in
    /tcmur/sys/modules

    When a device is added, a node appears in /tcmur/dev/<subtype><minornumber>

    The /tcmur/dev nodes appear as regular files (rather than block devices),
    but they can still be mounted as filesystems, e.g.

            sudo mount /tcmur/dev/ram000 /mnt/k

NOTES
    The source is in the libtcmur branch (default) at
    https://github.com/DavidButterfield/tcmu-runner.git

    First make in the main tcmu-runner directory, to get version.h and the binary
    handlers:
            cmake .
            make
            sudo make install

    Then cd into the libtcmur subdirectory where there is a hacked-up makefile
    that creates the fuse_tcmur binary.  Today there are no dependencies
    computed, so always
            make clean; make

    The program will attempt to create the mount-point directory that fuse will
    mount on.  This will succeed if the program is run as superuser; otherwise
    you can create it manually first with
            sudo mkdir /tcmur

    The server presently runs only one thread.  Despite this, the time for a
    script to download a few repositories and build a software package is only a
    few percent longer through the fuse mount to a tcmu-runner ramdisk, as
    compared with a regular kernel filesystem mount to my home directory
    spinning disk.  (Not an optimal comparison, but it's what I have at hand.)

BUGS
    No doubt.  This code was born in July 2019.  The makefile leaves much to be
    desired.

    So far I have only tested it using handler_ram.so, because that's the
    handler that can be used without figuring out how to install and run
    sophisticated back-end software.

SEE ALSO
    fuse_tree(3), libtcmur(3),  fuse(8),  tcmu-runner(8)

AUTHOR
    David A. Butterfield

Manpage updated 23 Jul 2019

[-- Attachment #2: fuse_tcmur.1 --]
[-- Type: text/plain, Size: 4880 bytes --]

fuse_tcmur(1)                    User Commands                     fuse_tcmur(1)

NAME
    fuse_tcmur - access tcmu-runner devices through a fuse mount

SYNOPSIS
    fuse_tcmur
    echo "command" > /tcmur/dev/tcmur

    Commands:
        load subtype                    # load tcmu-runner handler for subtype
        unload subtype

        add minornumber /subtype/handler-cfg-string
        remove minornumber

        help
        source command-file-name        # read commands from file
        dump                            # dump the fuse tree structure

DESCRIPTION
    fuse_tcmur provides access to tcmu-runner devices through a fuse(8) mount.
    Each tcmu-runner device is represented by a node in the fuse filesystem
    tree.  The devices can be accessed via the nodes, for example with dd(1),
    and can be mounted as filesystems.  

    Each tcmu device is denoted by a unique minor number, which is specified
    when the device is added.  All tcmu-runner handlers share one minor-space.

    The diagram shows the components of the fuse_tcmur program (to the right of
    kernel).  fuse_tcmur sits in the middle, providing a main program and the
    translation between fuse operations and tcmu-runner handler operations.

    fuse_tree is a simple tree-structure implemented on libfuse.  Like /proc,
    the existence of nodes in the tree is controlled only by the server, not
    through filesystem operations.  But individual leaf nodes may be readable
    and/or writable through the filesystem depending on permissions.

                                       .--fuse_operations
                                       |
    /tcmur <==> kernel <==> libfuse <==> fuse_tree
                                             ^ |
                                      -errno | | fuse_node_ops
                                              \V
                                        fuse_tcmur
                                            ^  |
                                  cmd->done |  | calls to tcmur_*
                                            |  V
                                          libtcmur <==> tcmur-handler

    tcmur-handler is one of the binaries in /usr/local/lib/tcmu-runner/.

    libtcmur is a usermode API to the loadable tcmu-runner handlers.  LIO, TCMU,
    and tcmur-runner are uninvolved -- libtcmur only loads and uses the
    handlers, and only the block-I/O entry points are called (not handle_cmd).

    fuse_tree(3) and libtcmur(3) are independent -- each usable for its purpose
    without reference to the other.  In the middle is fuse_tcmur, which links
    them together, and consists of three parts:

        The main() program initializes the other parts and calls fuse_main().

        The fuse_tcmur part translates I/O requests between fuse and libtcmur.

        The fuse_tcmur_ctl part interprets commands written to a node in the
        fuse filesystem -- commands can be written with cat(1) or echo(1) to
        /tcmur/dev/tcmur.

FILES
    When a handler is loaded, an empty directory node appears for it in
    /tcmur/sys/modules

    When a device is added, a node appears in /tcmur/dev/<subtype><minornumber>

    The /tcmur/dev nodes appear as regular files (rather than block devices),
    but they can still be mounted as filesystems, e.g.

            sudo mount /tcmur/dev/ram000 /mnt/k

NOTES
    The source is in the libtcmur branch (default) at
    https://github.com/DavidButterfield/tcmu-runner.git

    First make in the main tcmu-runner directory, to get version.h and the binary
    handlers:
            cmake .
            make
            sudo make install

    Then cd into the libtcmur subdirectory where there is a hacked-up makefile
    that creates the fuse_tcmur binary.  Today there are no dependencies
    computed, so always
            make clean; make

    The program will attempt to create the mount-point directory that fuse will
    mount on.  This will succeed if the program is run as superuser; otherwise
    you can create it manually first with
            sudo mkdir /tcmur

    The server presently runs only one thread.  Despite this, the time for a
    script to download a few repositories and build a software package is only a
    few percent longer through the fuse mount to a tcmu-runner ramdisk, as
    compared with a regular kernel filesystem mount to my home directory
    spinning disk.  (Not an optimal comparison, but it's what I have at hand.)

BUGS
    No doubt.  This code was born in July 2019.  The makefile leaves much to be
    desired.

    So far I have only tested it using handler_ram.so, because that's the
    handler that can be used without figuring out how to install and run
    sophisticated back-end software.

SEE ALSO
    fuse_tree(3), libtcmur(3),  fuse(8),  tcmu-runner(8)

AUTHOR
    David A. Butterfield

Manpage updated 23 Jul 2019

[-- Attachment #3: fuse_tree.3 --]
[-- Type: text/plain, Size: 4484 bytes --]

fuse_tree(3)                Linux Programmer's Manual               fuse_tree(3)

NAME
    fuse_tree -- API to fuse filesystem tree

SYNOPSIS
    #include "fuse_tree.h"

    error_t fuse_tree_init(const char * mountpoint);
    error_t fuse_tree_exit(void);

    error_t fuse_loop_run(void * unused);

    fuse_node_t fuse_node_add(
		const char * name, fuse_node_t parent, mode_t,
		const struct fuse_node_ops *, uintptr_t data);

    error_t fuse_node_remove(const char * name , fuse_node_t parent);

    fuse_node_t fuse_tree_mkdir(const char * name, fuse_node_t parent);
    error_t fuse_tree_rmdir(const char * name, fuse_node_t parent);

    fuse_node_t fuse_node_lookup(const char * path);

    uintptr_t fuse_node_data_get(fuse_node_t);

    void fuse_node_update_mode(fuse_node_t, mode_t);
    void fuse_node_update_size(fuse_node_t, size_t);
    void fuse_node_update_mtime(fuse_node_t);

    char * fuse_tree_fmt(void);

DESCRIPTION
    fuse_tree maintains a filesystem tree, implementing fuse_operations.  Like
    /proc, the tree itself is managed internally by the application -- there is
    no creation of files or directories through system calls on the mounted fuse
    filesystem.

    However, also like /proc, individual files represented in the tree may be
    readable and/or writable through the mounted filesystem, depending on
    permissions.

    When adding a node to the tree, the application can supply a fuse_node_ops
    vector specifying functions to be called to back filesystem operations on the
    node.  The struct fuse_node_ops includes these members, all of which are
    optional to fill in:

        int     (*open)   (fuse_node_t, uintptr_t data);
        int     (*release)(fuse_node_t, uintptr_t data);
        int     (*fsync)  (uintptr_t data, int datasync);
        ssize_t (*read)   (uintptr_t data, void * buf, size_t, loff_t);
        ssize_t (*write)  (uintptr_t data, const char * buf, size_t, loff_t);

    fuse_tree_init() should be called before any of the other calls described
    here, passing the path to the mount point to be used for the fuse mount.

    fuse_tree_exit() should be called last after any other calls described here.

    fuse_loop_run() should be called to run fuse_main().

    fuse_node_add() adds a node with the given name under the given parent node.
    fuse_node_remove() removes it.  The last "data" argument is private to the caller
    and is passed to the fuse_node_ops callback functions.

    fuse_tree_mkdir() creates a new child directory fuse_node with the specified
    name under the specified parent fuse_node.  fuse_tree_rmdir() removes it.

    fuse_node_lookup() returns a pointer to the fuse_node representing the path
    string. Path string is the full path from the fuse mount, starting with '/'.

    fuse_node_data_get() returns the private data specified to fuse_node_add.

    fuse_node_update_mode() updates the fuse_node's mode permissions.

    fuse_node_update_size() updates the fuse_node's size in bytes.

    fuse_node_update_mtime() updates the fuse_node's modification time to the
    present.

    fuse_tree_fmt() returns a human-readable string representing the fuse tree.
    The string should be freed by the caller when done with it.

RETURN VALUE
    Upon successful completion, functions returning type error_t return zero.
    Failures return -errno.

    fuse_node_add() and fuse_tree_mkdir() each return a pointer to the new
    fuse_node, or NULL on error.

    fuse_node_lookup() returns a pointer to the fuse_node, or NULL if the path
    is not found.

    fuse_tree_fmt() returns a freeable human-readable debugging string
    representing the fuse tree.

ERRORS
    fnode_remove()
	-EBUSY		fuse_node is open by some process through the fuse FS
	-ENOENT		named fnode not found under parent

    fuse_tree_rmdir()
	-ENOENT		named fnode not found under parent
	-ENOTEMPTY	directory node is not empty

    fuse_tree_init()
	-EINVAL		mountpoint does not start with '/', or it ends in '/'

    fuse_tree_exit()
	-EBUSY		root node still has child(ren)

    fuse_loop_run()
	errors returned by fuse_main()
	errors returned by asprintf()

NOTES

BUGS
    fuse_node_lookup() should hold the returned node, and a new function should
    be added to drop it.

    There should be a threading option passed to fuse_loop_run().  At present
    it always runs fuse single-threaded.

SEE ALSO
    fuse(8)

AUTHOR
    David A. Butterfield

Manpage updated 23 Jul 2019

[-- Attachment #4: libtcmur.3 --]
[-- Type: text/plain, Size: 6547 bytes --]

libtcmur(3)                 Linux Programmer's Manual                libtcmur(3)

NAME
    libtcmur -- usermode API to tcmu-runner block storage handlers

SYNOPSIS
    #include "libtcmur.h"

    error_t libtcmur_init(const char * handler_prefix);
    error_t libtcmur_exit(void);

    error_t tcmur_handler_load(const char * subtype);
    error_t tcmur_handler_unload(const char * subtype);

    error_t tcmur_check_config(char const * cfgstring);
    error_t tcmur_device_add(int minor, const char * cfgstring);
    error_t tcmur_device_remove(int minor);

    error_t tcmur_read(int minor, struct tcmulib_cmd *,
			struct iovec *, size_t niov, size_t, loff_t);
    error_t tcmur_write(int minor, struct tcmulib_cmd *,
			struct iovec *, size_t niov, size_t, loff_t);
    error_t tcmur_flush(int minor, struct tcmulib_cmd *);

    ssize_t tcmur_get_size(int minor);
    ssize_t tcmur_get_block_size(int minor);
    ssize_t tcmur_get_max_xfer(int minor);

    const char * tcmur_get_dev_name(int minor);

DESCRIPTION
    libtcmur provides a usermode application programming interface to access
    block storage services through tcmu-runner block storage handlers.

    Note that LIO, TCMU and tcmu-runner are uninvolved -- libtcmur only calls
    the tcmu-runner loadable storage handlers, e.g. qcow, glfs, ram, etc.

    libtcmur makes use of the handler read, write, and flush block I/O
    functions only -- no calls are made to handle_cmd().

    Functions returning type error_t return zero for success, otherwise -errno.

    Call libtcmur_init() once before using libtcmur services.  If handler_prefix
    is NULL, the default is used: "/usr/local/lib/tcmu-runner/handler_".  The
    expected handler paths are the concatenation of:

		    handler_prefix  tcmu_subtype  ".so"

    Call libtcmur_exit() once last, after any other functions described here.

    tcmur_handler_load() will dlopen() a tcmu-runner handler of the given
    subtype and load it into the program for use.  tcmur_handler_unload()
    unloads it.

    tcmur_check_config() checks a handler device configuration string for
    validity.  The handler for the configuration must already have been loaded
    using tcmur_handler_load().  cfgstring takes this form, specifying the
    handler's TCMU subtype:
		    /subtype/handler-cfg-string

    See tcmu-runner(8) for more about subtype and handler-cfg-string.

    tcmur_device_add() adds a device, with specified cfgstring, as the
    specified tcmur minor number.  tcmur_device_remove() removes the specified
    minor.  The handler is determined from the subtype in the first segment of
    cfgstring.  All tcmur subtypes share a common space of minor numbers.

    tcmur_read(), tcmur_write() and tcmur_flush() start I/O operations to the
    specified minor.  Errors in the I/O start process can be reported by -errno
    return from these calls.  A return value of zero denotes a successful I/O
    start, in which case there will be a completion call to cmd->done(), which
    may report either an "sts" error, or success (denoted by TCMU_STS_OK).

    Note that the completion call may occur before the request call returns.

    tcmur_read() and tcmur_write() take an iovec array with niov elements, an
    I/O size in bytes, and a seek offset into the device where the I/O begins.

    struct tcmulib_cmd includes this field, which must be set before passing
    the command to tcmur_read(), tcmur_write(), or tcmur_flush():

	cmd_done_t done;	/* completion callback */

    The callback function is of this type:

	typedef
	void (*cmd_done_t)(struct tcmu_device *, struct tcmulib_cmd *, int);

    The third argument to the callback is TCMU status (see libtcmu_common.h).

    tcmur_get_size() returns the size in bytes of the specified minor.  If the
    minor does not exist then the return is a -errno.

    tcmur_get_block_size() returns the block size in bytes of the specified
    minor.  If the minor does not exist then the return is a -errno.

    tcmur_get_max_xfer() returns the maximum I/O size in bytes of the specified
    minor.  If the minor does not exist then the return is a -errno.

    tcmur_get_dev_name() returns the device name of the specified minor.  If
    the minor does not exist then the return is NULL.

RETURN VALUE
    Upon successful completion, functions returning type error_t return zero.
    All functions return -errno on failure, except tcmur_get_dev_name(), which
    returns NULL in that case.

ERRORS
    tcmur_read()
    tcmur_write()
	-ENODEV	    no device at specified minor (including minor out of range)
	-ENXIO	    handler does not implement the requested function
	-EINVAL	    I/O would exceed device bounds
	-EIO	    I/O completed with nonzero "sts"

    tcmur_flush()
	-ENODEV	    no device at specified minor
	-EIO	    I/O completed with nonzero "sts"

    tcmur_get_size(), tcmur_get_block_size(), tcmur_get_max_xfer()
	-ENODEV	    no device at specified minor

    tcmur_device_add()
	-ENODEV	    minor number out of range
	-EBUSY	    minor number already in use by prior add
	errors returned by tcmur_check_config()
	errors returned by rhandler->open()

    tcmur_device_remove()
	-ENODEV	    no device at specified minor

    tcmur_check_config()
	-ENXIO	    no loaded handler subtype matches this config string
	-EINVAL	    config string does not start with '/'
	-EINVAL	    config string is too long
	errors returned by rhandler->check_config()

    tcmur_handler_load()
	-EEXIST	    handler already loaded for the specified subtype
	-ENOSPC	    all handler slots are in use
	-ENOMEM	    failed to asprintf the handler path
	-ENOENT	    failed to dlopen the handler path
	-EBADF	    failed to dlsym("handler_init")
	-EIO	    handler_init returned non-zero

    tcmur_handler_unload()
	-ENOENT	    no handler is loaded for the specified subtype
	-EBUSY	    handler has existing devices (added but not removed)

    libtcmur_exit()
	-EBUSY	    a handler is still loaded

NOTES

BUGS
    Not all symbols possibly referenced by handlers are implemented.
    Some such symbols are "stubbed out" and print a warning if called.
    Others do not exist, which will disallow loading of referencing handlers.

    There is no check for an attempt to add the same device twice.

    There is no generic way to specify the device size, block size, or maximum
    I/O size.

    libtcmur_exit() should auto-unload any handlers that have no devices
    currently added.

SEE ALSO
    tcmu-runner(8)

AUTHOR
    David A. Butterfield

Manpage updated 23 Jul 2019

^ permalink raw reply	[flat|nested] only message in thread

only message in thread, other threads:[~2019-07-23 21:01 UTC | newest]

Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2019-07-23 21:01 fuse_tcmur - access tcmu-runner devices through a fuse mount David Butterfield

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.