Conventions =========== A brief overview of conventions used for the xNVMe C codebase. C: format --------- clang-format is utilized to format the code. There are two clang-format-option files, one for the headers (``toolbox/clang-format-h``) and one for the source ``toolbox/clang-format-c``. You can setup your editor to use the format-files, or run clang-format via the toolbox: ``make format-all``. This will format all files in the repository. C: style -------- * Declare variables - At the **top** of code-blocks; before any logic, e.g. - at the top of a function-code-block / loop-nest / anonymous-block - for-loop "control/iterator" variables should be declared and initialized in the for-loop header. - In the code-block nearest to its utilization - in a function, the top-most scope is commonly used for argument-unpacking, and the idiomatic ``int err`` variable, along with auxilary variables needed by the function body in the outer-most scope - variable-declaration in nested-block-scope is encouraged; still at the top of that block-scope. For example, when a variable is only ever used in a nested-block, then declare it within that scope * Initialize variables - Using zero/constant-initialization; ``struct foo = {0};`` - To unpack arguments; ``int simple = arg->is->alot->simple;`` - Do not initialize using functions that require error-handling * Global variables - Prefix them with ``g_`` * Compiler-specific features - For example, gnu/gcc-only is discouraged, as it hinders portability - Code should preferably translate using any of gcc/clang/icc/pgi - Exceptions to this would be code in backends utilizing features which are only available on the platform that the backend services C: API ------ * The API and its backend implementations aims to be consistent with the following - Pass pointers to "objects" e.g. ``xnvme_cmd_pass(*ctx, ...);`` - Pass double-pointers for "object-initialization" e.g. ``xnvme_queue(..., **queue);`` - On success, return 0. On error, Return negative ``errno``. - Exceptions: API-calls mimicking legacy interfaces, such as the ``xnvme_buf_alloc()`` which is operating similar to ``malloc()/free()``. * The API and its backend implementation cannot be assumed to be thread-safe * Be minimal with definitions in the public API - E.g. the 'sys/queue.h' was removed from the public API as it gave several head-aches when building xNVMe on multiple platforms. However, internally, specifically in backend implementations, assumptions can be made on the availability of certain headers and their general availability * Each function in the API must have a command-line tool exercising it - The source-code for the command-line tools serve as examples of utilizing the C API in general and the tools are used during testing - The command-line utilities must use the ``libxnvme_cli.h`` as this provides a common command-line interface, the common-cli is nice as for usability, such that all cli-tools use the same arguments, it is also essential for instrumentation of not just the logic of the tool but also the library backend C: Backend Implementations -------------------------- The backend interface is declared in the internal header ``xnvme_be.h``. It consists of function-pointers for different tasks grouped together in structs. Those different set of tasks are referred to as the function-interfaces, they are labeled / grouped by: * **dev** : device handles and device enumeration * **mem** : memory allocation for command-payloads aka buffers * **admin** : synchronous admin-command processing * **sync** : synchronous I/O command processing * **async** : asynchronous I/O command submission/completion via queues Additionally, a backend-state is available for specialization to a given backend and its function-interface implementations. * Should have a header-file defining the backend and what it exposes - Located in ``include/xnvme_be_.h`` - Have a state-struct named ``xnvme_be__state`` - Provide ``extern struct xnvme_be_ *`` definitions for the interface implementations, these are used when "mixing-in" the implementations into the backend * Should be implemented in a file named ``lib/xnvme_be___.c`` - The ```` is a name uniquely identifying the interface-implementation - Example: the Linux backend, named **linux**, has the **async** implementation of the **async** interface utilizing **io_uring** in a file named: * ``xnvme_be_linux_async_liburing.c`` - Example: The SPDK backend, named **spdk**, has the **async** implementation using the SPDK NVMe driver in a file named: * ``xnvme_be_spdk_async_nvme.c`` * Should use static function declarations to avoid symbol-leak - The ``cmd_io()`` backend interface function should be declared ``static int cmd_io(...)`` * In case an interface-function should be shared by other backends, then provide the full "name", backend/interface/ident/function - Example: for the Common Backend Implementations (CBI), the **sync** implementation using **psync** for the **cmd_iov()** interface function, then the function is named: ``xnvme_be_cbi_sync_psync_cmd_io()``