From patchwork Sun Dec 16 16:32:36 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: "Wiles, Keith" X-Patchwork-Id: 48953 Return-Path: X-Original-To: patchwork@dpdk.org Delivered-To: patchwork@dpdk.org Received: from [92.243.14.124] (localhost [127.0.0.1]) by dpdk.org (Postfix) with ESMTP id D17501B912; Sun, 16 Dec 2018 17:32:51 +0100 (CET) Received: from mga09.intel.com (mga09.intel.com [134.134.136.24]) by dpdk.org (Postfix) with ESMTP id 623031B8BE for ; Sun, 16 Dec 2018 17:32:46 +0100 (CET) X-Amp-Result: SKIPPED(no attachment in message) X-Amp-File-Uploaded: False Received: from fmsmga001.fm.intel.com ([10.253.24.23]) by orsmga102.jf.intel.com with ESMTP/TLS/DHE-RSA-AES256-GCM-SHA384; 16 Dec 2018 08:32:43 -0800 X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.56,361,1539673200"; d="scan'208";a="130405369" Received: from asooresh-mobl1.amr.corp.intel.com ([10.254.183.55]) by fmsmga001.fm.intel.com with ESMTP; 16 Dec 2018 08:32:42 -0800 From: Keith Wiles To: dev@dpdk.org Date: Sun, 16 Dec 2018 10:32:36 -0600 Message-Id: <20181216163239.87702-1-keith.wiles@intel.com> X-Mailer: git-send-email 2.18.0 MIME-Version: 1.0 Subject: [dpdk-dev] [PATCH 1/4] dfs:add FUSE based filesystem for DPDK X-BeenThere: dev@dpdk.org X-Mailman-Version: 2.1.15 Precedence: list List-Id: DPDK patches and discussions List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: dev-bounces@dpdk.org Sender: "dev" DFS stands for DPDK Filesystem, which helps expose data and control files in a FUSE based filesystem. The dfs requires libfuse3 and libjansson to be present in the Linux system. DFS provides a simplified API on top of the FUSE APIs to simplify creating and using FUSE. Here is the github repo: https://github.com/akheron/jansson Here is the github repo: https://github.com/libfuse/libfuse Please use you system updater tool yum, apt-get, ... to add support for these two libraries. Also read the dfs documentation in the docs directory for more details. The DFS is only setup for Linux at this time with FreeBSD and windows support coming later. Signed-off-by: Keith Wiles --- MAINTAINERS | 5 + config/common_linuxapp | 5 + doc/guides/prog_guide/dfs_lib.rst | 660 ++++++++++++++++++++++ doc/guides/prog_guide/index.rst | 1 + examples/Makefile | 1 + examples/dfs/Makefile | 70 +++ examples/dfs/main.c | 86 +++ examples/dfs/meson.build | 13 + lib/Makefile | 2 + lib/librte_dfs/Makefile | 51 ++ lib/librte_dfs/dfs.c | 842 +++++++++++++++++++++++++++++ lib/librte_dfs/dfs.h | 768 ++++++++++++++++++++++++++ lib/librte_dfs/dfs_cryptodev.c | 53 ++ lib/librte_dfs/dfs_cryptodev.h | 25 + lib/librte_dfs/dfs_dpdk.c | 323 +++++++++++ lib/librte_dfs/dfs_dpdk.h | 37 ++ lib/librte_dfs/dfs_eal.c | 652 ++++++++++++++++++++++ lib/librte_dfs/dfs_eal.h | 102 ++++ lib/librte_dfs/dfs_ethdev.c | 324 +++++++++++ lib/librte_dfs/dfs_ethdev.h | 23 + lib/librte_dfs/dfs_mempool.c | 130 +++++ lib/librte_dfs/dfs_mempool.h | 26 + lib/librte_dfs/dfs_noop.c | 35 ++ lib/librte_dfs/dfs_oper.c | 836 ++++++++++++++++++++++++++++ lib/librte_dfs/dfs_oper.h | 48 ++ lib/librte_dfs/dfs_rawdev.c | 54 ++ lib/librte_dfs/dfs_rawdev.h | 25 + lib/librte_dfs/dfs_ring.c | 107 ++++ lib/librte_dfs/dfs_ring.h | 25 + lib/librte_dfs/dfs_search.c | 203 +++++++ lib/librte_dfs/dfs_search.h | 266 +++++++++ lib/librte_dfs/dfs_timer.c | 58 ++ lib/librte_dfs/dfs_timer.h | 25 + lib/librte_dfs/meson.build | 47 ++ lib/librte_dfs/rte_dfs_version.map | 29 + lib/meson.build | 2 +- mk/rte.app.mk | 3 + 37 files changed, 5961 insertions(+), 1 deletion(-) create mode 100644 doc/guides/prog_guide/dfs_lib.rst create mode 100644 examples/dfs/Makefile create mode 100644 examples/dfs/main.c create mode 100644 examples/dfs/meson.build create mode 100644 lib/librte_dfs/Makefile create mode 100644 lib/librte_dfs/dfs.c create mode 100644 lib/librte_dfs/dfs.h create mode 100644 lib/librte_dfs/dfs_cryptodev.c create mode 100644 lib/librte_dfs/dfs_cryptodev.h create mode 100644 lib/librte_dfs/dfs_dpdk.c create mode 100644 lib/librte_dfs/dfs_dpdk.h create mode 100644 lib/librte_dfs/dfs_eal.c create mode 100644 lib/librte_dfs/dfs_eal.h create mode 100644 lib/librte_dfs/dfs_ethdev.c create mode 100644 lib/librte_dfs/dfs_ethdev.h create mode 100644 lib/librte_dfs/dfs_mempool.c create mode 100644 lib/librte_dfs/dfs_mempool.h create mode 100644 lib/librte_dfs/dfs_noop.c create mode 100644 lib/librte_dfs/dfs_oper.c create mode 100644 lib/librte_dfs/dfs_oper.h create mode 100644 lib/librte_dfs/dfs_rawdev.c create mode 100644 lib/librte_dfs/dfs_rawdev.h create mode 100644 lib/librte_dfs/dfs_ring.c create mode 100644 lib/librte_dfs/dfs_ring.h create mode 100644 lib/librte_dfs/dfs_search.c create mode 100644 lib/librte_dfs/dfs_search.h create mode 100644 lib/librte_dfs/dfs_timer.c create mode 100644 lib/librte_dfs/dfs_timer.h create mode 100644 lib/librte_dfs/meson.build create mode 100644 lib/librte_dfs/rte_dfs_version.map diff --git a/MAINTAINERS b/MAINTAINERS index 71ba31208..e555ec5b7 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1215,6 +1215,11 @@ F: test/bpf/ F: test/test/test_bpf.c F: doc/guides/prog_guide/bpf_lib.rst +DFS - EXPERIMENTAL +M: Keith Wiles +F: lib/librte_dfs +F: examples/dfs +F: doc/guides/prog_guide/dfs_lib.rst Test Applications ----------------- diff --git a/config/common_linuxapp b/config/common_linuxapp index 6c1c8d0f4..70f55508e 100644 --- a/config/common_linuxapp +++ b/config/common_linuxapp @@ -56,3 +56,8 @@ CONFIG_RTE_LIBRTE_PMD_DPAA2_QDMA_RAWDEV=y # NXP ENETC PMD Driver # CONFIG_RTE_LIBRTE_ENETC_PMD=y + +# +# DPDK File System (DFS) enable building. +# +CONFIG_RTE_LIBRTE_DFS=n diff --git a/doc/guides/prog_guide/dfs_lib.rst b/doc/guides/prog_guide/dfs_lib.rst new file mode 100644 index 000000000..d55e6b4c8 --- /dev/null +++ b/doc/guides/prog_guide/dfs_lib.rst @@ -0,0 +1,660 @@ +.. SPDX-License-Identifier: BSD-3-Clause + Copyright(c) 2018 Intel Corporation. + +DPDK File System (DFS) +====================== + +This DPDK library provides a pseudo file system much like Linux /proc or /system +file systems, but this one is a userspace file system. Allowing applications to +create and design file system directories and files using the FUSE 3.2 +https://github.com/libfuse/libfuse code. + +The libfuse library can be installed via the Linux distro packaging system or +you can compile the code from the git repo above. + +The DFS also requires json library called ``Jansson`` which can be installed via +your Linux distro packaging system or from https://github.com/akheron/jansson website. +Version 2.11.0 is the version used on my Ubuntu 18.04 LTS system. + +The kernel needs to support FUSE to use the libfuse library and most currently +Linux kernels and systems do support FUSE. For FreeBSD and Windows a port has +been created for these two OSes, but they are at different completeness. + +The DFS library creates layer on top of the libfuse APIs to similfy the +developers design by providing cleaner APIs and not requiring the developers +to understand the FUSE design. + +.. note:: + + Make sure you enable 'user_allow_other' in /etc/fuse.conf file. + + A set of signals (HUP, INT, TERM, SEGV and PIPE) are set up to be caught + these signals and bring down the FUSE file system by removing the mount + point and temporay files. The PIPE signal is captured and ignored. + +Advantages +---------- + +- Provides a simple userspace file system for information and configuration. +- Simple abstraction layer on top of FUSE to reduce learning curve and code. +- Able to dynamicly add/remove files and directories. +- A simple API using a data struct to create the file. +- Able to read and write files in the file system to get information and to configure +- Allows applications on top of DPDK to create its own directory/file tree. +- Able to access the files from Linux command line, scripts or any other method. +- Orchestration can access the file to configure applications and DPDK with out having to build a communication path to the DPDK/App process. + +The libfuse design has two levels of APIs 'lowlevel' and 'highlevel'. For DFS +the lowlevel APIs were used for a bit more performance, but mostly because the +lowlevel APIs provide better control for DFS. + +In DFS the developer needs only add files and/or directories plus manage the +simple event callbacks in his code. The files/directories within DFS are +defined at runtime, but FUSE/DFS also allow for modification during runtime. + +To make the developer job easier a the DFS system calls back to the developer +code with simple operations e.g. INIT, OPEN, INFO, READ, WRITE, RELEASE. + +The developer routine only needs to act on a operation it when it is required as +all except the INIT operation is optional. If the developer does something +special in say the OPEN he may need to also handle the RELEASE operation to +cleanup any items he needed for the open. + +The READ and WRITE operations are normally handled in the layers below DFS and +the developer can ignore these unless he utilizes something special. In a number +of files in the librte_dfs directory you will notice the use ``dfs_tmpfile_create`` and +``dfs_tmpfile_release``. These routines help create temporay files to hold generated +data. Please look at these files to see how to use these APIs. + +Configuration +------------- + +By enabling the define ``CONFIG_RTE_LIBRTE_DFS=y`` you can enable the filesystem. +The DFS library is optional and is not required for normal operations of DPDK. +Currently the DPDK Filesystem is disabled in the mk/common_base file. To enable +DFS copy one of the config files e.g. defconfig_x86_64-native-linuxapp-gcc to say +defconfig_x86_64-dfs-linuxapp-gcc then edit the file and add ``CONFIG_RTE_LIBRTE_DFS=y`` +to the file. Then use this config files to build and run DFS. + +To have the application create the filesystem for the application a EAL argument +needs to be placed on the commandline before and user arguments ``--``. The new +command line argument is ``--dfs``, which if left off will *not* create the filesystem. + +If the ``CONFIG_RTE_LIBRTE_DFS=n`` then the ``--dfs`` option has no effect. The lib/librte_dfs +directory will be built, but only to resolve possible defines in the application if configured +to use DFS. Not all of the functions are allowed if the application is built with DFS disabled. + +The functions currently allowed to exist are ``dfs_is_configured()``, ``dfs_is_enabled()`` and +``dfs_set_defaults()``. + +The DFS code will create a default mount point for the filesystem with a root +directory called ``/dpdk/-``, then using the application name 'prgname' +to complete the path for the application. If more then one DPDK application is +running then each will be created in the ``/dpdk`` directory. The ``/dpdk`` directory +will be create if it does not exist. + +The DFS mount point can be shown using the ``mount`` command and look for the +``/dpdk/-`` mount point. The mount point needs to be un-mounted using +something like ``sudo umount `` command if needed. The DFS code will attempt to +remove the mount as long as the application can terminate normally, which includes using +signal handlers. + +In the ``example/dfs/main.c`` file you can set the name of the application and +the mount point via the ``dfs_set_defaults(const char *name, const char *mountpoint, _sig_handler_t sig_handle)`` API. +The mount point will default to ``/dpdk/`` unless the above function is given before ``rte_eal_init()`` is called. +The application name will also default to *dpdk* as the name of the directory. + +You can retrieve the ``struct dfs`` pointer using the call ``dfs_get_instance()`` if needed +for other calls like ``dfs_destroy(struct dfs *dfs)``. + +By using the ``--dfs`` command line option the application can use the default DPDK +file system files and/or create his own files/directories using the DFS APIs. + +APIs +---- +The main API for DFS is: + +Example Files +============= + +Header file +----------- +.. code-block:: c + + /* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2018 Intel Corporation. + */ + + #ifndef _DFS_RAWDEV_H_ + #define _DFS_RAWDEV_H_ + + /** + * @file + * RTE DFS Rawdev Operations + * + * These routines supply the Rawdev usage support routines. + */ + + #ifdef __cplusplus + extern "C" { + #endif + + #define DFS_RAWDEV_INFO_FILE "rawdev-info.tmp" + + #ifdef __cplusplus + } + #endif + + #endif /* _DFS_RAWDEV_H_ */ + +Code above is the header file for dfs_rawdev.c file. + +Their can only be one DFS running in a DPDK process at a time and any attempts +to create more then one will always return the same DFS system. + +'C' file +-------- + +.. code-block:: c + + /* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2018 Intel Corporation. + */ + + #define _GNU_SOURCE + + #include + #include + #include + #include + + #include + #include + #include + #include + + #include "dfs.h" + #include "dfs_rawdev.h" + + #ifdef RTE_LIBRTE_RAWDEV + static int + rawdev_info(struct dfs_node *node, oper_t opt) + { + static char count[24]; + + switch(opt) { + case DFS_FILE_INIT: + snprintf(count, sizeof(count), "%d\n", + rte_rawdev_count()); + dfs_node_init(node, count, -1, 0); + break; + + /* These are here just for information and not included + * in the real file. + */ + case DFS_FILE_INFO: + /* FALLTHRU */ + case DFS_FILE_RELEASE: + /* FALLTHRU */ + case DFS_FILE_OPEN: + /* FALLTHRU */ + case DFS_FILE_READ: + /* FALLTHRU */ + case DFS_FILE_WRITE: + /* FALLTHRU */ + default: + break; + } + + return 0; + } + + static struct dfs_tree rawdev_tree[] = { + u_dir("rawdev"), + u_file("count", rawdev_info, DFS_FILE_RDONLY, 0), + u_end() + }; + + int + _rawdev_init(void) + { + DFS_LOG(DEBUG, "DFS Setup RAWDEV \n"); + return dfs_add_tree(NULL, rawdev_tree); + } + + RTE_INIT(dfs_rawdev_init) + { + dfs_register("Rawdev", _rawdev_init); + } + #endif + +The file above is the 'C' code to create and handle the rawdev DFS directory and +any sub-files or directories. Lets skip to bottom of the file to ``dfs_raw_init()`` +routine. The ``dfs_rawdev_init()`` does only one thing call the ``dfs_add_tree()`` +function create/add the entries in the ``rawdev_tree[]`` structure. + +Notice the ``u_dir()`` and ``u_file`` entries, these are macros to help create +each entry of the given type. Directory entries just have a name of the directory, +but the files have a ``name``, function pointer, access mode and unique ID value within +the directory it sits. + +Note the order of creating directories and files must be done correctly. When you +define a directory e.g. ``u_dir("foo")`` the next set of ``u_file()`` entries will +be placed in the ``foo`` directory. + +If you need to add a file or directory to a different location just specify the +path in the name e.g. ``"../file"`` or ``"/mempool/newfile"`` will work +with directories too. + +The unique ID value is any 32 bit number defined by the developer and is used +when the developer uses a single function for a number of files to help identify +the file to be accessed. + +In this case it is set to zero by default. The ``count`` file is read-only and +the function to handle the file is called ``rawdev_info``. The function is called +with a ``node pointer`` and ``operation value``. The ``node pointer`` is the +``struct dfs_node`` and contains information about the node like type or size, ... + +Now look in the ``rawdev_info()`` function a ``static char count[24];`` variable is +defined to hold the number of rawdev devices. We have a number types of operations +that can be performed on a file. + +* DFS_FILE_INIT - Called only when the file/node is created. +* DFS_FILE_INFO - Called when the FUSE file system needs to know something about the file like size or access mode. +* DFS_FILE_RELEASE - Called when the FUSE file system no longer needs the file. This does not mean it is to be removed only that FUSE decided to stop tracking the file for now. +* DFS_FILE_OPEN - Called when the FUSE file is opened (optional) +* DFS_FILE_READ - Called just before the file is to be read, which movement of the data is handled by the lower layers ``dfs_oper.c`` +* DFS_FILE_WRITE - Called when the file has been written to or modified. + +Notice in the ``DFS_FILE_INIT`` section the ``count`` string is filled in using +snprintf() and the value from the ``rte_rawdev_count()`` routine. The function +to initialize the node/file is called ``dfs_node_init()`` passing the node pointer, +memory buffer and two others -1 and zero. The ``-1`` being passed is to inform the +routine to use strlen() on the memory address ``count`` to determine its size. The +last value is zero meaning the max file size is zero enforcing the file is read-only. + +The file size will be the length of the string in ``count`` array. You can pass +the real size here or give a max file size if you allow writing into the file. + +You could have used the ``OPEN`` operation to retrieve the value and recreate the +string each time the file is opened, but in this case the value does not change +and we can setup the string at ``INIT`` time. + +.. code-block:: c + + /** + * Initialize the node structure. + * + * @param node + * Pointer to the node structure + * @param p + * Pointer to the node data, can be NULL. As the pointer will be + * provided in the DFS_FILE_INFO event callback. + * @param s + * The size of the node data. If -1 then use strlen(p) for size. + * @param ms + * The max file size. If 0 then not writeable, >0 then the max amount of data + * that is able to be pushed in the file. + * + */ + static inline void + dfs_node_init(struct dfs_node *node, void *p, ssize_t s, size_t ms) + { + node->file_fd = -1; + if (s < 0) + node->file_size = strlen(p); + else + node->file_size = (size_t)s; + node->file_data = p; + node->file_offset = 0; + node->maxfile_size = ms; + } + +The ``node->file_data`` pointer will point to the ``count`` array and the lower layers +will operate on that buffer using the ``node->file_size`` value. Notice the +``node->file_fd`` this file descriptor value can point to an opened file instead +of a buffer and the lower DFS layer will handle access to this fd. + +Look in ``dfs_mempool.c`` in function ``info_file`` it will create +temporay file containing the json output, which the FUSE layer will read to +obtain the information about the mempool structures. + +The method is used on the ``info`` file in the mempool directory and the ``dump`` +uses the same method with different output format, which redirects the +``rte_mempool_list_dump(FILE *f)`` output to a tempory file referenced by the +``node->file_fd``. + +The tempory files are created in the ``/tmp`` directory and then ``unlink`` is used +to make them invisiable plus when the application exits the files are removed. + +The dpdk/examples/dfs example file +---------------------------------- + +Leaving out some of the DPDK port setup details. + +.. code-block:: c + + static int force_quit; + + /* + * The lcore main. This routine just loops forever with a rte_pause() call. + */ + static void + lcore_main(void) + { + printf("\nCore %u. [Ctrl+C to quit]\n", rte_lcore_id()); + + /* Run until the application is stopped or killed. */ + + while (!force_quit) + rte_pause(); + } + + static void + signal_handler(int signum) + { + if (signum == SIGINT || signum == SIGTERM) { + printf("\n\nSignal %d received, preparing to exit...\n", + signum); + force_quit = true; + } + } + + /* + * The main function, which uses only one core and starts the DPDK file system + */ + int + main(int argc, char *argv[]) + { + int ret; + + force_quit = false; + + dfs_set_defaults(basename(argv[0]), NULL, signal_handler); + + /* Initialize the Environment Abstraction Layer (EAL) and DFS. */ + ret = rte_eal_init(argc, argv); + if (ret < 0) + rte_exit(EXIT_FAILURE, "Error with EAL initialization\n"); + + printf("*** DFS: %s Configured and %s Enabled\n", + dfs_is_configured() ? "is" : "is not", + dfs_is_enabled() ? "is" : "is not"); + + if (!dfs_is_configured() || !dfs_is_enabled()) { + signal(SIGINT, signal_handler); + signal(SIGTERM, signal_handler); + } + + /* Call lcore_main on the master core only. */ + lcore_main(); + + return 0; + } + +The main function in the above code for DFS is the ``dfs_set_defaults()`` +function call passing the program name, a NULL pointer for the base mount point and +the function pointer to a signal handler if needed. The DFS system only handles signals +``SIGINT`` and ``SIGTERM``. + +The base mount point being NULL will use the default location ``/dpdk``. The default +mount point is ``basename(argv[0])`` or program name. If the ``--dfs`` is on the +command line the DFS will be created during ``rte_eal_init()`` call. + +In the example command lines below if you use the example/dfs application the + will be 'dfs' followed by the process ID number. + +Example command lines +--------------------- + +Using the dpdk/example/dfs program executed using ``sudo ./build/dfs -l 1 --dfs`` + +Example command line for dfs application +---------------------------------------- + +.. code-block:: console + + sudo ./build/dfs -l 1 --dfs + +Sample directory tree +--------------------- + +.. code-block:: console + + /dpdk/-/ + ├── copyright - DPDK copyright + ├── debug - Debug directory + │   ├── dump_fs - Dump the DFS internal filesystem + │   ├── hash - Dump the DFS dir/file hash table + │   ├── scratch - A scratch space file for testing writes/reads + │   └── sizes - Sizes of some DFS structures + │   ├── dfs - Size of the DFS structure + │   └── dfs_node - Size of the DFS inode structure + ├── devbind-net - dpdk-devbind.py output + ├── eal + │   ├── rte-lcores - write only file to add/remove RTE lcores + │   ├── service-lcores - write only file to add/remove SERVICE lcores + │   ├── bus + │   │   ├── buses - List of all of the buses + │   │   ├── dpaa + │ │   │ └── list - list of DPAA devices + │   │   ├── fslmc + │ │   │ └── list - list of DPAA devices + │   │   ├── ifpga + │ │   │ └── list - list of DPAA devices + │   │   ├── pci + │ │   │ ├── list-all - list of all PCI devices + │ │   │ └── list-ether - list of ethernet PCI devices + │   │   └── vdev + │ │   └── list - list of vdev devices + │   ├── config.json - Configuration information of DPDK in json format. + │   ├── hypervisor - Are in a hypervisor + │   ├── lcore-all.json - List of all lcores in JSON format + │   ├── lcore-cnt - Number of lcore allocated to DPDK + │   ├── lcore-list.json - List of lcores with roles attached + │   ├── memzones.json - List of memzones + │   ├── roles - List of Roles for lcores + │   └── socket-cnt - The number of sockets in the system + │   └── tailqs.json - List of tailqs in JSON format + ├── ethdev + │   ├── avail_count - Number of available ports in the system + │   ├── port-0 - Directory for each port with control and information + │   │   ├── led - Turn on and off the LED on the NIC + │   │   ├── link.json - The link status of the port + │   │   ├── mtu - The MTU of the device + │   │   ├── socket_id - The socket ID for this device is attached + │   │   ├── stat_reset - Clear the stats for this port + │   │   └── stats.json - Dump out the stats in JSON format + │   ├── port-1 + │   │   ├── led + │   │   ├── link.json + │   │   ├── mtu + │   │   ├── socket_id + │   │   ├── stat_reset + │   │   └── stats.json + │   ├── port-2 + │   │   ├── led + │   │   ├── link.json + │   │   ├── mtu + │   │   ├── socket_id + │   │   ├── stat_reset + │   │   └── stats.json + │   ├── port-3 + │   │   ├── led + │   │   ├── link.json + │   │   ├── mtu + │   │   ├── socket_id + │   │   ├── stat_reset + │   │   └── stats.json + │   ├── port-4 + │   │   ├── led + │   │   ├── link.json + │   │   ├── mtu + │   │   ├── socket_id + │   │   ├── stat_reset + │   │   └── stats.json + │   ├── port-5 + │   │   ├── led + │   │   ├── link.json + │   │   ├── mtu + │   │   ├── socket_id + │   │   ├── stat_reset + │   │   └── stats.json + │   ├── port-6 + │   │   ├── led + │   │   ├── link.json + │   │   ├── mtu + │   │   ├── socket_id + │   │   ├── stat_reset + │   │   └── stats.json + │   ├── port-7 + │   │   ├── led + │   │   ├── link.json + │   │   ├── mtu + │   │   ├── socket_id + │   │   ├── stat_reset + │   │   └── stats.json + │   └── total_count - Total number of ports found. + ├── fuse-version - Fuse version string + ├── mempool - Mempool information + │   ├── dump - raw dump in ACSII of mempool structures + │   └── info.json - JSON format of mempool structures + ├── pid - Process ID of this DPDK instance + ├── rawdev - Information about rawdev devices + │   └── count - Number of rawdev devices + ├── ring - Ring information + │   └── info.json - JSON format of all rings + ├── timer - Timer information + │   └── dump - RAW ASCII information about timers + ├── type - Primary or Secondary process type + └── version - DPDK version string + +.. code-block:: console + + $cat /dpdk/-/version + DPDK 18.08.0-rc0 + $ + + $ cat /dpdk/-/copyright + BSD LICENSE + + Copyright(c) 2010-2018 Intel Corporation. All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + * Neither the name of Intel Corporation nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + SPDX-License-Identifier: BSD-3-Clause + + $ + +Output format of the files should be JSON or simple text. If the data is large +or it needs to be read quickly by a script JSON is the best format. Using the +Jansson library https://jansson.readthedocs.io/en/2.4/ to format the output or +read the input should be used. + +.. code-block:: console + + $ cat /dpdk/-/eal/config + { + "master_lcore": 1, + "lcore_count": 1, + "numa_node_count": 2, + "service_lcore_count": 0, + "lcore_role": ".R..........................................", + "process_type": "Primary", + "iova_mode": "PA", + "mem_config": { + "magic": 19820526, + "nchannel": 0, + "nrank": 0, + "mem_cfg_addr": 140285840723968, + "memzones": { + "name": "memzone", + "count": 581, + "len": 2560, + "elt_sz": 72 + } + } + } + + $ cat /dpdk/-/ethdev/port-0/stats + { + "port_id": 0, + "ipackets": 0, + "opackets": 0, + "ibytes": 0, + "obytes": 0, + "imissed": 0, + "ierrors": 0, + "oerrors": 0, + "rx_nombuf": 0, + "q_ipackets": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + "q_opackets": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + "q_ibytes": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + "q_obytes": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + "q_errors": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + } + +Install notes for Ubuntu 18.04 Linux +------------------------------------ + +The DFS support in DPDK needs the Jansson JSON library and libfuse to build. +The github repo: https://github.com/akheron/jansson +The github repo: https://github.com/libfuse/libfuse + +but you should install the library using +apt-get or yum or whatever means you have to install distro libraries. + +I am using Ubuntu 18.04 and include the libraries via: +'sudo apt-get install libjansson-dev' +'sudo apt-get install libfuse-dev' + +Build DPDK as you normally would, I use: + +$ export RTE_SDK=`pwd` +$ export RTE_TARGET=x86_64-native-linuxapp-gcc + +$ make install T=${RTE_TARGET} -j // You will get an error about not being able to install, it is OK + +Then + +$ sudo mkdir /dpdk +$ sudo chmod 755 /dpdk + +I created a dpdk-user to set an owner to the files. + +$ sudo adduser --disabled-login --no-create-home --disabled-password dpdk-user +$ sudo chown dpdk-user /dpdk +$ sudo chgrp dpdk-user /dpdk + +$ cd examples/dfs +$ make -j + +To run the example: + +$ sudo -E ./build/dfs -l 1 // You can add more lcores, but they will do nothing. + +$ cd /dpdk/dfs- // This is the location of the FUSE filesystem. diff --git a/doc/guides/prog_guide/index.rst b/doc/guides/prog_guide/index.rst index ba8c1f6ad..0f1d21605 100644 --- a/doc/guides/prog_guide/index.rst +++ b/doc/guides/prog_guide/index.rst @@ -54,6 +54,7 @@ Programmer's Guide vhost_lib metrics_lib bpf_lib + dfs_lib source_org dev_kit_build_system dev_kit_root_make_help diff --git a/examples/Makefile b/examples/Makefile index 33fe0e586..cfe2279e8 100644 --- a/examples/Makefile +++ b/examples/Makefile @@ -14,6 +14,7 @@ DIRS-$(CONFIG_RTE_LIBRTE_BBDEV) += bbdev_app DIRS-$(CONFIG_RTE_LIBRTE_PMD_BOND) += bond DIRS-y += cmdline DIRS-$(CONFIG_RTE_LIBRTE_DISTRIBUTOR) += distributor +DIRS-$(CONFIG_RTE_LIBRTE_DFS) += dfs DIRS-y += ethtool DIRS-y += exception_path DIRS-$(CONFIG_RTE_LIBRTE_EFD) += server_node_efd diff --git a/examples/dfs/Makefile b/examples/dfs/Makefile new file mode 100644 index 000000000..6b284dc35 --- /dev/null +++ b/examples/dfs/Makefile @@ -0,0 +1,70 @@ +# SPDX-License-Identifier: BSD-3-Clause +# Copyright(c) 2018 Intel Corporation + +# binary name +APP = dfs + +# all source are stored in SRCS-y +SRCS-y := main.c + +# Build using pkg-config variables if possible +$(shell pkg-config --exists libdpdk) +ifeq ($(.SHELLSTATUS),0) + +all: shared +.PHONY: shared static +shared: build/$(APP)-shared + ln -sf $(APP)-shared build/$(APP) +static: build/$(APP)-static + ln -sf $(APP)-static build/$(APP) + +PC_FILE := $(shell pkg-config --path libdpdk) +CFLAGS += -O3 -g +CFLAGS += -DALLOW_EXPERIMENTAL_API +CFLAGS += $(shell pkg-config --cflags libdpdk) +CFLAGS += $(shell pkg-config --cflags fuse3) +CFLAGS += $(shell pkg-config --cflags jansson) +LDFLAGS_SHARED = $(shell pkg-config --libs libdpdk) +LDFLAGS_SHARED += $(shell pkg-config --libs-only-l fuse3) +LDFLAGS_SHARED += $(shell pkg-config --libs-only-l jansson) + +LDFLAGS_STATIC = -Wl,-Bstatic $(shell pkg-config --static --libs libdpdk) +LDFLAGS_STATIC += $(shell pkg-config --static --libs-only-l fuse3) +LDFLAGS_STATIC += $(shell pkg-config --static --libs-only-l jansson) + +build/$(APP)-shared: $(SRCS-y) Makefile $(PC_FILE) | build + $(CC) $(CFLAGS) $(SRCS-y) -o $@ $(LDFLAGS) $(LDFLAGS_SHARED) + +build/$(APP)-static: $(SRCS-y) Makefile $(PC_FILE) | build + $(CC) $(CFLAGS) $(SRCS-y) -o $@ $(LDFLAGS) $(LDFLAGS_STATIC) + +build: + @mkdir -p $@ + +.PHONY: clean +clean: + rm -f build/$(APP) build/$(APP)-static build/$(APP)-shared + rmdir --ignore-fail-on-non-empty build + +else # Build using legacy build system + +ifeq ($(RTE_SDK),) +$(error "Please define RTE_SDK environment variable") +endif + +# Default target, can be overridden by command line or environment +RTE_TARGET ?= x86_64-native-linuxapp-gcc + +include $(RTE_SDK)/mk/rte.vars.mk + +CFLAGS += -O3 -g $(WERROR_FLAGS) +CFLAGS += -DALLOW_EXPERIMENTAL_API + +# workaround for a gcc bug with noreturn attribute +# http://gcc.gnu.org/bugzilla/show_bug.cgi?id=12603 +ifeq ($(CONFIG_RTE_TOOLCHAIN_GCC),y) +CFLAGS_main.o += -Wno-return-type +endif + +include $(RTE_SDK)/mk/rte.extapp.mk +endif diff --git a/examples/dfs/main.c b/examples/dfs/main.c new file mode 100644 index 000000000..ff4072cf8 --- /dev/null +++ b/examples/dfs/main.c @@ -0,0 +1,86 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2018 Intel Corporation + */ + +/* example command: + * # will not create the /dpdk/dfs- directory + * sudo ./build/dfs -l 1 + * + * # will create the /dpdk/dfs- directory + * sudo ./build/dfs -l 1 --dfs + * + * Adding the '--dfs' will enable the filesystem. + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include + +static int force_quit; + +/* + * The lcore main. This routine just loops forever with a rte_pause() call. + */ +static void +lcore_main(void) +{ + printf("\nCore %u. [Ctrl+C to quit]\n", rte_lcore_id()); + + /* Run until the application is stopped or killed. */ + + while (!force_quit) + rte_pause(); +} + +static void +signal_handler(int signum) +{ + if (signum == SIGINT || signum == SIGTERM) { + printf("\n\nSignal %d received, preparing to exit...\n", + signum); + force_quit = true; + } +} + +/* + * The main function, which uses only one core and starts the DPDK file system + */ +int +main(int argc, char *argv[]) +{ + int ret; + + force_quit = false; + + dfs_set_defaults(basename(argv[0]), NULL, signal_handler); + + /* Initialize the Environment Abstraction Layer (EAL) and DFS. */ + ret = rte_eal_init(argc, argv); + if (ret < 0) + rte_exit(EXIT_FAILURE, "Error with EAL initialization\n"); + + printf("*** DFS: %s Configured and %s Enabled\n", + dfs_is_configured() ? "is" : "is not", + dfs_is_enabled() ? "is" : "is not"); + + if (!dfs_is_configured() || !dfs_is_enabled()) { + signal(SIGINT, signal_handler); + signal(SIGTERM, signal_handler); + } + + /* Call lcore_main on the master core only. */ + lcore_main(); + + return 0; +} diff --git a/examples/dfs/meson.build b/examples/dfs/meson.build new file mode 100644 index 000000000..5923f8957 --- /dev/null +++ b/examples/dfs/meson.build @@ -0,0 +1,13 @@ +# SPDX-License-Identifier: BSD-3-Clause +# Copyright(c) 2018 Intel Corporation + +# meson file, for building this example as part of a main DPDK build. +# +# To build this example as a standalone application with an already-installed +# DPDK instance, use 'make' + +allow_experimental_apis = true +sources = files( + 'main.c' +) +deps += [ 'dfs' ] diff --git a/lib/Makefile b/lib/Makefile index b7370ef97..ae6de9bd5 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -21,6 +21,8 @@ DEPDIRS-librte_timer := librte_eal DIRS-$(CONFIG_RTE_LIBRTE_CFGFILE) += librte_cfgfile DIRS-$(CONFIG_RTE_LIBRTE_CMDLINE) += librte_cmdline DEPDIRS-librte_cmdline := librte_eal +DIRS-y += librte_dfs +DEPDIRS-librte_dfs := librte_eal librte_ethdev librte_timer librte_pci DIRS-$(CONFIG_RTE_LIBRTE_ETHER) += librte_ethdev DEPDIRS-librte_ethdev := librte_net librte_eal librte_mempool librte_ring DEPDIRS-librte_ethdev += librte_mbuf diff --git a/lib/librte_dfs/Makefile b/lib/librte_dfs/Makefile new file mode 100644 index 000000000..0fcacc843 --- /dev/null +++ b/lib/librte_dfs/Makefile @@ -0,0 +1,51 @@ +# SPDX-License-Identifier: BSD-3-Clause +# Copyright(c) 2018 Intel Corporation + +include $(RTE_SDK)/mk/rte.vars.mk + +# library name +LIB = librte_dfs.a + +CFLAGS += $(WERROR_FLAGS) -I$(SRCDIR) -O3 +CFLAGS += -DALLOW_EXPERIMENTAL_API -D_FILE_OFFSET_BITS=64 +CFLAGS += -D_GNU_SOURCE +CFLAGS += -I$(RTE_SDK)/drivers/bus/pci +LDLIBS += -lrte_eal -lrte_mempool -lrte_hash -lrte_ethdev -lrte_utils +LDLIBS += -lrte_ring -lrte_timer -lrte_rawdev -lrte_cryptodev +LDLIBS += -lpthread +LDLIBS += $(shell pkg-config --libs-only-l fuse3) +LDLIBS += $(shell pkg-config --libs-only-l jansson) + +EXPORT_MAP := rte_dfs_version.map + +LIBABIVER := 1 + +# all source are stored in SRCS-y +ifeq ($(CONFIG_RTE_LIBRTE_DFS),y) +SRCS-y := dfs.c dfs_oper.c dfs_search.c dfs_dpdk.c + +SRCS-$(CONFIG_RTE_LIBRTE_DFS) += dfs_eal.c +SRCS-$(CONFIG_RTE_LIBRTE_DFS) += dfs_ethdev.c +SRCS-$(CONFIG_RTE_LIBRTE_DFS) += dfs_mempool.c +SRCS-$(CONFIG_RTE_LIBRTE_DFS) += dfs_ring.c +SRCS-$(CONFIG_RTE_LIBRTE_DFS) += dfs_timer.c +SRCS-$(CONFIG_RTE_LIBRTE_DFS) += dfs_rawdev.c +SRCS-$(CONFIG_RTE_LIBRTE_DFS) += dfs_cryptodev.c + +SYMLINK-y-include += dfs.h dfs_oper.h dfs_search.h dfs_dpdk.h + +SYMLINK-$(CONFIG_RTE_LIBRTE_DFS)-include += dfs_eal.h +SYMLINK-$(CONFIG_RTE_LIBRTE_DFS)-include += dfs_ethdev.h +SYMLINK-$(CONFIG_RTE_LIBRTE_DFS)-include += dfs_mempool.h +SYMLINK-$(CONFIG_RTE_LIBRTE_DFS)-include += dfs_ring.h +SYMLINK-$(CONFIG_RTE_LIBRTE_DFS)-include += dfs_timer.h +SYMLINK-$(CONFIG_RTE_LIBRTE_DFS)-include += dfs_rawdev.h +SYMLINK-$(CONFIG_RTE_LIBRTE_DFS)-include += dfs_cryptodev.h +else +SRCS-y := dfs_noop.c +SYMLINK-y-include := dfs.h +endif + + +# install includes +include $(RTE_SDK)/mk/rte.lib.mk diff --git a/lib/librte_dfs/dfs.c b/lib/librte_dfs/dfs.c new file mode 100644 index 000000000..43d2e8a0b --- /dev/null +++ b/lib/librte_dfs/dfs.c @@ -0,0 +1,842 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2018 Intel Corporation. + */ +/* Created 2018 by Keith Wiles @ intel.com */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#ifdef RTE_ARCH_X86 +#include +#define DEFAULT_HASH_FUNC rte_hash_crc +#else +#include +#define DEFAULT_HASH_FUNC rte_jhash +#endif + +#include "dfs.h" +#include "dfs_search.h" +#include "dfs_oper.h" + +#include "dfs_dpdk.h" +#include "dfs_eal.h" +#include "dfs_ethdev.h" +#include "dfs_mempool.h" +#include "dfs_ring.h" +#include "dfs_timer.h" +#include "dfs_rawdev.h" +#include "dfs_cryptodev.h" + +static struct dfs *dfs_main; +static char *dfs_prgname = const2char(DFS_DEFAULT_PRG_NAME); +static char *dfs_basedir = const2char(DFS_BASE_MOUNT_POINT); +static _sig_handler_t dfs_sig_handler; + +struct dfs_inits { + dfs_init_t func; + const char *name; +} dfs_func_table[DFS_MAX_INITS]; +static unsigned int dfs_init_idx; + +int libdfs_logtype; + +typedef int (*dfs_init_t)(void); + +void +dfs_register(const char *name, dfs_init_t func) +{ + if (dfs_init_idx >= DFS_MAX_INITS) + rte_exit(0, "DFS init table is full\n"); + + dfs_func_table[dfs_init_idx].func = func; + dfs_func_table[dfs_init_idx++].name = name; +} + +struct dfs * +dfs_get_instance(void) +{ + return dfs_main; +} + +void +dfs_set_defaults(const char *prg, const char *basedir, + _sig_handler_t sig_handler) +{ + if (prg) + dfs_prgname = const2char(prg); + + if (basedir) + dfs_basedir = const2char(basedir); + + dfs_sig_handler = sig_handler; +} + +const char * +dfs_get_prgname(void) +{ + return dfs_prgname; +} + +const char * +dfs_get_basedir(void) +{ + return dfs_basedir; +} + +_sig_handler_t +dfs_get_sig_handler(void) +{ + return dfs_sig_handler; +} + +/* Allocate a node from the DFS node pool */ +static inline struct dfs_node * +dfs_alloc(const char *name, struct dfs_node *parent, int type) +{ + struct dfs_node *node; + + if (!name || (name[0] == '\0')) + return NULL; + + node = malloc(sizeof(struct dfs_node)); + if (!node) + return NULL; + + memset(node, 0, sizeof(struct dfs_node)); + + node->name = strdup(name); + if (!node->name) { + free(node); + return NULL; + } + + TAILQ_INIT(&node->items); + + node->ino = rte_atomic64_add_return( + &dfs_get_instance()->next_inode, 1L); + node->type = type; + node->parent = parent; + + return node; +} + +/* Free a node back to the DFS mempool */ +static inline void +dfs_node_free(struct dfs_node *node) +{ + if (node) { + if (node->name) + free(node->name); + if (node->alias_str) + free(const2char(node->alias_str)); + if (node->file_fd > 0) + close(node->file_fd); + if (node->json_root) + dfs_json_release_data(node); + free(node); + } +} + +/* Helper routine to remove nodes from the DFS tree */ +int +dfs_remove_node(struct dfs_node *node) +{ + struct dfs_node *parent, *n; + + if (!node) + return 0; + + parent = node->parent; + if (!parent) /* Can not remove '/' or root */ + return -1; + + switch (node->type) { + case DFS_DIR_NODE: + if (!TAILQ_EMPTY(&node->items)) + while (!TAILQ_EMPTY(&node->items)) { + n = TAILQ_FIRST(&node->items); + if (dfs_remove_node(n)) + return -1; + } + break; + case DFS_FILE_NODE: + case DFS_ALIAS_NODE: + break; + default: + return -1; + } + + TAILQ_REMOVE(&parent->items, node, next); + + dfs_node_free(node); + + return 0; +} + +/* Helper routine to add nodes to the DFS tree */ +static struct dfs_node * +__add_node(const char *name, struct dfs_node *parent, int type, + dfs_func_t func, uint32_t mode, uint32_t id) +{ + struct dfs *dfs = dfs_get_instance(); + struct dfs_node *node; + + if (!name) + return NULL; + + switch (type) { + case DFS_DIR_NODE: + if (parent && (strcmp(name, "/") == 0)) + return NULL; + if (func) + return NULL; + break; + case DFS_FILE_NODE: + if (!parent || !func) + return NULL; + break; + case DFS_ALIAS_NODE: + if (!parent || func) + return NULL; + break; + default: + return NULL; + } + + node = dfs_alloc(name, parent, type); + if (node == NULL) { + printf("%s: No nodes left\n", __func__); + return NULL; + } + + node->fmode = mode; + node->file_size = 0; + node->file_offset = 0; + node->maxfile_size = 0; + node->user_id = id; + + /* Default time stamps */ + clock_gettime(CLOCK_REALTIME, &node->atime); + node->mtime = node->atime; + node->ctime = node->atime; + + switch (type) { + case DFS_FILE_NODE: + case DFS_ALIAS_NODE: + node->func = func; + if (dfs_file_init(node)) { + dfs_node_free(node); + return NULL; + } + break; + default: + break; + } + + if (rte_hash_add_key_data(dfs->hash, (const void *)&node->ino, + (void *)node)) { + DFS_LOG(DEBUG, "Hash add failed for (%s, %ld)\n", + node->name, node->ino); + dfs_node_free(node); + return NULL; + } + + if (parent) + TAILQ_INSERT_HEAD(&parent->items, node, next); + else + TAILQ_INSERT_HEAD(&dfs->root, node, next); + + return node; +} + +/* Add a direcrtory to the DFS tree */ +struct dfs_node * +dfs_add_dir(const char *name, struct dfs_node *dir) +{ + struct dfs *dfs = dfs_get_instance(); + char *argv[DFS_MAX_PATHS] = { NULL }, *p; + char path[DFS_MAX_PATH_LENGTH]; + int cnt, i; + struct dfs_node *n, *ret; + dfs_func_t func; + struct dfs_node *d = dir; + + RTE_ASSERT(dfs != NULL); + if (!name) + return NULL; + + if (!d) /* Passed in a NULL to start at root node */ + d = get_root(); + + if (name[0] == '.' || name[0] == '/') { + d = dfs_last_dir_in_path(name, dir); + if (!d) + return NULL; + + name = strrchr(name, '/'); + name++; + } + + /* return the last node if directory path already exists */ + if (dfs_find_node((char *)(uintptr_t)name, &ret, DFS_DIR_NODE)) + return ret; + + /* Set the function structure to NULL */ + func = NULL; + + memset(path, '\0', sizeof(path)); + + p = dfs->scratch; + + /* Grab a local copy of the directory path */ + strncpy(p, name, DFS_MAX_SCRATCH_LENGTH); + + if (p[0] == '/') { /* Start from root */ + d = get_root(); + p++; /* Skip the / in the original path */ + path[0] = '/'; /* Add root to the path */ + } + + cnt = _strtok(p, "/", argv, DFS_MAX_PATHS); + + n = NULL; + for (i = 0; i < cnt; i++) { + /* Append each directory part to the search path */ + strcat(path, argv[i]); + + if (dfs_find_node(path, &ret, DFS_DIR_NODE)) { + d = ret; + continue; + } + + n = __add_node(argv[i], d, DFS_DIR_NODE, func, + DFS_FILE_RDONLY, 0); + if (n == NULL) + break; + d = n; + } + return n; +} + +/* Add a command alias executable to the DFS tree */ +struct dfs_node * +dfs_add_alias(const char *name, struct dfs_node *dir, const char *line) +{ + struct dfs_node *d = dir; + struct dfs_node *alias; + + if (name[0] == '.' || name[0] == '/') { + d = dfs_last_dir_in_path(name, dir); + if (!d) + return NULL; + + name = strrchr(name, '/'); + name++; + } + + alias = __add_node(name, d, DFS_ALIAS_NODE, NULL, + DFS_FILE_RDONLY, 0); + alias->alias_str = line; + + return alias; +} + +/* Add a file to the DFS tree */ +struct dfs_node * +dfs_add_file(const char *name, struct dfs_node *dir, dfs_func_t func, + uint32_t flags, uint32_t id) +{ + struct dfs_node *d = dir; + + if (name[0] == '.' || name[0] == '/') { + d = dfs_last_dir_in_path(name, dir); + if (!d) + return NULL; + + name = strrchr(name, '/'); + name++; + } + + return __add_node(name, d, DFS_FILE_NODE, func, flags, id); +} + +/* Add a directory/commands/files/... to a directory */ +int +dfs_add_tree(struct dfs_node *parent, struct dfs_tree *tree) +{ + struct dfs *dfs = dfs_get_instance(); + struct dfs_tree *t; + struct dfs_dir *d; + struct dfs_file *f; + struct dfs_alias *a; + struct dfs_node *n; + + if (!tree) + return -1; + + if (!parent) + parent = dfs->root.tqh_first; + + for (t = tree; t->type != DFS_UNK_NODE; t++) { + switch (t->type) { + case DFS_DIR_NODE: + d = &t->dir; + + n = dfs_add_dir(d->name, parent); + if (n == NULL) { + DFS_LOG(ERR, "Add dir %s failed\n", + d->name); + return -1; + } + parent = n; + break; + + case DFS_FILE_NODE: + f = &t->file; + if (!dfs_add_file(f->name, parent, f->func, + f->flags, f->user_id)) { + DFS_LOG(ERR, "Add file %s failed\n", + f->name); + return -1; + } + break; + + case DFS_ALIAS_NODE: + a = &t->alias; + if (!dfs_add_alias(a->name, parent, + a->alias_atr)) { + DFS_LOG(ERR, "Add alias %s failed\n", + a->name); + return -1; + } + break; + + case DFS_UNK_NODE: + default: + DFS_LOG(DEBUG, "Unknown Node type %d\n", + t->type); + return 0; + } + } + + return 0; +} + +int +dfs_execute_cmd(const char *cmd, FILE *fp) +{ + char buf[DFS_MAX_PATH_LENGTH]; + FILE *f; + size_t tlen, slen; + char *sdk = getenv("RTE_SDK"); + + if (!sdk) { + DFS_LOG(INFO, + "RTE_SDK not found, did you forget 'sudo -E'\n"); + return -1; + } + if (sdk) + snprintf(buf, sizeof(buf), "PATH=$PATH:%s/usertools %s", + sdk, cmd); + else + snprintf(buf, sizeof(buf), "%s", cmd); + + f = popen(buf, "r"); + if (!f) { + DFS_LOG(DEBUG, "`%s` failed to execute!\n", cmd); + return -1; + } + + tlen = 0; + do { + slen = fread(buf, 1, sizeof(buf), f); + if (!slen && feof(f)) + break; + + tlen += slen; + fwrite(buf, 1, slen, fp); + } while (1); + + fflush(fp); + + return tlen; +} + +int +dfs_tmpfile_create(struct dfs_node *n, const char *file, + void (*func)(FILE *fp)) +{ + FILE *fp; + char path[DFS_MAX_PATH_LENGTH]; + int f; + + if (!n || !func || !file) { + DFS_LOG(ERR, "Invalid argument\n"); + return -1; + } + + snprintf(path, sizeof(path), "/tmp/dfs-%d-%s", + dfs_get_instance()->pid, file); + + if (n->file_fd != -1) { + close(n->file_fd); + if (remove(path)) { + if (errno != ENOENT) { + DFS_LOG(ERR, "remove error %s\n", + strerror(errno)); + return -1; + } + } + } + n->file_fd = -1; + + f = open(path, O_CREAT | O_RDWR, 0666); + if (f < 0) { + DFS_LOG(ERR, "open(%s) error %s\n", path, strerror( + errno)); + return -1; + } + + /* Grab the FILE pointer for this file */ + fp = fdopen(f, "r+"); + if (!fp) { + DFS_LOG(ERR, "fdopen(%d) error %s\n", f, + strerror(errno)); + return -1; + } + + func(fp); /* Call the user suppiled function */ + + fflush(fp); /* Make sure all of the data is written */ + + /* Find end of file */ + n->file_size = (size_t)lseek(f, 0L, SEEK_END); + n->file_fd = f; + + if (remove(path)) { /* Make file invisible, but leave open */ + DFS_LOG(ERR, "remove(%s) error %s\n", path, + strerror(errno)); + return -1; + } + + return 0; +} + +void +dfs_tmpfile_release(struct dfs_node *n) +{ + if (n) { + if (n->file_fd >= 0) + close(n->file_fd); + n->file_fd = -1; + } +} + +static int +__list_long_dir(struct dfs_node *node, + uint32_t type __rte_unused, args_t *args) +{ + uint16_t flags = args->arg1.u16[3]; + uint16_t spc = args->arg2.u16[0]; + FILE *f = args->arg3.voidp; + + if (is_alias(node)) + fprintf(f, " %*s%4ld %-16s %s : %s\n", spc, "", + node->ino, + node->name, + dfs_node_type(node), + node->alias_str); + else if (is_directory(node)) + fprintf(f, " %*s%4ld %s/\n", spc, "", node->ino, + node->name); + else + fprintf(f, " %*s%4ld %-16s %ld bytes\n", spc, "", + node->ino, node->name, node->file_size); + + if ((flags & DFS_RECURSE_FLAG) && is_directory(node)) { + args->arg2.u16[0] += 4; + dfs_scan_directory(node, __list_long_dir, type, args); + args->arg2.u16[0] = spc; + } + + return 0; +} + +void +dfs_dump(FILE *f) +{ + struct dfs_node *root; + args_t args = { 0 }; + + if (!f) + f = stderr; + + fprintf(f, "*** Dump Filesystem ***\n"); + + memset(&args, 0, sizeof(args)); + + args.arg1.u16[0] = 0; + args.arg1.u16[1] = 16; + args.arg1.u16[2] = 80 / 16; + args.arg1.u16[3] = DFS_LONG_LIST_FLAG | DFS_RECURSE_FLAG; + args.arg2.u16[0] = 0; + args.arg3.voidp = f; + + root = get_root(); + fprintf(f, "%4ld %-16s\n", root->ino, root->name); + dfs_scan_directory(root, __list_long_dir, DFS_VALID_NODE, + &args); + + fprintf(f, "\n"); +} + +/** + * Create the root directory + * + * @note Uses thread variable dfs_main. + * + * @param dirname + * Name of root directory, if null uses '/' + * @return + * NULL on error or the root node on success. + */ + +static struct dfs_node * +dfs_root_create(const char *dirname) +{ + struct dfs_node *root; + + /* Create and add the root directory */ + root = __add_node(dirname, NULL, DFS_DIR_NODE, NULL, + DFS_FILE_RDONLY, 0); + if (root) + root->nlookup = 2; + + return root; +} + +/* Cleanup the DFS allocation of memory */ +RTE_FINI(dfs_destroy) +{ + struct dfs *dfs = dfs_main; + const void *key; + void *data; + uint32_t next; + int idx; + + if (!dfs) + return; + + dfs_ops_destroy(dfs); + + free(dfs->scratch); + + key = NULL; + next = 0; + for (;;) { + idx = rte_hash_iterate( + (const struct rte_hash *)dfs->hash, + &key, &data, &next); + if (idx >= 0) + dfs_node_free((struct dfs_node *)data); + else + break; + } + + if (dfs->hash) + rte_hash_free(dfs->hash); + + free(dfs); + + dfs_main = NULL; +} + +static void * +__dfs_thread(void *arg) +{ + struct dfs *dfs = arg; + unsigned int i; + + pthread_detach(pthread_self()); + + dfs->se = dfs_ops_create(dfs); + if (!dfs->se) { + DFS_LOG(DEBUG, "setup dfs_ops failed\n"); + goto err_exit; + } + + if (!dfs_root_create("/")) { + DFS_LOG(DEBUG, "setup root node failed\n"); + goto err_exit; + } + + DFS_LOG(DEBUG, "Number of DFS modules %u\n", dfs_init_idx); + + /* call the init functions to set up dirs and files */ + for (i = 0; i < dfs_init_idx; i++) { + DFS_LOG(DEBUG, "Call DFS init routine for %s\n", + dfs_func_table[i].name); + + if (dfs_func_table[i].func()) { + DFS_LOG(ERR, "DFS setup failed for %s\n", + dfs_func_table[i].name); + goto err_exit; + } + } + + fuse_session_loop(dfs->se); +err_exit: + return NULL; +} + +static struct dfs * +dfs_create(void) +{ + struct dfs *dfs; + pthread_t tid; + struct passwd *pw; + struct rte_hash_parameters params = { + .name = "dfs-hash", + .entries = 1024, + .key_len = sizeof(fuse_ino_t), + .hash_func = DEFAULT_HASH_FUNC, + .hash_func_init_val = 0, + .extra_flag = 0 + }; + + if (dfs_main) + return dfs_main; + + dfs = malloc(sizeof(struct dfs)); + if (dfs == NULL) { + DFS_LOG(ERR, "Unable to allocate DFS structure\n"); + goto err_exit; + } + memset(dfs, '\0', sizeof(struct dfs)); + + TAILQ_INIT(&dfs->root); /* Init the root directory list */ + + /* Need to get the original PID before pthread_create() */ + dfs->pid = getpid(); + + DFS_LOG(DEBUG, "DFS master pid %d\n", getpid()); + + /* locate and return the DPDK user uid and gid */ + pw = getpwnam(DFS_DPDK_USER_NAME); + if (!pw) { + DFS_LOG(ERR, "'%s' was not found! uid/gid = 1000\n", + DFS_DPDK_USER_NAME); + dfs->uid = 1000; + dfs->gid = 1000; + } else { + dfs->uid = pw->pw_uid; + dfs->gid = pw->pw_gid; + } + + /* Allocate the tempory scratch space for paths */ + dfs->scratch = malloc(DFS_MAX_SCRATCH_LENGTH + 1); + if (!dfs->scratch) + goto err_exit; + memset(dfs->scratch, 0, DFS_MAX_SCRATCH_LENGTH + 1); + + dfs->hash = rte_hash_create(¶ms); + if (!dfs->hash) { + DFS_LOG(ERR, "Unable to create hash table\n"); + goto err_exit; + } + + /* Launch the FUSE thread */ + if (pthread_create(&tid, NULL, __dfs_thread, dfs)) { + DFS_LOG(ERR, "pthead_create() failed for DFS!\n"); + goto err_exit; + } + DFS_LOG(DEBUG, "DFS pthread id for DFS thread %lx\n", tid); + + dfs_main = dfs; + + DFS_LOG(DEBUG, "dfs %p\n", dfs); + return dfs; + +err_exit: + return NULL; +} + +int +dfs_is_running(void) +{ + struct dfs *dfs = dfs_get_instance(); + + if (dfs_is_configured() == 0) + return 0; + else if (dfs_is_enabled() == 0) + return 0; + +try_again: + if (dfs == NULL || dfs->se == NULL) { + uint64_t tsc_end; + + tsc_end = rte_rdtsc() + rte_get_timer_hz(); + + while (rte_rdtsc() < tsc_end) { + if (dfs && dfs->se) + goto try_again; + } + printf("DFS is not running\n"); + return 0; + } + + return fuse_session_exited(dfs->se) == 0; +} + + +static int32_t __rte_experimental +rte_dfs_init(void) +{ + struct dfs *dfs; + + DFS_LOG(DEBUG, "DFS init called\n"); + + /* Initialize the DPDK File System */ + dfs = dfs_create(); + if (!dfs) { + DFS_LOG(ERR, "Error DFS Create failed\n"); + return -ENOMEM; + } + + return 0; +} + +static struct rte_option option = { + .opt_str = (char *)(uintptr_t)"--dfs", + .cb = rte_dfs_init, + .enabled = 0 +}; + +int +dfs_is_enabled(void) +{ + return option.enabled; +} + +RTE_INIT(libdfs_init_log) +{ + /* Set up the first DFS module */ + dfs_func_table[dfs_init_idx].func = dfs_dpdk_init; + dfs_func_table[dfs_init_idx++].name = "DPDK"; + + libdfs_logtype = rte_log_register("librte.dfs"); + if (libdfs_logtype >= 0) + rte_log_set_level(libdfs_logtype, RTE_LOG_INFO); + + rte_option_register(&option); +} diff --git a/lib/librte_dfs/dfs.h b/lib/librte_dfs/dfs.h new file mode 100644 index 000000000..d78f88eab --- /dev/null +++ b/lib/librte_dfs/dfs.h @@ -0,0 +1,768 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2018 Intel Corporation. + */ +/* Created 2018 by Keith Wiles @ intel.com */ + +#ifndef _DFS_H_ +#define _DFS_H_ + +/** + * @file + * DPDK file system (DFS). Using the FUSE library to create a userspace file + * system for query and control of DPDK and applications. + * + * DFS uses the FUSE low level API set of version 3.2 located here: + * https://github.com/libfuse/libfuse + * + */ +#ifndef FUSE_USE_VERSION +#define FUSE_USE_VERSION 31 +#endif + +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * ENUMs for misc values used in DFS + * + */ +enum { + DFS_MAX_PATH_LENGTH = 2048, /**< Max path string length */ + DFS_MAX_SCRATCH_LENGTH = 4096, /**< Max scratch space length */ + DFS_MAX_PATHS = 64, + DFS_MAX_INITS = 64 +}; + + +typedef void (*_sig_handler_t)(int sig); /**< Signal handler */ + +#ifdef RTE_LIBRTE_DFS +struct dfs; +struct dfs_node; + +#define countof(t) (sizeof((t)) / sizeof((t)[0])) + +/* Setup the JSON output with 4 space for indent as default */ +#define DFS_JSON_INDENT JSON_INDENT(4) + +#define DFS_DPDK_USER_NAME "dpdk-user" +#define DFS_BASE_MOUNT_POINT "/dpdk" +#define DFS_DEFAULT_PRG_NAME "dpdk" + +extern int libdfs_logtype; +#define DFS_LOG(level, fmt, args ...) \ + rte_log(RTE_LOG_ ## level, libdfs_logtype, "%s(%d): " fmt, \ + __func__, getpid(), ## args) + +/** + * Function to test if debugging is enabled + * + * @return + * True if enabled or False is not + */ +static inline int +dfs_debug_enabled(void) +{ + return rte_log_get_level(libdfs_logtype) >= (int)RTE_LOG_DEBUG; +} + +/* convert const char * to char * values */ +#define const2char(x) (char *)(uintptr_t)(x) + +/** + * The following ENUMs are used in the callback routines to indicate the type + * of request is being made to the (file, directory or aliases) node. + * + */ +typedef enum { + DFS_FILE_INIT = 0, /**< used when file is created */ + DFS_FILE_INFO, /**< used when FUSE needs info on node */ + DFS_FILE_RELEASE, /**< used when FUSE release or closes a node */ + DFS_FILE_OPEN, /**< used when FUSE opens the file */ + DFS_FILE_READ, /**< used when FUSE does a read on node */ + DFS_FILE_WRITE /**< used when FUSE does a write on node */ +} oper_t; + +/** + * Return the node type string for a given node type. + * + * @param opt + * The node type value used to locate node string. + * @return + * Pointer to node string type. + */ +static inline const char * +dfs_opt_str(oper_t opt) +{ + const char *p; + + switch (opt) { + case DFS_FILE_INIT: + p = "FILE_INIT"; + break; + case DFS_FILE_INFO: + p = "FILE_INFO"; + break; + case DFS_FILE_RELEASE: + p = "FILE_RELEASE"; + break; + case DFS_FILE_OPEN: + p = "FILE_OPEN"; + break; + case DFS_FILE_READ: + p = "FILE_READ"; + break; + case DFS_FILE_WRITE: + p = "FILE_WRITE"; + break; + default: + p = "Unknown"; + break; + } + return p; +} + +#define DFS_RECURSE_FLAG (1 << 0) +#define DFS_LONG_LIST_FLAG (1 << 1) + +/* bitmap for the node type */ +typedef enum { + DFS_UNK_NODE = 0x0000, /**< Unknown node type */ + DFS_DIR_NODE = 0x0001, /**< Directory node type */ + DFS_FILE_NODE = 0x0004, /**< File node type */ + DFS_ALIAS_NODE = 0x0008,/**< Alias node type */ + DFS_VALID_NODE = (DFS_DIR_NODE | DFS_FILE_NODE | DFS_ALIAS_NODE), +} node_type_t; + +/* Keep this list in sync with the node_type_t enum above */ +#define DFS_NODE_TYPES \ + { "Unknown", "Directory", "File", "Alias", NULL } + +typedef int (*dfs_func_t)(struct dfs_node *node, oper_t opt); +/**< DFS function pointer type for a file type node */ + +typedef int (*dfs_tree_t)(void); +/**< DFS function pointer type for user initialization */ + +TAILQ_HEAD(dfs_head, dfs_node); /**< head of node entries or root */ + +/** + * Generic node structure for all node types in the directory tree + * + */ +struct dfs_node { + TAILQ_ENTRY(dfs_node) next; /**< link list of nodes */ + char *name; /**< Name of Node */ + uint64_t ino; /**< inode value */ + + node_type_t type; /**< Node Type Root, Dir or cmd */ + dfs_func_t func; /**< Function to call for files */ + + uint32_t fmode; /**< file mode value */ + uint32_t user_id; /**< User defined ID value */ + int file_fd; /**< file descriptor value */ + int pad0; + + struct timespec atime; /**< Access time */ + struct timespec mtime; /**< Modify time */ + struct timespec ctime; /**< Creation time */ + + json_t *json_root; /**< JSON root node */ + off_t file_offset; /**< Current offset in file */ + size_t file_size; /**< Size of file */ + size_t maxfile_size; /**< Max size of the object node */ + uint64_t nlookup; /**< Number of lookups */ + char *file_data; /**< Pointer to file data */ + struct dfs_node *parent;/**< Parent directory (NULL == ROOT) */ + + const char *alias_str; /**< Alias string */ + + struct dfs_head items; /**< List of nodes in directory */ +}; + +/** + * The structure to hold all of the DFS information. + * + */ +struct dfs { + struct dfs_head root; /**< head of node entries or root */ + struct rte_hash *hash; /**< HASH table */ + struct fuse_session *se; /**< Fuse session pointer */ + rte_atomic64_t next_inode; /**< Next inode number */ + char *scratch; /**< Place to build data */ + pid_t pid; /**< Current PID of process */ + uid_t uid; /**< User Id */ + gid_t gid; /**< Group Id */ +}; + +typedef int (*dfs_init_t)(void); + +/** + * Check if DFS is configured to run or not. + * + * @return + * Returns the state of RTE_ENABLE_RUNTIME_DFS or zero + */ +static inline int +dfs_is_configured(void) +{ +#ifdef RTE_LIBRTE_DFS + return 1; +#else + return 0; +#endif +} + +/** + * Check if DFS is enabled to run. + * + * @return + * Test if --dfs option was give on command line. + */ +int __rte_experimental dfs_is_enabled(void); + +/** + * Get the signal handler set by the user in dfs_set_defaults() call. + * + * @return + * The signal handler function pointer or NULL not set. + */ +_sig_handler_t __rte_experimental dfs_get_sig_handler(void); + +/** + * Test if the DPDK filesystem is running. + * + * @return + * If running return 1 or 0 if not running. + */ +int __rte_experimental dfs_is_running(void); + +/** + * Register DFS init functions. + * + * @param name + * Constant string pointer for name of function. + * @param func + * The function for the dfs init routine. + * @return + * -1 on error or zero on success. + */ +void __rte_experimental dfs_register(const char *name, dfs_init_t func); + +/** + * Return the current DFS pointer + * + * @return + * DFS data pointer or NULL on error. + */ +struct dfs *__rte_experimental dfs_get_instance(void); + +/** + * Set the defaults prgname and basedir name strings + * + * Change the default program name and base directory strings from the + * default values. If prg and/or basedir are NULL then the default values + * will be used. + * + * @param prg + * The string for DFS program name. + * @param basedir + * The string for the base mount point directory. + * @param use_signals + * Use the FUSE based signal handlers default is not to use them for + * applications that register its own signal handlers. + */ +void __rte_experimental dfs_set_defaults(const char *prg, const char *basedir, + _sig_handler_t sig_handler); + +/** + * return the current program name set by user. + * + * @return + * String for program name. + */ +const char *__rte_experimental dfs_get_prgname(void); + +/** + * Return the string for the base directory of DFS. + * + * @return + * The base directory path. + */ +const char *__rte_experimental dfs_get_basedir(void); + +/** + * Return the string for the given node type + * + * @param node + * struct dfs_node pointer + * @return + * String for the node type. + */ +static inline const char * +dfs_node_type(struct dfs_node *node) +{ + const char *node_str[] = DFS_NODE_TYPES; + + switch (node->type) { + case DFS_DIR_NODE: return node_str[1]; + case DFS_FILE_NODE: return node_str[2]; + case DFS_ALIAS_NODE: return node_str[3]; + case DFS_UNK_NODE: + default: + break; + } + + return node_str[0]; +} + +/** + * Initialize the node structure. + * + * @param node + * Pointer to the node structure + * @param p + * Pointer to the node data, can be NULL. As the pointer will be + * provided in the DFS_FILE_INFO event callback. + * @param s + * The size of the node data. If -1 then use strlen(p) for size. + * @param ms + * The max file size. If 0 then not writeable, >0 then the max amount of data + * that is able to be pushed in the file. + * + */ +static inline void +dfs_node_init(struct dfs_node *node, void *p, ssize_t s, size_t ms) +{ + node->file_fd = -1; + if (s < 0) + node->file_size = strlen(p); + else + node->file_size = (size_t)s; + node->file_data = p; + node->file_offset = 0; + node->maxfile_size = ms; +} + +/** + * simple helper routines for calling the user callback functions with the + * correct arguments. + * + */ +static inline int +dfs_file_init(struct dfs_node *n) +{ + RTE_ASSERT(n != NULL); + + if (!n->func) + return 0; + + return n->func(n, DFS_FILE_INIT); +} + +static inline int +dfs_file_info(struct dfs_node *n) +{ + RTE_ASSERT(n != NULL); + + if (!n->func) + return 0; + + return n->func(n, DFS_FILE_INFO); +} + +static inline int +dfs_file_open(struct dfs_node *n) +{ + RTE_ASSERT(n != NULL); + + clock_gettime(CLOCK_REALTIME, &n->atime); + + if (!n->func) + return 0; + + return n->func(n, DFS_FILE_OPEN); +} + +static inline int +dfs_file_release(struct dfs_node *n) +{ + RTE_ASSERT(n != NULL); + + if (!n->func) + return 0; + + return n->func(n, DFS_FILE_RELEASE); +} + +static inline int +dfs_file_read(struct dfs_node *n) +{ + RTE_ASSERT(n != NULL); + + if (!n->func) + return 0; + + return n->func(n, DFS_FILE_READ); +} + +static inline int +dfs_file_write(struct dfs_node *n) +{ + RTE_ASSERT(n != NULL); + + clock_gettime(CLOCK_REALTIME, &n->mtime); + n->ctime = n->mtime; + + if (!n->func) + return 0; + + return n->func(n, DFS_FILE_WRITE); +} + +static inline char * +dfs_ctime(struct timespec *ts) +{ + static char buf[64]; + + snprintf(buf, sizeof(buf), "%ld.%ld", ts->tv_sec, ts->tv_nsec); + + return buf; +} + +static inline void +dfs_json_set_data(struct dfs_node *n, json_t *root) +{ + n->file_data = json_dumps(root, DFS_JSON_INDENT); + n->file_size = strlen(n->file_data); + n->json_root = root; +} + +/** + * Release the information attached to the node and decrement json refcnt + * + * @param n + * The node pointer + */ +static inline void +dfs_json_release_data(struct dfs_node *n) +{ + RTE_ASSERT(n != NULL); + + if (n->file_data) + free(n->file_data); + if (n->json_root) + json_decref(n->json_root); + n->file_data = NULL; + n->file_size = 0; + n->json_root = NULL; +} + +/** + * Structures to define the given node type in the primary node structure + */ +struct dfs_dir { + const char *name; /**< directory name */ +}; + +struct dfs_alias { + const char *name; /**< Name of command */ + const char *alias_atr; /**< Alias string */ +}; /**< List of alias for dfs_add_aliases() */ + +struct dfs_file { + const char *name; /**< Name of command */ + dfs_func_t func; /**< Read/Write function pointer */ + uint32_t flags; + uint32_t user_id; +}; + +/** + * structure used to allow the user to define directories, files and aliases + * to be passed to the df_create_tree() routine. + */ +struct dfs_tree { + node_type_t type; /**< type of node to create */ + union { + struct dfs_dir dir; /**< directory and bin directory */ + struct dfs_file file; /**< file nodes */ + struct dfs_alias alias; /**< alias nodes */ + }; +}; + +/* ENUMs for the type access to the node, mostly for file type nodes */ +enum { + DFS_FILE_RD = 1, + DFS_FILE_WR = 2, + DFS_FILE_RDWR = (DFS_FILE_RD | DFS_FILE_WR) +}; + +#define DFS_FILE_RDONLY DFS_FILE_RD +#define DFS_FILE_WRONLY DFS_FILE_WR + +/**< Used to help create a tree */ +#define u_dir(n) { DFS_DIR_NODE, .dir = {(n)} } +#define u_file(n, f, t, u) { DFS_FILE_NODE, .file = {(n), (f), (t), (u)} } +#define u_alias(n, l) { DFS_ALIAS_NODE, .alias = {(n), (l)} } +#define u_end() { DFS_UNK_NODE, .dir = { NULL } } + +/** + * DFS root directory node. + * + * @return + * Pointer to current working directory. + */ +static inline struct dfs_node * +get_root(void) +{ + RTE_ASSERT(dfs_get_instance() != NULL); + + return dfs_get_instance()->root.tqh_first; +} + +/** + * Helper routine to compare two strings exactly + * + * @param s1 + * Pointer to first string. + * @param s2 + * Pointer to second string. + * @return + * 0 failed to compare and 1 is equal. + */ +static inline int +is_match(const char *s1, const char *s2) +{ + if (!s1 || !s2) + return 0; + + while ((*s1 != '\0') && (*s2 != '\0')) { + if (*s1++ != *s2++) + return 0; + } + if (*s1 != *s2) + return 0; + + return 1; +} + +/** + * Test if the node is of a given type(s) + * + * @param node + * Pointer the dfs_node structure + * @return + * True if node is one of the types given + */ +static inline int +is_node(struct dfs_node *node, uint32_t types) +{ + return node->type & types; +} + +/** + * Test if the node is alias + * + * @param node + * Pointer the dfs_node structure + * @return + * True if alias else false if not + */ +static inline int +is_alias(struct dfs_node *node) +{ + return is_node(node, DFS_ALIAS_NODE); +} + +/** + * Test if the node is a file + * + * @param node + * Pointer the dfs_node structure + * @return + * True if a file else false if not + */ +static inline int +is_file(struct dfs_node *node) +{ + return is_node(node, DFS_FILE_NODE); +} + +/** + * Test if the node is directory + * + * @param node + * Pointer the dfs_node structure + * @return + * True if directory else false if not + */ +static inline int +is_directory(struct dfs_node *node) +{ + return is_node(node, DFS_DIR_NODE); +} + +/** + * Add a dfs directory + * + * @param dirname + * String pointing to the directory name + * @param parent + * Parent node of the new directory + * @return + * pointer to directory entry or NULL on error + */ +struct dfs_node *__rte_experimental dfs_add_dir(const char *dirname, + struct dfs_node *parent); + +/** + * Add an alias string or special command type + * * + * @param name + * Pointer to command name string + * @param dir + * Directory node pointer + * @param line + * Pointer to alias string + * @return + * NULL on error or the node address for the command + */ +struct dfs_node *__rte_experimental dfs_add_alias(const char *name, + struct dfs_node *dir, const char *line); + +/** + * Add an file to a directory + * + * @param name + * Pointer to command name string + * @param dir + * Directory node pointer + * @param func + * Pointer to a function attached to the file. + * @return + * NULL on error or the node pointer. + */ +struct dfs_node *__rte_experimental dfs_add_file(const char *name, + struct dfs_node *dir, dfs_func_t func, uint32_t flags, + uint32_t id); + +/** + * Remove a node from the directory tree + * + * @param node + * The pointer to the node to remove + * @return + * 0 on OK and -1 on error + */ +int __rte_experimental dfs_remove_node(struct dfs_node *node); + +/** + * Add a list of nodes to a directory + * + * @param dir + * Node pointer to directory for add commands + * @param treee + * Pointer to list of nodes to add to the tree + * @return + * -1 on error or 0 for OK + */ +int __rte_experimental dfs_add_tree(struct dfs_node *dir, + struct dfs_tree *tree); + +/** + * Dump the directory tree for DFS in ascii text to the file pointer. + * + * @param f + * The File pointer as in stderr or stdout. + */ +void __rte_experimental dfs_dump(FILE *f); + +/** + * Create a tmpfile for the filesystem data. + * + * The tmpfile is unlinked after it is created in the /tmp directory + * to make cleanup a bit easier and prevent access by others. + * + * @param n + * DFS node pointer + * @param file + * Name of tmpfile to create + * @param func + * Function pointer to call to create data for file + * @return + * 0 on OK and -1 on error + */ +int __rte_experimental dfs_tmpfile_create(struct dfs_node *n, + const char *file, + void (*func)(FILE *fp)); + +/** + * Close the tmpfile using the file descriptor value + * + * @param n + * dfs_node pointer to release file descriptor + */ +void __rte_experimental dfs_tmpfile_release(struct dfs_node *n); + +/** + * Execute the command string and write data into file pointer + * + * @param + * String to execute e.g. '/usr/bin/lspci | grep Ether' + * @param + * The file pointer to write the command output. + * @return + * -1 on error or total number of bytes written to file. + */ +int __rte_experimental dfs_execute_cmd(const char *cmd, FILE *fp); + +#else + +/* Dummy functions for when RTE_LIBRTE_DFS is not defined */ +static inline int +dfs_is_configured(void) +{ + return 0; +} + +static inline int +dfs_is_enabled(void) +{ + return 0; +} + +static inline void +dfs_set_defaults(const char *prg __rte_unused, + const char *basedir __rte_unused, + _sig_handler_t sig_handler __rte_unused) +{ +} +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* _DFS_H_ */ diff --git a/lib/librte_dfs/dfs_cryptodev.c b/lib/librte_dfs/dfs_cryptodev.c new file mode 100644 index 000000000..96412e0dd --- /dev/null +++ b/lib/librte_dfs/dfs_cryptodev.c @@ -0,0 +1,53 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2018 Intel Corporation. + */ + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "dfs.h" +#include "dfs_cryptodev.h" + +#ifdef RTE_LIBRTE_CRYPTODEV +static int +cryptodev_info(struct dfs_node *node, oper_t opt) +{ + static char count[24]; + + switch (opt) { + case DFS_FILE_INIT: + snprintf(count, sizeof(count), "%d\n", rte_cryptodev_count()); + dfs_node_init(node, count, -1, 0); + break; + + default: + break; + } + + return 0; +} + +static struct dfs_tree cryptodev_tree[] = { + u_dir("cryptodev"), + u_file("count", cryptodev_info, DFS_FILE_RDONLY, 0), + u_end() +}; + +static int +_cryptodev_init(void) +{ + return dfs_add_tree(NULL, cryptodev_tree); +} + +RTE_INIT(dfs_cryptodev_init) +{ + dfs_register("Cryptodev", _cryptodev_init); +} +#endif diff --git a/lib/librte_dfs/dfs_cryptodev.h b/lib/librte_dfs/dfs_cryptodev.h new file mode 100644 index 000000000..647eba3df --- /dev/null +++ b/lib/librte_dfs/dfs_cryptodev.h @@ -0,0 +1,25 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2018 Intel Corporation. + */ + +#ifndef _DFS_CRYPTODEV_H_ +#define _DFS_CRYPTODEV_H_ + +/** + * @file + * RTE DFS Cryptodev Operations + * + * These routines supply the Crypto usage support routines. + */ + +#ifdef __cplusplus +extern "C" { +#endif + +#define DFS_CRYPTODEV_INFO_FILE "cryptodev-info.tmp" + +#ifdef __cplusplus +} +#endif + +#endif /* _DFS_CRYPTODEV_H_ */ diff --git a/lib/librte_dfs/dfs_dpdk.c b/lib/librte_dfs/dfs_dpdk.c new file mode 100644 index 000000000..b6352de02 --- /dev/null +++ b/lib/librte_dfs/dfs_dpdk.c @@ -0,0 +1,323 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2018 Intel Corporation. + */ + +#include +#include +#include + +#include +#include +#include +#include + +#include "dfs.h" +#include "dfs_dpdk.h" + +static const char *copyright = + " BSD LICENSE\n" + "\n" + " Copyright(c) 2010-2018 Intel Corporation. All rights reserved.\n" + "\n" + " Redistribution and use in source and binary forms, with or without\n" + " modification, are permitted provided that the following conditions\n" + " are met:\n" + "\n" + " * Redistributions of source code must retain the above copyright\n" + " notice, this list of conditions and the following disclaimer.\n" + " * Redistributions in binary form must reproduce the above copyright\n" + " notice, this list of conditions and the following disclaimer in\n" + " the documentation and/or other materials provided with the\n" + " distribution.\n" + " * Neither the name of Intel Corporation nor the names of its\n" + " contributors may be used to endorse or promote products derived\n" + " from this software without specific prior written permission.\n" + "\n" + " THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n" + " \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n" + " LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\n" + " A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\n" + " OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n" + " SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\n" + " LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\n" + " DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\n" + " THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n" + " (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n" + " OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n" + "\n" + " SPDX-License-Identifier: BSD-3-Clause\n"; + +static int +copyright_file(struct dfs_node *node, oper_t opt) +{ + switch (opt) { + case DFS_FILE_INIT: + dfs_node_init(node, const2char(copyright), -1, 0); + break; + + default: + break; + } + + return 0; +} + +static int +version_file(struct dfs_node *node, oper_t opt) +{ + static char ver[32]; + + switch (opt) { + case DFS_FILE_INIT: + snprintf(ver, sizeof(ver), "%s\n", rte_version()); + dfs_node_init(node, ver, -1, 0); + break; + + default: + break; + } + + return 0; +} + +#ifdef DFS_TEST_WRITE_FILE +static char scratch_buf[2048] = { 0 }; + +static int +scratch_file(struct dfs_node *node, oper_t opt) +{ + switch (opt) { + case DFS_FILE_INIT: + dfs_node_init(node, scratch_buf, 0, sizeof(scratch_buf)); + break; + + default: + break; + } + + return 0; +} +#endif + +static int +pid_file(struct dfs_node *node, oper_t opt) +{ + static char pid_str[24]; + + switch (opt) { + case DFS_FILE_INIT: + node->file_size = snprintf(pid_str, sizeof(pid_str), "%d\n", + dfs_get_instance()->pid); + dfs_node_init(node, pid_str, -1, 0); + break; + + default: + break; + } + + return 0; +} + +static int +type_file(struct dfs_node *node, oper_t opt) +{ + struct rte_config *cfg = rte_eal_get_configuration(); + static char type_str[12]; + + switch (opt) { + case DFS_FILE_INIT: + node->file_size = snprintf(type_str, sizeof(type_str), "%s\n", + (cfg->process_type == RTE_PROC_PRIMARY) ? "Primary" : + (cfg->process_type == RTE_PROC_SECONDARY) ? + "Secondary" : "Auto"); + dfs_node_init(node, type_str, -1, 0); + break; + + default: + break; + } + + return 0; +} + +static int +fuse_file(struct dfs_node *node, oper_t opt) +{ + static char fuse_str[64]; + + switch (opt) { + case DFS_FILE_INIT: + snprintf(fuse_str, sizeof(fuse_str), "version %i.%i\n", + FUSE_MAJOR_VERSION, FUSE_MINOR_VERSION); + dfs_node_init(node, fuse_str, -1, 0); + break; + + default: + break; + } + + return 0; +} + +static void +__devbind(FILE *fp) +{ + (void)dfs_execute_cmd(DFS_DEVBIND_CMD " --status-dev net", fp); +} + +static int +devbind_file(struct dfs_node *node, oper_t opt) +{ + DFS_LOG(DEBUG, "node %s, ino %ld, %s\n", + node->name, node->ino, dfs_opt_str(opt)); + + switch (opt) { + case DFS_FILE_INIT: + dfs_node_init(node, NULL, 0, 0); + break; + + case DFS_FILE_OPEN: + if (dfs_tmpfile_create(node, DFS_DEVBIND_FILE, __devbind)) { + DFS_LOG(DEBUG, "OPEN tmpfile create failed\n"); + return -1; + } + break; + + case DFS_FILE_RELEASE: + dfs_tmpfile_release(node); + break; + + default: + break; + } + + return 0; +} + +static int +dump_fs_file(struct dfs_node *node, oper_t opt) +{ + + switch (opt) { + case DFS_FILE_INIT: + dfs_node_init(node, NULL, 0, 0); + break; + + case DFS_FILE_INFO: + if (dfs_tmpfile_create(node, DFS_DEBUG_FILE, dfs_dump)) + return -1; + break; + + case DFS_FILE_RELEASE: + dfs_tmpfile_release(node); + break; + + default: + break; + } + + return 0; +} + +static void +hash_dump(FILE *fp) +{ + const void *key; + void *data; + uint32_t next; + int idx; + + key = NULL; + next = 0; + for (;;) { + idx = rte_hash_iterate( + (const struct rte_hash *)dfs_get_instance()->hash, + &key, &data, &next); + if (idx >= 0) { + struct dfs_node *node = data; + + fprintf(fp, "%5d - (%s, %lu) parent %s\n", + idx, node->name, node->ino, + (node->parent) ? node->parent->name : ""); + } else + break; + } +} + +static int +hash_file(struct dfs_node *node, oper_t opt) +{ + + switch (opt) { + case DFS_FILE_INIT: + dfs_node_init(node, NULL, 0, 0); + break; + + case DFS_FILE_INFO: + if (dfs_tmpfile_create(node, DFS_HASH_FILE, hash_dump)) + return -1; + break; + + case DFS_FILE_RELEASE: + dfs_tmpfile_release(node); + break; + + default: + break; + } + + return 0; +} + +static int +sizes_file(struct dfs_node *node, oper_t opt) +{ + static char dfs_sz[16], dfs_node_sz[16]; + + switch (opt) { + case DFS_FILE_INIT: + switch (node->user_id) { + case 0: + snprintf(dfs_sz, sizeof(dfs_sz), "%ld\n", + sizeof(struct dfs)); + dfs_node_init(node, dfs_sz, -1, 0); + break; + case 1: + snprintf(dfs_node_sz, sizeof(dfs_node_sz), "%ld\n", + sizeof(struct dfs_node)); + dfs_node_init(node, dfs_node_sz, -1, 0); + break; + } + break; + + default: + break; + } + + return 0; +} + +static struct dfs_tree dpdk_tree[] = { + u_file("copyright", copyright_file, DFS_FILE_RDONLY, 0), + u_file("version", version_file, DFS_FILE_RDONLY, 0), + u_file("pid", pid_file, DFS_FILE_RDONLY, 0), + u_file("proc-type", type_file, DFS_FILE_RDONLY, 0), + u_file("fuse-version", fuse_file, DFS_FILE_RDONLY, 0), + u_file("devbind-net", devbind_file, DFS_FILE_RDONLY, 0), + u_dir("debug"), +#ifdef DFS_TEST_WRITE_FILE + u_file("scratch", scratch_file, DFS_FILE_RDWR, 0), +#endif + u_file("dump_fs", dump_fs_file, DFS_FILE_RDONLY, 0), + u_file("hash", hash_file, DFS_FILE_RDONLY, 0), + u_dir("sizes"), + u_file("dfs", sizes_file, DFS_FILE_RDONLY, 0), + u_file("dfs_node", sizes_file, DFS_FILE_RDONLY, 1), + u_end() +}; + +int +dfs_dpdk_init(void) +{ + return dfs_add_tree(NULL, dpdk_tree); +} diff --git a/lib/librte_dfs/dfs_dpdk.h b/lib/librte_dfs/dfs_dpdk.h new file mode 100644 index 000000000..1b0ca9106 --- /dev/null +++ b/lib/librte_dfs/dfs_dpdk.h @@ -0,0 +1,37 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2018 Intel Corporation. + */ + +#ifndef _DFS_DPDK_H_ +#define _DFS_DPDK_H_ + +/** + * @file + * RTE DFS DPDK Operations + * + * These routines supply the DPDK usage support routines. + */ + +#ifdef __cplusplus +extern "C" { +#endif + +#define DFS_DEBUG_FILE "debug.tmp" +#define DFS_HASH_FILE "hash.tmp" +#define DFS_DEVBIND_FILE "devbind.tmp" + +#define DFS_DEVBIND_CMD "dpdk-devbind.py" + +/** + * Initialize the DPDK support for DFS + * + * @return + * -1 on error or 0 on OK + */ +int __rte_experimental dfs_dpdk_init(void); + +#ifdef __cplusplus +} +#endif + +#endif /* _DFS_DPDK_H_ */ diff --git a/lib/librte_dfs/dfs_eal.c b/lib/librte_dfs/dfs_eal.c new file mode 100644 index 000000000..1af8a1701 --- /dev/null +++ b/lib/librte_dfs/dfs_eal.c @@ -0,0 +1,652 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2018 Intel Corporation. + */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "dfs.h" +#include "dfs_eal.h" + +static int +eal_file(struct dfs_node *node, oper_t opt) +{ + static char lcore_count[8]; + static char socket_count[8]; + static char hypervisor[32]; + const char *s; + json_t *root, *entry, *array; + unsigned int i, cnt; + + switch (opt) { + case DFS_FILE_INIT: + switch (node->user_id) { + case 1: /* lcore-cnt */ + /* initialize the node as empty */ + dfs_node_init(node, const2char(lcore_count), 0, 0); + break; + case 2: /* lcore-list */ + /* initialize the node as empty */ + dfs_node_init(node, NULL, 0, 0); + break; + case 3: /* socket-cnt */ + snprintf(socket_count, sizeof(socket_count), "%d\n", + rte_socket_count()); + /* initialize the node with static socket_count */ + dfs_node_init(node, const2char(socket_count), -1, 0); + break; + case 4: /* hypervisor */ + s = rte_hypervisor_get_name(rte_hypervisor_get()); + snprintf(hypervisor, sizeof(hypervisor), "%s\n", s); + dfs_node_init(node, hypervisor, -1, 0); + break; + case 5: /* lcore-all */ + /* initialize the node as empty */ + dfs_node_init(node, NULL, 0, 0); + break; + default: + break; + } + break; + + case DFS_FILE_INFO: + switch (node->user_id) { + case 1: /* lcore-cnt */ + snprintf(lcore_count, sizeof(lcore_count), "%d\n", + rte_lcore_count()); + /* Create the node using the lcore_count string */ + dfs_node_init(node, const2char(lcore_count), -1, 0); + break; + case 2: /* lcore-list */ + root = json_object(); /* create root object */ + array = json_array(); /* create the array object */ + + /* Create and add the array node to the list */ + json_object_set_new(root, "lcores", array); + + /* Create all of the array objects */ + cnt = 0; + RTE_LCORE_FOREACH(i) { + entry = json_object(); + json_object_set_new(entry, "lcore", + json_integer(i)); + json_object_set_new(entry, "socket", + json_integer(rte_lcore_to_socket_id(i))); + json_object_set_new(entry, "type", + json_string( + rte_lcore_has_role(i, ROLE_RTE) + ? "RTE" : + rte_lcore_has_role(i, ROLE_SERVICE) + ? "SERVICE" : "OFF")); + + if (rte_get_master_lcore() == i) + json_object_set_new(entry, "master", + json_boolean(1)); + else + json_object_set_new(entry, "master", + json_boolean(0)); + + /* append the new json object to the array */ + json_array_append(array, entry); + cnt++; + } + /* Create and append the lcore-cnt + * JSON object to root + */ + json_object_set_new(root, "lcore-cnt", + json_integer(cnt)); + + if (node->file_data) + free(node->file_data); + + /* Print the JSON info into the node to be access */ + dfs_json_set_data(node, root); + break; + case 3: /* socket-cnt */ + break; + case 4: /* hypervisor */ + break; + case 5: /* lcore-all */ + root = json_object(); /* create root object */ + array = json_array(); /* create the array object */ + + /* Create and add the array node to the list */ + json_object_set_new(root, "lcores", array); + + /* Create all of the array objects */ + cnt = 0; + for (i = 0; i < RTE_MAX_LCORE; i++) { + if ((node->user_id == 2) && + !rte_lcore_is_enabled(i)) + continue; + + entry = json_object(); + json_object_set_new(entry, "lcore", + json_integer(i)); + json_object_set_new(entry, "socket", + json_integer(rte_lcore_to_socket_id(i))); + json_object_set_new(entry, "type", + json_string(rte_lcore_has_role(i, ROLE_RTE) + ? "RTE" : + rte_lcore_has_role(i, ROLE_SERVICE) + ? "SERVICE" : "OFF")); + + if (rte_get_master_lcore() == i) + json_object_set_new(entry, "master", + json_boolean(1)); + else + json_object_set_new(entry, "master", + json_boolean(0)); + + /* append the new json object to the array */ + json_array_append(array, entry); + cnt++; + } + /* Create and append the lcore-cnt + * JSON object to root + */ + json_object_set_new(root, "lcore-cnt", + json_integer(cnt)); + + if (node->file_data) + free(node->file_data); + + /* Print the JSON info into the node to be access */ + dfs_json_set_data(node, root); + break; + + default: + break; + } + break; + + case DFS_FILE_RELEASE: + switch (node->user_id) { + case 2: /*lcore-list */ + /* When FUSE is done caching the node release any + * data attached to the node if needed. + */ + dfs_json_release_data(node); + break; + default: + break; + } + break; + + default: + break; + } + + return 0; +} + +static int +roles_file(struct dfs_node *node, oper_t opt) +{ + static char roles[RTE_MAX_LCORE + 8]; + char *p; + int i; + + switch (opt) { + case DFS_FILE_INIT: + dfs_node_init(node, const2char(roles), 0, 0); + break; + + case DFS_FILE_INFO: + memset(roles, '.', sizeof(roles)); + roles[sizeof(roles) - 1] = '\0'; + + p = roles; + *p++ = '['; + RTE_LCORE_FOREACH(i) { + if (rte_lcore_has_role(i, ROLE_RTE)) + p[i] = 'R'; + else if (rte_lcore_has_role(i, ROLE_SERVICE)) + p[i] = 'S'; + else + p[i] = '-'; + } + p[i++] = ']'; + p[i++] = '\n'; + p[i] = '\0'; + + node->file_size = strlen(roles); + break; + + default: + break; + } + return 0; +} + +static void +json_ptr(char *buf, int bufsize, void *val) +{ + if (val) + snprintf(buf, bufsize, "%p", val); + else + snprintf(buf, bufsize, "%s", "0x00"); +} + +static void +cfg_dump(FILE *fp) +{ + struct rte_config *cfg = rte_eal_get_configuration(); + struct rte_mem_config *m = cfg->mem_config; + char buf[RTE_MAX_LCORE + 64]; + json_t *entry; + char *p; + int i, cnt; + + json_t *root = json_object(); + + json_object_set_new(root, "master_lcore", + json_integer(cfg->master_lcore)); + json_object_set_new(root, "lcore_count", + json_integer(cfg->lcore_count)); + json_object_set_new(root, "numa_node_count", + json_integer(cfg->numa_node_count)); + json_object_set_new(root, "service_lcore_count", + json_integer(cfg->service_lcore_count)); + + memset(buf, '.', sizeof(buf)); + buf[sizeof(buf) - 1] = '\0'; + + p = buf; + RTE_LCORE_FOREACH(i) { + if (rte_lcore_has_role(i, ROLE_RTE)) + p[i] = 'R'; + else if (rte_lcore_has_role(i, ROLE_SERVICE)) + p[i] = 'S'; + else + p[i] = '-'; + } + p[i] = '\0'; + json_object_set_new(root, "lcore_role", json_string(buf)); + + json_object_set_new(root, "process_type", json_string( + (cfg->process_type == RTE_PROC_PRIMARY) ? "Primary" : + (cfg->process_type == RTE_PROC_SECONDARY) ? + "Secondary" : "Auto")); + json_object_set_new(root, "iova_mode", json_string( + (cfg->iova_mode == RTE_IOVA_DC) ? "DC" : + (cfg->iova_mode == RTE_IOVA_PA) ? "PA" : "VA")); + + json_t *mem_config = json_object(); + json_object_set_new(mem_config, "magic", + json_integer(m->magic)); + json_object_set_new(mem_config, "nchannel", + json_integer(m->nchannel)); + json_object_set_new(mem_config, "nrank", + json_integer(m->nrank)); + + json_ptr(buf, sizeof(buf), (void *)m->mem_cfg_addr); + json_object_set_new(mem_config, "mem_cfg_addr", + json_string(buf)); + + json_t *memzones = json_object(); + json_object_set_new(memzones, "name", + json_string(m->memzones.name)); + json_object_set_new(memzones, "count", + json_integer(m->memzones.count)); + json_object_set_new(memzones, "len", + json_integer(m->memzones.len)); + json_object_set_new(memzones, "elt_sz", + json_integer(m->memzones.elt_sz)); + + json_object_set_new(mem_config, "memzones", memzones); + json_object_set_new(root, "mem_config", mem_config); + + json_t *array = json_array(); + + json_object_set_new(mem_config, "memsegs", array); + + cnt = 0; + for (i = 0; i < RTE_MAX_MEMSEG_LISTS; i++) { + struct rte_memseg_list *ml = &m->memsegs[i]; + if (!ml->base_va) + continue; + entry = json_object(); + + json_ptr(buf, sizeof(buf), ml->base_va); + json_object_set_new(entry, "base_va", + json_string(buf)); + json_object_set_new(entry, "socket_id", + json_integer(ml->socket_id)); + json_object_set_new(entry, "page_sz", + json_integer(ml->page_sz)); + json_object_set_new(entry, "version", + json_integer(ml->version)); + + json_t *memseg_arr = json_object(); + json_object_set_new(memseg_arr, "name", + json_string(ml->memseg_arr.name)); + json_object_set_new(memseg_arr, "count", + json_integer(ml->memseg_arr.count)); + json_object_set_new(memseg_arr, "len", + json_integer(ml->memseg_arr.len)); + json_object_set_new(memseg_arr, "elt_sz", + json_integer(ml->memseg_arr.elt_sz)); + + json_object_set_new(entry, "memseg_arr", memseg_arr); + + cnt++; + json_array_append(array, entry); + } + json_object_set_new(mem_config, "memseg_cnt", json_integer(cnt)); + + array = json_array(); + json_object_set_new(mem_config, "tailq_head", array); + + cnt = 0; + for (i = 0; i < RTE_MAX_TAILQ; i++) { + struct rte_tailq_head *th = &m->tailq_head[i]; + + if (th->name[0] == '\0') + continue; + + json_array_append(array, json_string(th->name)); + + cnt++; + } + json_object_set_new(mem_config, "tailq_cnt", json_integer(cnt)); + + array = json_array(); + json_object_set_new(mem_config, "malloc_heap", array); + + cnt = 0; + for (i = 0; i < RTE_MAX_NUMA_NODES; i++) { + struct malloc_heap *h = &m->malloc_heaps[i]; + + if (!h->total_size) + continue; + + entry = json_object(); + + json_object_set_new(entry, "total_size", + json_integer(h->total_size)); + json_object_set_new(entry, "alloc_count", + json_integer(h->alloc_count)); + json_ptr(buf, sizeof(buf), h->first); + json_object_set_new(entry, "first", json_string(buf)); + json_ptr(buf, sizeof(buf), h->last); + json_object_set_new(entry, "last", json_string(buf)); + cnt++; + json_array_append(array, entry); + } + json_object_set_new(mem_config, "heap_cnt", json_integer(cnt)); + + p = json_dumps(root, DFS_JSON_INDENT); + fprintf(fp, "%s\n", p); /* Add a newline to the end */ + free(p); + json_decref(root); +} + +static int +config_file(struct dfs_node *node, oper_t opt) +{ + + switch (opt) { + case DFS_FILE_INIT: + dfs_node_init(node, NULL, 0, 0); + break; + + case DFS_FILE_INFO: + if (dfs_tmpfile_create(node, DFS_CONFIG_FILE, cfg_dump)) + return -1; + break; + + case DFS_FILE_RELEASE: + dfs_tmpfile_release(node); + break; + + default: + break; + } + return 0; +} + +static void +__memzone_walk(const struct rte_memzone *mz, void *arg) +{ + char buf[64]; + json_t *array = (json_t *)arg; + json_t *info = json_object(); + + json_object_set_new(info, "name", json_string(mz->name)); + json_object_set_new(info, "len", json_integer(mz->len)); + json_ptr(buf, sizeof(buf), (void *)mz->iova); + json_object_set_new(info, "iova", json_string(buf)); + json_ptr(buf, sizeof(buf), (void *)mz->addr); + json_object_set_new(info, "addr", json_string(buf)); + json_object_set_new(info, "hugepage_sz", + json_integer(mz->hugepage_sz)); + json_object_set_new(info, "socket_id", + json_integer(mz->socket_id)); + snprintf(buf, sizeof(buf), "0x%x", mz->flags); + json_object_set_new(info, "flags", json_string(buf)); + + json_array_append(array, info); +} + +static void +__memzone(FILE *fp) +{ + json_t *root = json_object(); + json_t *array = json_array(); + + json_object_set_new(root, "memzones", array); + + rte_memzone_walk(__memzone_walk, array); + + json_dumpf(root, fp, DFS_JSON_INDENT); + + fprintf(fp, "\n"); + json_decref(root); +} + +static int +memzone_file(struct dfs_node *node, oper_t opt) +{ + + switch (opt) { + case DFS_FILE_INIT: + dfs_node_init(node, NULL, 0, 0); + break; + + case DFS_FILE_INFO: + if (dfs_tmpfile_create(node, DFS_MEMZONE_FILE, + __memzone)) + return -1; + break; + + case DFS_FILE_RELEASE: + dfs_tmpfile_release(node); + break; + + default: + break; + } + return 0; +} + +static void +__tailq_walk(const struct rte_tailq_head *tq, void *arg) +{ + char buf[32]; + json_t *array = (json_t *)arg; + json_t *info = json_object(); + const struct rte_tailq_entry_head *head = &tq->tailq_head; + + if (!tq->name || (tq->name[0] == '\0')) + return; + + json_object_set_new(info, "name", json_string(tq->name)); + + json_ptr(buf, sizeof(buf), head->tqh_first); + json_object_set_new(info, "first", json_string(buf)); + + json_ptr(buf, sizeof(buf), head->tqh_last); + json_object_set_new(info, "last", json_string(buf)); + + json_array_append(array, info); +} + +static void +__tailq(FILE *fp) +{ + struct rte_config *cfg = rte_eal_get_configuration(); + struct rte_mem_config *m = cfg->mem_config; + json_t *root = json_object(); + json_t *array = json_array(); + int i, cnt = 0; + + json_object_set_new(root, "tailqs", array); + + rte_tailq_walk(__tailq_walk, array); + + for (i = 0; i < RTE_MAX_TAILQ; i++) { + struct rte_tailq_head *th = &m->tailq_head[i]; + if (th->name[0] == '\0') + continue; + + cnt++; + } + json_object_set_new(root, "tailq_cnt", json_integer(cnt)); + + json_dumpf(root, fp, DFS_JSON_INDENT); + + fprintf(fp, "\n"); + json_decref(root); +} + +static int +tailq_file(struct dfs_node *node, oper_t opt) +{ + + switch (opt) { + case DFS_FILE_INIT: + dfs_node_init(node, NULL, 0, 0); + break; + + case DFS_FILE_INFO: + if (dfs_tmpfile_create(node, DFS_TAILQ_FILE, __tailq)) + return -1; + break; + + case DFS_FILE_RELEASE: + dfs_tmpfile_release(node); + break; + + default: + break; + } + return 0; +} + +static void +pci_list_all(FILE *fp) +{ + (void)dfs_execute_cmd("/usr/bin/lspci", fp); +} + +static void +pci_list_ether(FILE *fp) +{ + if (dfs_execute_cmd("/usr/bin/lspci | grep Ether", fp) < 0) + return; + /* Add other device types here in a new call */ +} + +static void +_dummy(FILE *fp __rte_unused) +{ +} + +static struct bus_list bus_list[] = { + { DFS_BUSES_FILE, rte_bus_dump }, + { DFS_BUS_PCI_FILE, pci_list_all }, + { DFS_BUS_PCI_FILE, pci_list_ether }, + { DFS_BUS_VDEV_FILE, _dummy }, + { DFS_BUS_DPAA_FILE, _dummy }, + { DFS_BUS_FSLMC_FILE, _dummy }, + { DFS_BUS_IFPGA_FILE, _dummy } +}; + +static int +bus_file(struct dfs_node *node, oper_t opt) +{ + struct bus_list *b = &bus_list[node->user_id]; + + switch (opt) { + case DFS_FILE_INIT: + dfs_node_init(node, NULL, 0, 0); + break; + + case DFS_FILE_OPEN: + if (dfs_tmpfile_create(node, b->file, b->func)) + return -1; + break; + + case DFS_FILE_RELEASE: + dfs_tmpfile_release(node); + break; + + default: + break; + } + + return 0; +} + +static struct dfs_tree eal_tree[] = { + u_dir("eal"), + u_file("lcore-cnt", eal_file, DFS_FILE_RDONLY, 1), + u_file("lcore-list.json", eal_file, DFS_FILE_RDONLY, 2), + u_file("socket-cnt", eal_file, DFS_FILE_RDONLY, 3), + u_file("hypervisor", eal_file, DFS_FILE_RDONLY, 4), + u_file("lcore-all.json", eal_file, DFS_FILE_RDONLY, 5), + + u_file("roles", roles_file, DFS_FILE_RDONLY, 0), + u_file("config.json", config_file, DFS_FILE_RDONLY, 0), + u_file("memzones.json", memzone_file, DFS_FILE_RDONLY, 0), + u_file("tailqs.json", tailq_file, DFS_FILE_RDONLY, 0), + u_dir("bus"), + u_file("buses", bus_file, DFS_FILE_RDONLY, 0), + u_dir("pci"), + u_file("list-all", bus_file, DFS_FILE_RDONLY, 1), + u_file("list-ether", bus_file, DFS_FILE_RDONLY, 2), + u_dir("../vdev"), + u_file("list", bus_file, DFS_FILE_RDONLY, 3), + u_dir("../dpaa"), + u_file("list", bus_file, DFS_FILE_RDONLY, 4), + u_dir("../fslmc"), + u_file("list", bus_file, DFS_FILE_RDONLY, 5), + u_dir("../ifpga"), + u_file("list", bus_file, DFS_FILE_RDONLY, 6), + u_end() +}; + +static int +_eal_init(void) +{ + DFS_LOG(DEBUG, "DFS Setup EAL\n"); + return dfs_add_tree(get_root(), eal_tree); +} + +RTE_INIT(dfs_eal_init) +{ + dfs_register("EAL", _eal_init); +} diff --git a/lib/librte_dfs/dfs_eal.h b/lib/librte_dfs/dfs_eal.h new file mode 100644 index 000000000..99edf55a7 --- /dev/null +++ b/lib/librte_dfs/dfs_eal.h @@ -0,0 +1,102 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2018 Intel Corporation. + */ + +#ifndef _DFS_EAL_H_ +#define _DFS_EAL_H_ + +#include "dfs_search.h" + +/** + * @file + * RTE DFS EAL Operations + * + * These routines supply the EAL usage support routines. + */ + +#ifdef __cplusplus +extern "C" { +#endif + +#define DFS_CONFIG_FILE "config.tmp" +#define DFS_BUS_PCI_FILE "pci.tmp" +#define DFS_BUS_VDEV_FILE "vdev.tmp" +#define DFS_BUS_DPAA_FILE "dpaa.tmp" +#define DFS_BUS_FSLMC_FILE "fslmc.tmp" +#define DFS_BUS_IFPGA_FILE "ifpga.tmp" +#define DFS_BUSES_FILE "buses.tmp" +#define DFS_MEMZONE_FILE "memzone.tmp" +#define DFS_TAILQ_FILE "tailq.tmp" + +struct bus_list { + const char *file; + void (*func)(FILE *f); +}; + +static inline int +_parse_corelist(const char *corelist, uint8_t *lcores, int len) +{ + int idx = 0; + unsigned int count = 0; + char *end = NULL; + int min, max, k; + char cl_buf[128], *cl = cl_buf; + + if (corelist == NULL) + return -1; + + memset(lcores, 0, len); + + strlcpy(cl, corelist, sizeof(cl_buf)); + + /* Remove all blank characters ahead and after */ + cl = _strtrim(cl); + + /* Get list of cores */ + min = RTE_MAX_LCORE; + k = 0; + do { + while (isblank(*cl)) + cl++; + + if (*cl == '\0') + return -1; + + errno = 0; + idx = strtoul(cl, &end, 10); + if (errno || end == NULL) + return -1; + + while (isblank(*end)) + end++; + + if (*end == '-') + min = idx; + else if ((*end == ',') || (*end == '\0')) { + max = idx; + + if (min == RTE_MAX_LCORE) + min = idx; + + for (idx = min; idx <= max; idx++) { + lcores[idx] = 1; + count++; + } + min = RTE_MAX_LCORE; + } else + return -1; + + cl = end + 1; + + if (k >= len) + return -1; + } while (*end != '\0'); + + return count; +} + +#ifdef __cplusplus +} +#endif + +#endif /* _DFS_EAL_H_ */ diff --git a/lib/librte_dfs/dfs_ethdev.c b/lib/librte_dfs/dfs_ethdev.c new file mode 100644 index 000000000..af765804b --- /dev/null +++ b/lib/librte_dfs/dfs_ethdev.c @@ -0,0 +1,324 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2018 Intel Corporation. + */ + +#include +#include +#include + +#include "dfs.h" +#include "dfs_search.h" +#include "dfs_ethdev.h" + +static int +ethdev_file(struct dfs_node *node, oper_t opt) +{ + static char total_count[8]; + static char avail_count[8]; + + switch (opt) { + case DFS_FILE_INIT: + if (node->user_id == 1) + dfs_node_init(node, const2char(total_count), 0, 0); + else + dfs_node_init(node, const2char(avail_count), 0, 0); + break; + + case DFS_FILE_INFO: + if (node->user_id == 1) { + snprintf(total_count, sizeof(total_count), "%d\n", + rte_eth_dev_count_total()); + node->file_size = strlen(total_count); + } else { + snprintf(avail_count, sizeof(avail_count), "%d\n", + rte_eth_dev_count_avail()); + node->file_size = strlen(avail_count); + } + break; + + default: + break; + } + + return 0; +} + +static int +stats_file(struct dfs_node *node, oper_t opt) +{ + struct rte_eth_stats stat = { 0 }; + int i; + + switch (opt) { + case DFS_FILE_INIT: + dfs_node_init(node, NULL, 0, 0); + break; + + case DFS_FILE_INFO: + if (rte_eth_stats_get(node->user_id, &stat)) { + DFS_LOG(DEBUG, "Unable to get stats from port %d\n", + node->user_id); + return -1; + } + json_t *root = json_object(); + + json_object_set_new(root, "port_id", + json_integer(node->user_id)); + json_object_set_new(root, "ipackets", + json_integer(stat.ipackets)); + json_object_set_new(root, "opackets", + json_integer(stat.opackets)); + json_object_set_new(root, "ibytes", json_integer(stat.ibytes)); + json_object_set_new(root, "obytes", json_integer(stat.obytes)); + json_object_set_new(root, "imissed", + json_integer(stat.imissed)); + json_object_set_new(root, "ierrors", + json_integer(stat.ierrors)); + json_object_set_new(root, "oerrors", + json_integer(stat.oerrors)); + json_object_set_new(root, "rx_nombuf", + json_integer(stat.rx_nombuf)); + + json_t *array = json_array(); + json_object_set_new(root, "q_ipackets", array); + for (i = 0; i < RTE_ETHDEV_QUEUE_STAT_CNTRS; i++) + json_array_append(array, + json_integer(stat.q_ipackets[i])); + array = json_array(); + json_object_set_new(root, "q_opackets", array); + for (i = 0; i < RTE_ETHDEV_QUEUE_STAT_CNTRS; i++) + json_array_append(array, + json_integer(stat.q_opackets[i])); + array = json_array(); + json_object_set_new(root, "q_ibytes", array); + for (i = 0; i < RTE_ETHDEV_QUEUE_STAT_CNTRS; i++) + json_array_append(array, + json_integer(stat.q_ibytes[i])); + array = json_array(); + json_object_set_new(root, "q_obytes", array); + for (i = 0; i < RTE_ETHDEV_QUEUE_STAT_CNTRS; i++) + json_array_append(array, + json_integer(stat.q_obytes[i])); + array = json_array(); + json_object_set_new(root, "q_errors", array); + for (i = 0; i < RTE_ETHDEV_QUEUE_STAT_CNTRS; i++) + json_array_append(array, + json_integer(stat.q_errors[i])); + if (node->file_data) + free(node->file_data); + + dfs_json_set_data(node, root); + break; + + case DFS_FILE_RELEASE: + dfs_json_release_data(node); + break; + + default: + break; + } + + return 0; +} + +static int +sreset_file(struct dfs_node *node, oper_t opt) +{ + static char sreset[RTE_MAX_ETHPORTS]; + + switch (opt) { + case DFS_FILE_INIT: + memset(sreset, '0', sizeof(sreset)); + dfs_node_init(node, &sreset[node->user_id], 0, 1); + break; + + case DFS_FILE_WRITE: + if (sreset[node->user_id] == '1') { + rte_eth_stats_reset(node->user_id); + sreset[node->user_id] = '0'; + } + break; + + default: + break; + } + + return 0; +} + +#define DFS_SID_SIZE 8 + +static int +sid_file(struct dfs_node *node, oper_t opt) +{ + static char sids[RTE_MAX_ETHPORTS][DFS_SID_SIZE]; + + switch (opt) { + case DFS_FILE_INIT: + snprintf(&sids[node->user_id][0], DFS_SID_SIZE, "%d\n", + rte_eth_dev_socket_id(node->user_id)); + dfs_node_init(node, &sids[node->user_id][0], -1, 0); + break; + + default: + break; + } + + return 0; +} + +#define DFS_LINK_SIZE 16 + +static int +link_file(struct dfs_node *node, oper_t opt) +{ + struct rte_eth_link link = { 0 }; + + switch (opt) { + case DFS_FILE_INIT: + dfs_node_init(node, NULL, 0, 0); + break; + + case DFS_FILE_INFO: + rte_eth_link_get_nowait(node->user_id, &link); + + json_t *root = json_object(); + json_object_set_new(root, "link_speed", + json_integer(link.link_speed)); + json_object_set_new(root, "link_duplex", + json_string(link.link_duplex ? "Full" : "Half")); + json_object_set_new(root, "link_autoneg", + json_string(link.link_autoneg ? "On" : "Off")); + json_object_set_new(root, "link_status", + json_string(link.link_status ? "Up" : "Down")); + + dfs_json_set_data(node, root); + break; + + case DFS_FILE_RELEASE: + dfs_json_release_data(node); + break; + + default: + break; + } + + return 0; +} + +#define DFS_MTU_SIZE 8 + +static int +mtu_file(struct dfs_node *node, oper_t opt) +{ + static char mtus[RTE_MAX_ETHPORTS][DFS_MTU_SIZE]; + uint16_t mtu; + + switch (opt) { + case DFS_FILE_INIT: + dfs_node_init(node, &mtus[node->user_id][0], 0, DFS_MTU_SIZE); + break; + + case DFS_FILE_INFO: + rte_eth_dev_get_mtu(node->user_id, &mtu); + node->file_size = snprintf(&mtus[node->user_id][0], + DFS_MTU_SIZE, "%u\n", mtu); + break; + + case DFS_FILE_WRITE: + mtu = atoi(&mtus[node->user_id][0]); + rte_eth_dev_set_mtu(node->user_id, mtu); + break; + + default: + break; + } + + return 0; +} + +#define DFS_LED_SIZE 4 + +static int +led_file(struct dfs_node *node, oper_t opt) +{ + static char leds[RTE_MAX_ETHPORTS][DFS_LED_SIZE]; + int on; + + switch (opt) { + case DFS_FILE_INIT: + snprintf(&leds[node->user_id][0], DFS_LED_SIZE, "0\n"); + dfs_node_init(node, &leds[node->user_id][0], -1, DFS_LED_SIZE); + break; + + case DFS_FILE_INFO: + break; + + case DFS_FILE_WRITE: + DFS_LOG(DEBUG, "LED %s\n", &leds[node->user_id][0]); + on = atoi(&leds[node->user_id][0]); + if (on) + rte_eth_led_on(node->user_id); + else + rte_eth_led_off(node->user_id); + node->file_size = snprintf(&leds[node->user_id][0], + DFS_LED_SIZE, "%d\n", on); + break; + + default: + break; + } + + return 0; +} + +static struct dfs_tree ethdev_tree[] = { + u_dir("ethdev"), + u_file("total_count", ethdev_file, DFS_FILE_RDONLY, 1), + u_file("avail_count", ethdev_file, DFS_FILE_RDONLY, 2), + u_end() +}; + +#define ethdev_add(n, f, t) dfs_add_file(n, node, f, t, i) + +static int +_ethdev_init(void) +{ + struct dfs_node *parent, *node; + char name[16] = { 0 }; + int i, ret; + + DFS_LOG(DEBUG, "DFS Setup ETHDEV\n"); + ret = dfs_add_tree(get_root(), ethdev_tree); + if (ret) + return ret; + + parent = dfs_search_dir(get_root(), "ethdev", DFS_DIR_NODE); + if (!parent) { + DFS_LOG(DEBUG, "Unable to find /ethdev\n"); + return -1; + } + + for (i = 0; i < rte_eth_dev_count_total(); i++) { + snprintf(name, sizeof(name), "port-%d", i); + node = dfs_add_dir(name, parent); + if (!node) { + DFS_LOG(DEBUG, "Failed to add %s directory\n", name); + return -1; + } + /* macro uses node and i variables internally */ + ethdev_add("stats.json", stats_file, DFS_FILE_RDONLY); + ethdev_add("stat_reset", sreset_file, DFS_FILE_WRONLY); + ethdev_add("socket_id", sid_file, DFS_FILE_RDONLY); + ethdev_add("link.json", link_file, DFS_FILE_RDONLY); + ethdev_add("mtu", mtu_file, DFS_FILE_RDWR); + ethdev_add("led", led_file, DFS_FILE_RDWR); + } + + return 0; +} + +RTE_INIT(dfs_ethdev_init) +{ + dfs_register("Ethdev", _ethdev_init); +} diff --git a/lib/librte_dfs/dfs_ethdev.h b/lib/librte_dfs/dfs_ethdev.h new file mode 100644 index 000000000..76f30c1ab --- /dev/null +++ b/lib/librte_dfs/dfs_ethdev.h @@ -0,0 +1,23 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2018 Intel Corporation. + */ + +#ifndef _DFS_ETHDEV_H_ +#define _DFS_ETHDEV_H_ + +/** + * @file + * RTE DFS ETHDEV Operations + * + * These routines supply the ETHDEV usage support routines. + */ + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* _DFS_ETHDEV_H_ */ diff --git a/lib/librte_dfs/dfs_mempool.c b/lib/librte_dfs/dfs_mempool.c new file mode 100644 index 000000000..31765bbc0 --- /dev/null +++ b/lib/librte_dfs/dfs_mempool.c @@ -0,0 +1,130 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2018 Intel Corporation. + */ + +#include +#include +#include + +#include +#include +#include + +#include "dfs.h" +#include "dfs_mempool.h" + +static int +dump_file(struct dfs_node *node, oper_t opt) +{ + switch (opt) { + case DFS_FILE_INIT: + dfs_node_init(node, NULL, 0, 0); + break; + + case DFS_FILE_INFO: + if (dfs_tmpfile_create(node, DFS_MEMPOOL_DUMP_FILE, + rte_mempool_list_dump)) + return -1; + break; + + case DFS_FILE_RELEASE: + dfs_tmpfile_release(node); + break; + + default: + break; + } + + return 0; +} + +static void +__walk(struct rte_mempool *mp, void *arg) +{ + char str[24]; + json_t *root = (json_t *)arg; + json_t *info = json_object(); + + json_object_set_new(root, mp->name, info); + + snprintf(str, sizeof(str), "%p", mp->pool_config); + json_object_set_new(info, "pool_config", json_string(str)); + snprintf(str, sizeof(str), "%p", mp->mz); + json_object_set_new(info, "memzone", json_string(str)); + snprintf(str, sizeof(str), "%p", mp->pool_data); + json_object_set_new(info, "pool_data", json_string(str)); + snprintf(str, sizeof(str), "0x%x", mp->flags); + json_object_set_new(info, "flags", json_string(str)); + + json_object_set_new(info, "socket_id", json_integer(mp->socket_id)); + json_object_set_new(info, "size", json_integer(mp->size)); + json_object_set_new(info, "cache_size", json_integer(mp->cache_size)); + json_object_set_new(info, "elt_size", json_integer(mp->elt_size)); + json_object_set_new(info, "header_size", json_integer(mp->header_size)); + json_object_set_new(info, "trailer_size", + json_integer(mp->trailer_size)); + json_object_set_new(info, "private_data_size", + json_integer(mp->private_data_size)); + json_object_set_new(info, "ops_index", json_integer(mp->ops_index)); + json_object_set_new(info, "populated_size", + json_integer(mp->populated_size)); + json_object_set_new(info, "nb_mem_chunks", + json_integer(mp->nb_mem_chunks)); +} + +static void +__info(FILE *fp) +{ + json_t *root = json_object(); + + rte_mempool_walk(__walk, root); + + json_dumpf(root, fp, DFS_JSON_INDENT); + fprintf(fp, "\n"); + + json_decref(root); +} + +static int +info_file(struct dfs_node *node, oper_t opt) +{ + + switch (opt) { + case DFS_FILE_INIT: + dfs_node_init(node, NULL, 0, 0); + break; + + case DFS_FILE_INFO: + if (dfs_tmpfile_create(node, DFS_MEMPOOL_INFO_FILE, __info)) + return -1; + break; + + case DFS_FILE_RELEASE: + dfs_tmpfile_release(node); + break; + + default: + break; + } + + return 0; +} + +static struct dfs_tree mempool_tree[] = { + u_dir("mempool"), + u_file("dump", dump_file, DFS_FILE_RDONLY, 0), + u_file("info.json", info_file, DFS_FILE_RDONLY, 0), + u_end() +}; + +static int +_mempool_init(void) +{ + DFS_LOG(DEBUG, "DFS Setup MEMPOOL\n"); + return dfs_add_tree(NULL, mempool_tree); +} + +RTE_INIT(dfs_mempool_init) +{ + dfs_register("Mempool", _mempool_init); +} diff --git a/lib/librte_dfs/dfs_mempool.h b/lib/librte_dfs/dfs_mempool.h new file mode 100644 index 000000000..121217588 --- /dev/null +++ b/lib/librte_dfs/dfs_mempool.h @@ -0,0 +1,26 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2018 Intel Corporation. + */ + +#ifndef _DFS_MEMPOOL_H_ +#define _DFS_MEMPOOL_H_ + +/** + * @file + * RTE DFS MEMPOOL Operations + * + * These routines supply the MEMPOOL usage support routines. + */ + +#ifdef __cplusplus +extern "C" { +#endif + +#define DFS_MEMPOOL_DUMP_FILE "mempool-dump.tmp" +#define DFS_MEMPOOL_INFO_FILE "mempool-info.tmp" + +#ifdef __cplusplus +} +#endif + +#endif /* _DFS_MEMPOOL_H_ */ diff --git a/lib/librte_dfs/dfs_noop.c b/lib/librte_dfs/dfs_noop.c new file mode 100644 index 000000000..2f48d8923 --- /dev/null +++ b/lib/librte_dfs/dfs_noop.c @@ -0,0 +1,35 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2018 Intel Corporation. + */ +/* Created 2018 by Keith Wiles @ intel.com */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "dfs.h" + +static int32_t __rte_experimental +rte_dfs_init(void) +{ + return 0; +} + +static struct rte_option option = { + .opt_str = (char *)(uintptr_t)"--dfs", + .cb = &rte_dfs_init, + .enabled = 0 +}; + +RTE_INIT(libdfs_init_log) +{ + rte_option_register(&option); +} diff --git a/lib/librte_dfs/dfs_oper.c b/lib/librte_dfs/dfs_oper.c new file mode 100644 index 000000000..f789dcd6b --- /dev/null +++ b/lib/librte_dfs/dfs_oper.c @@ -0,0 +1,836 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2018 Intel Corporation. + */ + +/** @file + * FUSE library callback entry points. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef HAVE_SETXATTR +#include +#endif +#include /* flock(2) */ + +#ifdef HAVE_LIBULOCKMGR +#include +#endif + +#include +#include +#include +#include +#include + +#include "dfs.h" +#include "dfs_search.h" +#include "dfs_oper.h" + +#define DFS_TIMEOUT_SECONDS 3 + +static void +print_capabilities(unsigned int c) +{ + struct { + unsigned int bit; + const char *name; + } names[] = { +#define _(x) { x, #x } + + _(FUSE_CAP_ASYNC_READ), + _(FUSE_CAP_POSIX_LOCKS), + _(FUSE_CAP_ATOMIC_O_TRUNC), + _(FUSE_CAP_EXPORT_SUPPORT), + _(FUSE_CAP_DONT_MASK), + _(FUSE_CAP_SPLICE_WRITE), + _(FUSE_CAP_SPLICE_MOVE), + _(FUSE_CAP_SPLICE_READ), + _(FUSE_CAP_FLOCK_LOCKS), + _(FUSE_CAP_IOCTL_DIR), + _(FUSE_CAP_AUTO_INVAL_DATA), + _(FUSE_CAP_READDIRPLUS), + _(FUSE_CAP_READDIRPLUS_AUTO), + _(FUSE_CAP_ASYNC_DIO), + _(FUSE_CAP_WRITEBACK_CACHE), + _(FUSE_CAP_NO_OPEN_SUPPORT), + _(FUSE_CAP_PARALLEL_DIROPS), + _(FUSE_CAP_POSIX_ACL), + _(FUSE_CAP_HANDLE_KILLPRIV), + +#undef _ + { 0, NULL } + }; + unsigned int i, k; + + printf(" "); + for (i = 0, k = 0; i < countof(names) - 1; i++) { + if (c & names[i].bit) + printf(">%-18s", &names[i].name[9]); + else + printf(">%-18s", ""); + + if ((++k % 4) == 0) { + k = 0; + printf("\n "); + } + } + printf("\n\n"); +} + +static void +dump_fuse_info(struct fuse_conn_info *conn) +{ + (void)print_capabilities; + + if (dfs_debug_enabled()) { + printf("\nFUSE Kernel capabilities 0x%08x:\n", conn->capable); + print_capabilities(conn->capable); + + printf("Capabilities wanted 0x%08x:\n", conn->want); + print_capabilities(conn->want); + + printf(" FUSE Kernel Version %u.%u\n", + conn->proto_major, conn->proto_minor); + printf(" max_write %u, max_read %u, max_readahead %u\n", + conn->max_write, conn->max_read, conn->max_readahead); + printf( + " max_background %u, congestion_threshold %u, time_gran %u\n", + conn->max_background, + conn->congestion_threshold, + conn->time_gran); + printf("\n"); + } +} + +static void +dfs_init(void *userdata __rte_unused, struct fuse_conn_info *conn) +{ + if (conn->capable & FUSE_CAP_EXPORT_SUPPORT) { + if (dfs_debug_enabled()) + printf("*** EXPORT_SUPPORT found, enabling\n"); + conn->want |= FUSE_CAP_EXPORT_SUPPORT; + } + if (conn->capable & FUSE_CAP_WRITEBACK_CACHE) { + if (dfs_debug_enabled()) + printf("*** WRITEBACK_CACHE found, disabling\n"); + conn->want &= ~FUSE_CAP_WRITEBACK_CACHE; + } + + dump_fuse_info(conn); +} + +static void +dfs_ll_destroy(void *userdata) +{ + struct dfs *dfs = (struct dfs *)userdata; + + DFS_LOG(DEBUG, "Destroy %p\n", dfs); +} + +static int +dfs_stat(struct dfs_node *node, struct stat *stbuf) +{ + int mode; + + stbuf->st_ino = node->ino; + stbuf->st_uid = dfs_get_instance()->uid; + stbuf->st_gid = dfs_get_instance()->gid; + stbuf->st_blksize = getpagesize(); + + stbuf->st_atim = node->atime; + stbuf->st_mtim = node->mtime; + stbuf->st_ctim = node->ctime; + + mode = 0444; /* Read Only */ + switch (node->type) { + case DFS_DIR_NODE: + stbuf->st_mode = S_IFDIR | 0755; + stbuf->st_nlink = 2; + stbuf->st_size = 1; + break; + + case DFS_FILE_NODE: + if (node->fmode == DFS_FILE_RDWR) + mode = 0666; + else if (node->fmode == DFS_FILE_WRONLY) + mode = 0222; + + dfs_file_info(node); + + stbuf->st_mode = S_IFREG | mode; + stbuf->st_nlink = 1; + stbuf->st_size = node->file_size; + break; + + case DFS_ALIAS_NODE: + stbuf->st_mode = S_IFREG | mode; + stbuf->st_nlink = 1; + stbuf->st_size = node->file_size; + break; + + default: + DFS_LOG(ERR, "Node type not found %d\n", node->type); + return -1; + } + return 0; +} + +#define min(x, y) ((x) < (y) ? (x) : (y)) + +static inline int +reply_buf_limited(fuse_req_t req, const char *buf, size_t bufsize, + off_t off, size_t maxsize) +{ + DFS_LOG(DEBUG, "off %ld bufsize %ld maxsize %ld\n", + off, bufsize, maxsize); + if (off < (off_t)bufsize) + return fuse_reply_buf(req, buf + off, min(bufsize - off, + maxsize)); + else + return fuse_reply_buf(req, NULL, 0); +} + +static void +dfs_getattr(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi) +{ + struct dfs_node *node = (fi) ? (struct dfs_node *)fi->fh : NULL; + struct stat stbuf = { 0 }; + + DFS_LOG(DEBUG, "req %p, ino %ld fi %p\n", req, ino, fi); + + if (!node) { + node = dfs_find_inode(ino); + + if (!node) { + DFS_LOG(ERR, "inode %ld not found\n", ino); + fuse_reply_err(req, ENOENT); + return; + } + } + if (node->ino != ino) { + DFS_LOG(ERR, "inode %ld does not match %ld\n", node->ino, ino); + fuse_reply_err(req, ENOENT); + return; + } + + if (dfs_stat(node, &stbuf)) + fuse_reply_err(req, ENOENT); + else + fuse_reply_attr(req, &stbuf, 1.0); + DFS_LOG(DEBUG, "node %s ino %ld file_size %ld\n", + node->name, ino, node->file_size); +} + +static void +dfs_lookup(fuse_req_t req, fuse_ino_t parent, const char *name) +{ + struct fuse_entry_param e = { 0 }; + struct dfs_node *node, *dir; + + DFS_LOG(DEBUG, "req %p, look in ino %ld and find (%s)\n", + req, parent, name); + + e.attr.st_ino = parent; + e.attr_timeout = 0.0; + e.entry_timeout = 0.0; + + dir = dfs_find_inode(parent); + if (!dir) { + DFS_LOG(DEBUG, "Leave inode %ld Not Found\n", parent); + fuse_reply_err(req, ENOENT); + return; + } + DFS_LOG(DEBUG, "Found directory ('%s', %ld)\n", dir->name, dir->ino); + + node = dfs_search_dir(dir, name, DFS_VALID_NODE); + if (!node) { + DFS_LOG(DEBUG, "Leave '%s' Not Found\n", name); + fuse_reply_err(req, ENOENT); + return; + } + + e.ino = node->ino; + node->nlookup++; + + DFS_LOG(DEBUG, "generation 0x%lx\n", e.generation); + + dfs_stat(node, &e.attr); + + fuse_reply_entry(req, &e); + + DFS_LOG(DEBUG, "Found ('%s', %ld) size %ld\n", + node->name, node->ino, node->file_size); +} + +static void +dfs_forget(fuse_req_t req, fuse_ino_t ino, uint64_t nlookup) +{ + struct dfs_node *node; + + DFS_LOG(DEBUG, "req %p, ino %ld nlookup %ld\n", req, ino, nlookup); + + node = dfs_find_inode(ino); + if (!node) { + DFS_LOG(ERR, "Did not find inode %ld\n", ino); + fuse_reply_err(req, ENOENT); + return; + } + + if (nlookup > node->nlookup) + DFS_LOG(ERR, "nlookup %ld > %ld node->nlookup\n", + nlookup, node->nlookup); + else + node->nlookup -= nlookup; + + fuse_reply_none(req); +} + +static void +dfs_readlink(fuse_req_t req, fuse_ino_t ino) +{ + DFS_LOG(DEBUG, "req %p, ino %ld\n", req, ino); + + return (void)fuse_reply_err(req, ENOENT); +} + +static void +dfs_opendir(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi) +{ + int error = ENOMEM; + struct dfs_node *node; + + DFS_LOG(DEBUG, "req %p, ino %ld\n", req, ino); + + fi->fh = 0; + + node = dfs_find_inode(ino); + if (!node) { + fuse_reply_err(req, error); + return; + } + + node->file_offset = 0; + + DFS_LOG(DEBUG, "Dir name %s\n", node->name); + + /* Save node pointer in the file_info */ + fi->fh = (uintptr_t)node; + + fuse_reply_open(req, fi); +} + +static void +dfs_do_readdir(fuse_req_t req, fuse_ino_t ino, size_t size, + off_t offset, struct fuse_file_info *fi, int plus) +{ + struct dfs_node *node, *dir; + off_t off; + char *buf; + char *p; + size_t rem; + + DFS_LOG(DEBUG, "req %p, ino %ld size %ld offset %ld plus %d\n", + req, ino, size, offset, plus); + + dir = (struct dfs_node *)fi->fh; + if (!dir || dir->type != DFS_DIR_NODE) + return; + + buf = calloc(size + 1, 1); + if (!buf) + return (void)fuse_reply_err(req, ENOMEM); + + DFS_LOG(DEBUG, "offset %ld, dir->foffset %ld\n", + offset, dir->file_offset); + + off = 0; + rem = size; + + /* Seek to the new offset */ + for (node = TAILQ_FIRST(&dir->items); + node != NULL; + node = TAILQ_NEXT(node, next)) { + if (off >= offset) + break; + off++; + } + + if (offset != dir->file_offset) + dir->file_offset = offset; + + if (!node) { + fuse_reply_err(req, ENOENT); + free(buf); + return; + } + + p = buf; + for (; node != NULL; node = TAILQ_NEXT(node, next)) { + size_t entsize; + + dir->file_offset++; + + if (plus) { + struct fuse_entry_param e = { 0 }; + + e.attr_timeout = 1.0; + e.entry_timeout = 1.0; + + dfs_stat(node, &e.attr); + + DFS_LOG(DEBUG, "plus add node (%s) ino %ld off %ld\n", + node->name, node->ino, dir->file_offset); + entsize = fuse_add_direntry_plus(req, p, rem, + node->name, &e, dir->file_offset); + } else { + struct stat st = { + .st_ino = node->ino, + .st_mode = node->fmode << 12, + }; + DFS_LOG(DEBUG, "add node (%s) ino %ld off %ld\n", + node->name, node->ino, dir->file_offset); + entsize = fuse_add_direntry(req, p, rem, + node->name, &st, dir->file_offset); + } + + if (entsize > rem) + break; + + p += entsize; + rem -= entsize; + } + + DFS_LOG(DEBUG, "Leave size %ld\n", size - rem); + + fuse_reply_buf(req, buf, size - rem); + free(buf); +} + +static void +dfs_readdir(fuse_req_t req, fuse_ino_t ino, size_t size, + off_t offset, struct fuse_file_info *fi) +{ + dfs_do_readdir(req, ino, size, offset, fi, 0); +} + +static void +dfs_readdirplus(fuse_req_t req, fuse_ino_t ino, size_t size, + off_t offset, struct fuse_file_info *fi) +{ + dfs_do_readdir(req, ino, size, offset, fi, 1); +} + +static void +dfs_releasedir(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi) +{ + struct dfs_node *node = (struct dfs_node *)fi->fh; + + DFS_LOG(DEBUG, "req %p, ino %ld, node %s\n", req, ino, node->name); + + (void)ino; + node->file_offset = 0; /* Reset the read index */ + + fuse_reply_err(req, 0); +} + +static void +dfs_ll_create(fuse_req_t req, fuse_ino_t parent, const char *name, + mode_t mode, struct fuse_file_info *fi) +{ + (void)req; + (void)parent; + (void)name; + (void)mode; + (void)fi; + + DFS_LOG(DEBUG, "req %p, ino %ld name %s mode %o\n", + req, parent, name, mode); + + fuse_reply_err(req, ENOMEM); +} + +static void +dfs_open(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi) +{ + struct dfs_node *node; + + DFS_LOG(DEBUG, "req %p, ino %ld fi %p\n", req, ino, fi); + + if (!fi) { + DFS_LOG(ERR, "pointer fi is NULL\n"); + return; + } + + node = (struct dfs_node *)fi->fh; + if (!node) { + DFS_LOG(DEBUG, "fi->fh is NULL find inode %ld\n", ino); + node = dfs_find_inode(ino); + if (!node) { + DFS_LOG(ERR, "inode %ld not found\n", ino); + fuse_reply_err(req, ENOENT); + return; + } + } + + if (is_directory(node)) { + DFS_LOG(ERR, "inode %ld (%s) is directory\n", + node->ino, node->name); + fuse_reply_err(req, EISDIR); + } else if ((fi->flags & O_RDWR) != O_RDONLY) { + DFS_LOG(ERR, "inode %ld (%s) permission problem\n", + node->ino, node->name); + fuse_reply_err(req, EACCES); + } else { + if (dfs_file_open(node)) { + DFS_LOG(ERR, "inode %ld (%s) dfs_file_open() failed\n", + node->ino, node->name); + fuse_reply_err(req, EACCES); + } else { + /* Save node pointer for later use */ + fi->fh = (uint64_t)node; + + fi->keep_cache = 0; + + fuse_reply_open(req, fi); + } + } +} + +static void +dfs_release(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi) +{ + struct dfs_node *node = (struct dfs_node *)fi->fh; + + (void)ino; + + DFS_LOG(DEBUG, "fd %ld\n", ino); + + fi->fh = 0; + + fuse_reply_err(req, dfs_file_release(node)); +} + +static void +dfs_read(fuse_req_t req, fuse_ino_t ino, size_t size, + off_t offset, struct fuse_file_info *fi) +{ + struct dfs_node *node = (struct dfs_node *)fi->fh; + + DFS_LOG(DEBUG, "req %p, ino %ld size %ld offset %ld (%s)\n", + req, ino, size, offset, node->name); + + node->file_offset = offset; + + if (node->file_fd >= 0) { + struct fuse_bufvec buf = FUSE_BUFVEC_INIT(size); + + buf.buf[0].flags = FUSE_BUF_IS_FD | FUSE_BUF_FD_SEEK; + buf.buf[0].fd = (uint64_t)node->file_fd; + buf.buf[0].pos = offset; + + if (dfs_file_read(node)) { + fuse_reply_err(req, -1); + return; + } + + fuse_reply_data(req, &buf, FUSE_BUF_SPLICE_MOVE); + } else + reply_buf_limited(req, node->file_data, + node->file_size, offset, size); +} + +static void +dfs_write_buf(fuse_req_t req, fuse_ino_t ino, + struct fuse_bufvec *in_buf, off_t off, + struct fuse_file_info *fi) +{ + struct dfs_node *node = (struct dfs_node *)fi->fh; + ssize_t sz; + + (void)ino; + + DFS_LOG(DEBUG, "req %p, ino %ld off %ld\n", req, ino, off); + + if (node->file_fd >= 0) { + struct fuse_bufvec out_buf = + FUSE_BUFVEC_INIT(fuse_buf_size(in_buf)); + + out_buf.buf[0].flags = FUSE_BUF_IS_FD | FUSE_BUF_FD_SEEK; + out_buf.buf[0].fd = fi->fh; + out_buf.buf[0].pos = off; + + sz = fuse_buf_copy(&out_buf, in_buf, 0); + if (sz < 0) + fuse_reply_err(req, -sz); + else + fuse_reply_write(req, (size_t)sz); + } else { + sz = in_buf->buf[0].size; + if ((sz + off) > (off_t)node->maxfile_size) { + sz = EFBIG; + goto err_exit; + } + + rte_memcpy(node->file_data + off, in_buf->buf[0].mem, sz); + + node->file_size = sz + off; + if (dfs_file_write(node)) { + sz = -1; + goto err_exit; + } + + fuse_reply_write(req, sz); + } + return; + +err_exit: + fuse_reply_err(req, sz); +} + +static void +dfs_fsync(fuse_req_t req, fuse_ino_t ino, int datasync, + struct fuse_file_info *fi) +{ + (void)ino; + (void)datasync; + (void)fi; + + DFS_LOG(DEBUG, "ino %ld\n", ino); + fuse_reply_err(req, ENOENT); +} + +static void +dfs_fsyncdir(fuse_req_t req, fuse_ino_t ino, int datasync, + struct fuse_file_info *fi) +{ + (void)ino; + (void)datasync; + (void)fi; + + DFS_LOG(DEBUG, "ino %ld\n", ino); + fuse_reply_err(req, ENOENT); +} + +static struct fuse_lowlevel_ops dfs_oper = { + .init = dfs_init, + .destroy = dfs_ll_destroy, + .lookup = dfs_lookup, + .forget = dfs_forget, + .getattr = dfs_getattr, + .setattr = NULL, + .readlink = dfs_readlink, + .mknod = NULL, + .mkdir = NULL, + .unlink = NULL, + .rmdir = NULL, + .symlink = NULL, + .rename = NULL, + .link = NULL, + .open = dfs_open, + .read = dfs_read, + .write = NULL, + .flush = NULL, + .release = dfs_release, + .fsync = dfs_fsync, + .opendir = dfs_opendir, + .readdir = dfs_readdir, + .releasedir = dfs_releasedir, + .fsyncdir = dfs_fsyncdir, + .statfs = NULL, + .setxattr = NULL, + .getxattr = NULL, + .listxattr = NULL, + .removexattr = NULL, + .access = NULL, + .create = dfs_ll_create, + .getlk = NULL, + .setlk = NULL, + .bmap = NULL, + .ioctl = NULL, + .poll = NULL, + .write_buf = dfs_write_buf, + .retrieve_reply = NULL, + .forget_multi = NULL, + .flock = NULL, + .fallocate = NULL, + .readdirplus = dfs_readdirplus +}; + +static void reset_sig_handlers(void); + +static void +sig_handler(int sig) +{ + struct dfs *dfs = dfs_get_instance(); + + if (!dfs || !dfs->se) + return; + + fuse_session_exit(dfs->se); + + if (dfs_get_sig_handler()) { + _sig_handler_t sig_handler = dfs_get_sig_handler(); + + sig_handler(sig); + } +} + +static int +set_sig_handler(int sig, void (*handler)(int)) +{ + struct sigaction sa = { 0 }; + + sigemptyset(&(sa.sa_mask)); + sa.sa_handler = (handler == NULL) ? SIG_DFL : handler; + sa.sa_flags = 0; + + return sigaction(sig, &sa, NULL); +} + +static int +set_sig_handlers(void) +{ + if (set_sig_handler(SIGINT, sig_handler) < 0 || + set_sig_handler(SIGTERM, sig_handler) < 0) + return -1; + + return 0; +} + +static void +reset_sig_handlers(void) +{ + set_sig_handler(SIGINT, NULL); + set_sig_handler(SIGTERM, NULL); +} + +struct fuse_session * +dfs_ops_create(struct dfs *dfs) +{ + struct fuse_session *se = NULL; + char path[DFS_MAX_PATH_LENGTH], *dir; + struct fuse_args args = FUSE_ARGS_INIT(0, NULL); + + umask(0); + + errno = 0; + dir = const2char(dfs_get_basedir()); + if (access(dir, F_OK)) { + if (mkdir(dir, 0775)) { + DFS_LOG(DEBUG, "Failed to create (%s)! %s\n", + dir, strerror(errno)); + return NULL; + } + if (chown(dir, dfs->uid, dfs->gid)) { + DFS_LOG(DEBUG, "chown() failed %s\n", strerror(errno)); + return NULL; + } + } + + /* Create complete path of FUSE mount point */ + snprintf(path, sizeof(path), "%s/%s-%d", dir, dfs_get_prgname(), + dfs->pid); + + if (mkdir(path, 0775)) { + if (errno != EEXIST) { + DFS_LOG(DEBUG, "Failed to create (%s)!\n", path); + return NULL; + } + } + if (chown(path, dfs->uid, dfs->gid)) { + DFS_LOG(DEBUG, "chown() failed %s\n", strerror(errno)); + return NULL; + } + + /* Setup the arguments */ + fuse_opt_add_arg(&args, dfs_get_prgname()); + + DFS_LOG(DEBUG, + "\n*** DFS: Make sure 'user_allow_other' is enabled in /etc/fuse.conf\n"); + + fuse_opt_add_arg(&args, "-o"); + fuse_opt_add_arg(&args, "allow_other"); + + DFS_LOG(DEBUG, "Create new FUSE session\n"); + se = fuse_session_new(&args, &dfs_oper, sizeof(dfs_oper), dfs); + if (!se) { + DFS_LOG(ERR, "New FUSE session failed!\n"); + goto err_exit; + } + + DFS_LOG(DEBUG, "Set signal handlers\n"); + if (set_sig_handlers() != 0) { + DFS_LOG(ERR, "FUSE set signal handlers failed!\n"); + goto err_exit; + } + + DFS_LOG(DEBUG, "Mount FUSE file system\n"); + if (fuse_session_mount(se, path) != 0) { + DFS_LOG(ERR, "FUSE session mount failed!\n"); + goto err_exit; + } + + DFS_LOG(DEBUG, "Free options args\n"); + fuse_opt_free_args(&args); + + return se; + +err_exit: + fuse_opt_free_args(&args); + + dfs_ops_destroy(dfs); + + return NULL; +} + +void +dfs_ops_destroy(struct dfs *dfs) +{ + char path[DFS_MAX_PATH_LENGTH]; + uint64_t tsc_end; + + if (!dfs || !dfs->se) + return; + + if (fuse_session_exited(dfs->se) == 0) + fuse_session_exit(dfs->se); + + tsc_end = rte_rdtsc(); + tsc_end += (rte_get_timer_hz() * DFS_TIMEOUT_SECONDS); + + /* Wait for the session to exit for a least a few seconds */ + while (rte_rdtsc() < tsc_end && fuse_session_exited(dfs->se) == 0) + rte_pause(); + + reset_sig_handlers(); + + fuse_session_unmount(dfs->se); + fuse_session_destroy(dfs->se); + + /* Need to make sure the directory is removed */ + snprintf(path, sizeof(path), "%s/%s-%d", dfs_get_basedir(), + dfs_get_prgname(), dfs->pid); + if (rmdir(path)) + printf("%s: rmdir(%s) failed: %s\n", + __func__, path, strerror(errno)); + + if (dfs_get_sig_handler() == NULL) + kill(getpid(), SIGINT); +} diff --git a/lib/librte_dfs/dfs_oper.h b/lib/librte_dfs/dfs_oper.h new file mode 100644 index 000000000..a853201f2 --- /dev/null +++ b/lib/librte_dfs/dfs_oper.h @@ -0,0 +1,48 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2018 Intel Corporation. + */ + +#ifndef _DFS_OPER_H_ +#define _DFS_OPER_H_ + +/** + * @file + * RTE DFS Operations + * + * The dfs library provides the ability to create and destroy configuration + * files and directories in a FUSE file system. Allowing configuration at + * runtime for DPDK applications. + * + * This file will provide the higher layer routines on top of FUSE low level + * API set. The routines here will call the top level simplified user API. + */ + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Create the operation + * + * @param dfs + * The pointer the DFS structure. + * @return + * fuse session pointer on exit or NULL on error + */ +struct fuse_session *__rte_experimental dfs_ops_create(struct dfs *dfs); + +/** + * destroy the operation + * + * @param dfs + * The DFS structure pointer + * @return + * N/A + */ +void __rte_experimental dfs_ops_destroy(struct dfs *dfs); + +#ifdef __cplusplus +} +#endif + +#endif /* _DFS_OPER_H_ */ diff --git a/lib/librte_dfs/dfs_rawdev.c b/lib/librte_dfs/dfs_rawdev.c new file mode 100644 index 000000000..a1306db34 --- /dev/null +++ b/lib/librte_dfs/dfs_rawdev.c @@ -0,0 +1,54 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2018 Intel Corporation. + */ + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "dfs.h" +#include "dfs_rawdev.h" + +#ifdef RTE_LIBRTE_RAWDEV +static int +rawdev_info(struct dfs_node *node, oper_t opt) +{ + static char count[24]; + + switch (opt) { + case DFS_FILE_INIT: + snprintf(count, sizeof(count), "%d\n", rte_rawdev_count()); + dfs_node_init(node, count, -1, 0); + break; + + default: + break; + } + + return 0; +} + +static struct dfs_tree rawdev_tree[] = { + u_dir("rawdev"), + u_file("count", rawdev_info, DFS_FILE_RDONLY, 0), + u_end() +}; + +static int +_rawdev_init(void) +{ + DFS_LOG(DEBUG, "DFS Setup RAWDEV\n"); + return dfs_add_tree(NULL, rawdev_tree); +} + +RTE_INIT(dfs_rawdev_init) +{ + dfs_register("Rawdev", _rawdev_init); +} +#endif diff --git a/lib/librte_dfs/dfs_rawdev.h b/lib/librte_dfs/dfs_rawdev.h new file mode 100644 index 000000000..69edb7cf0 --- /dev/null +++ b/lib/librte_dfs/dfs_rawdev.h @@ -0,0 +1,25 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2018 Intel Corporation. + */ + +#ifndef _DFS_RAWDEV_H_ +#define _DFS_RAWDEV_H_ + +/** + * @file + * RTE DFS Rawdev Operations + * + * These routines supply the Rawdev usage support routines. + */ + +#ifdef __cplusplus +extern "C" { +#endif + +#define DFS_RAWDEV_INFO_FILE "rawdev-info.tmp" + +#ifdef __cplusplus +} +#endif + +#endif /* _DFS_RAWDEV_H_ */ diff --git a/lib/librte_dfs/dfs_ring.c b/lib/librte_dfs/dfs_ring.c new file mode 100644 index 000000000..c952c07c3 --- /dev/null +++ b/lib/librte_dfs/dfs_ring.c @@ -0,0 +1,107 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2018 Intel Corporation. + */ + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "dfs.h" +#include "dfs_ring.h" + +static void +__walk(struct rte_ring *r, void *arg) +{ + char str[24]; + json_t *array = (json_t *)arg; + json_t *info = json_object(); + + json_object_set_new(info, "name", json_string(r->name)); + + snprintf(str, sizeof(str), "%p", r); + json_object_set_new(info, "addr", json_string(str)); + snprintf(str, sizeof(str), "0x%x", r->flags); + json_object_set_new(info, "flags", json_string(str)); + + json_object_set_new(info, "size", json_integer(r->size)); + json_object_set_new(info, "size", json_integer(r->size)); + json_object_set_new(info, "capacity", json_integer(r->capacity)); + json_object_set_new(info, "cons_tail", json_integer(r->cons.tail)); + json_object_set_new(info, "cons_head", json_integer(r->cons.head)); + json_object_set_new(info, "prod_tail", json_integer(r->prod.tail)); + json_object_set_new(info, "prod_head", json_integer(r->prod.head)); + json_object_set_new(info, "used", json_integer(rte_ring_count(r))); + json_object_set_new(info, "avail", + json_integer(rte_ring_free_count(r))); + + json_array_append(array, info); +} + +static void +__info(FILE *fp) +{ + json_t *root = json_object(); + json_t *array = json_array(); + + json_object_set_new(root, "rings", array); + + rte_ring_walk(__walk, array); + + json_dumpf(root, fp, DFS_JSON_INDENT); + + fprintf(fp, "\n"); + json_decref(root); +} + +static int +ring_info(struct dfs_node *node, oper_t opt) +{ + DFS_LOG(DEBUG, "node %s ino %ld, %s\n", + node->name, node->ino, dfs_opt_str(opt)); + + switch (opt) { + case DFS_FILE_INIT: + dfs_node_init(node, NULL, 0, 0); + break; + + case DFS_FILE_OPEN: + if (dfs_tmpfile_create(node, DFS_RING_INFO_FILE, __info)) { + DFS_LOG(INFO, "file_size %ld\n", node->file_size); + return -1; + } + break; + + case DFS_FILE_RELEASE: + dfs_tmpfile_release(node); + break; + + default: + break; + } + + return 0; +} + +static struct dfs_tree ring_tree[] = { + u_dir("ring"), + u_file("info.json", ring_info, DFS_FILE_RDONLY, 0), + u_end() +}; + +static int +_ring_init(void) +{ + DFS_LOG(DEBUG, "DFS Setup RING\n"); + return dfs_add_tree(NULL, ring_tree); +} + +RTE_INIT(dfs_ring_init) +{ + dfs_register("Ring", _ring_init); +} diff --git a/lib/librte_dfs/dfs_ring.h b/lib/librte_dfs/dfs_ring.h new file mode 100644 index 000000000..4e78c1120 --- /dev/null +++ b/lib/librte_dfs/dfs_ring.h @@ -0,0 +1,25 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2018 Intel Corporation. + */ + +#ifndef _DFS_RING_H_ +#define _DFS_RING_H_ + +/** + * @file + * RTE DFS Ring Operations + * + * These routines supply the Ring usage support routines. + */ + +#ifdef __cplusplus +extern "C" { +#endif + +#define DFS_RING_INFO_FILE "ring-info.tmp" + +#ifdef __cplusplus +} +#endif + +#endif /* _DFS_RING_H_ */ diff --git a/lib/librte_dfs/dfs_search.c b/lib/librte_dfs/dfs_search.c new file mode 100644 index 000000000..4912e62db --- /dev/null +++ b/lib/librte_dfs/dfs_search.c @@ -0,0 +1,203 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2018 Intel Corporation. + */ + +#include "dfs.h" +#include "dfs_search.h" + +static int +__count_nodes(struct dfs_node *node, + uint32_t flags, args_t *args) +{ + if (flags & node->type) + args->arg1.u32[0]++; + + return 0; +} + +uint32_t +dfs_dir_item_count(struct dfs_node *node, uint32_t types) +{ + args_t args; + + if (!node || !is_directory(node)) + return 0; + + memset(&args, '\0', sizeof(args)); + + dfs_scan_directory(node, __count_nodes, types, &args); + + return args.arg1.u32[0]; +} + +int +dfs_scan_directory(struct dfs_node *dir, + dfs_scan_t func, uint32_t flags, args_t *args) +{ + struct dfs_node *node; + int ret = 0; + + if (!func) + return ret; + + if (!dir) + dir = get_root(); + + TAILQ_FOREACH(node, &dir->items, next) { + if (node->type & flags) { + ret = func(node, flags, args); + if (ret) + break; + } + } + return ret; +} + +int +dfs_scan_path(const char *path, dfs_scan_t func, uint32_t flags, args_t *args) +{ + struct dfs_node *node; + + if (dfs_find_node(path, &node, DFS_VALID_NODE)) + if (dfs_scan_directory(node, func, flags, args)) + return 1; + return 0; +} + +struct dfs_node * +dfs_search_dir(struct dfs_node *dir, const char *name, uint32_t type) +{ + struct dfs_node *node; + + if (!name || (*name == '\0')) + return NULL; + + if (!dir) + dir = get_root(); + else if (!is_directory(dir)) + return NULL; + + /* Process the .. and . directories */ + if (!strcmp(name, "..")) + return dir->parent; + else if (!strcmp(name, ".")) + return dir; + + TAILQ_FOREACH(node, &dir->items, next) { + if (_strmatch(node->name, name) && (node->type & type)) + return node; + } + + return NULL; +} + +int +dfs_find_node(const char *path, struct dfs_node **ret, node_type_t type) +{ + struct dfs_node *node, *dir; + char *my_path = NULL; + char *argv[DFS_MAX_PATHS + 1] = { NULL }; + int n, i; + + if (!path || (*path == '\0')) + return 0; + + if (path[0] == '/' && path[1] == '\0') { + node = get_root(); + goto _leave; + } + + /* Skip the leading '/' */ + my_path = strdup((path[0] == '/') ? &path[1] : path); + if (!my_path) + return 0; + + n = _strtok(my_path, "/", argv, DFS_MAX_PATHS); + + dir = get_root(); + + /* Follow the directory path if present */ + for (i = 0, node = NULL; i < n; i++) { + node = dfs_search_dir(dir, argv[i], type); + + if (!node) + break; + + /* follow the next directory */ + if (is_directory(node) && (i < n)) + dir = node; + else + break; + } + + free(my_path); + +_leave: + if (ret) + *ret = node; + + return (node) ? 1 : 0; +} + +struct dfs_node * +dfs_find_inode(fuse_ino_t ino) +{ + struct dfs_node *node; + int ret; + + DFS_LOG(DEBUG, "Find inode %ld\n", ino); + + ret = rte_hash_lookup_data(dfs_get_instance()->hash, + (const void *)&ino, (void **)&node); + if (ret >= 0) + goto found; + + DFS_LOG(DEBUG, "inode %ld NOT Found ret %d\n", ino, ret); + return NULL; +found: + DFS_LOG(DEBUG, "Found (%s, %ld)\n", node->name, node->ino); + return node; +} + +struct dfs_node * +dfs_last_dir_in_path(const char *path, struct dfs_node *cwd) +{ + struct dfs_node *node, *dir; + char *my_path = NULL; + char *argv[DFS_MAX_PATHS + 1]; + int n, i; + + if (!path || (*path == '\0')) + return NULL; + + if (path[0] == '/' && path[1] == '\0') + return get_root(); + + /* Skip the leading '/' */ + my_path = strdup((path[0] == '/') ? &path[1] : path); + if (!my_path) + return NULL; + + n = _strtok(my_path, "/", argv, DFS_MAX_PATHS); + + /* handle the special case of leading '/' */ + dir = (path[0] == '/') ? get_root() : cwd; + + /* Follow the directory path if present */ + for (i = 0, node = NULL; i < n; i++) { + node = dfs_search_dir(dir, argv[i], DFS_VALID_NODE); + + if (!node) + break; + + /* follow the next directory */ + if (is_directory(node) && (i < n)) + dir = node; + else + break; + } + + free(my_path); + + return dir; +} diff --git a/lib/librte_dfs/dfs_search.h b/lib/librte_dfs/dfs_search.h new file mode 100644 index 000000000..c11085be2 --- /dev/null +++ b/lib/librte_dfs/dfs_search.h @@ -0,0 +1,266 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2018 Intel Corporation. + */ + +#ifndef _DFS_SEARCH_H_ +#define _DFS_SEARCH_H_ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +/** + * @file + * RTE Command line interface + * + */ + +#ifdef __cplusplus +extern "C" { +#endif + +typedef union { + void *voidp; /**< void * value */ + char chr[8]; /**< 8 characters */ + uint64_t u64; /**< 64bit value */ + uint32_t u32[2];/**< 2 32bit value */ + uint16_t u16[4];/**< 4 16bit values */ +} arg_u; /**< Union for argument word */ + +typedef struct { + arg_u arg1; /**< Argument Word 1 */ + arg_u arg2; /**< Argument Word 2 */ + arg_u arg3; /**< Argument Word 3 */ + arg_u arg4; /**< Argument Word 4 */ +} args_t; /**< 32 bytes of arguments */ + +struct dfs; +struct dfs_node; +struct dfs_head; + +typedef int (*dfs_scan_t)(struct dfs_node *node, + uint32_t flags, args_t *args); +/** typedef of function passed in dfs_scan_directory() */ + +/** + * Helper routine to compare two strings exactly + * + * @param s1 + * Pointer to first string. + * @param s2 + * Pointer to second string. + * @return + * 0 failed to compare and 1 strings are equal. + */ +static inline int +_strmatch(const char *s1, const char *s2) +{ + if (!s1 || !s2) + return 0; + + while ((*s1 != '\0') && (*s2 != '\0')) + if (*s1++ != *s2++) + return 0; + if (*s1 != *s2) + return 0; + + return 1; +} + +static inline char * +_strtrim(char *str) +{ + if (!str || !*str) + return str; + + /* trim white space characters at the front */ + while (isspace(*str)) + str++; + + /* Make sure the string is not empty */ + if (*str) { + char *p = &str[strlen(str) - 1]; + + /* trim trailing white space characters */ + while ((p >= str) && isspace(*p)) + p--; + + p[1] = '\0'; + } + return str; +} + +static inline int +_strtok(char *str, const char *delim, char **entries, int maxtokens) +{ + int i = 0; + char *saved; + + if (!str || !delim || !entries || !maxtokens) + return -1; + + do { + entries[i] = _strtrim(strtok_r(str, delim, &saved)); + str = NULL; + } while (entries[i] && (++i < maxtokens)); + + return i; +} + +/** + * Scan a directory and call the func with the node found. + * + * @param files + * Type of nodes to find + * @param dir + * Node pointer to directory to scan + * @param func + * dfs_scan_t function pointer + * @param arg + * void * used by the function being called. + * @return + * 0 on success or -1 on error + */ +int __rte_experimental dfs_scan_directory(struct dfs_node *dir, + dfs_scan_t func, uint32_t flags, args_t *args); + +/** + * Find a node by path + * + * @param path + * Path to node + * @param ret + * Pointer to pointer of a dfs_node if found + * @param type + * Type of node to search for in the list. + * @return + * 1 if found else 0 + */ +int __rte_experimental dfs_find_node(const char *path, + struct dfs_node **ret, node_type_t type); + +/** + * Find a node by inode + * + * @param + * root - root node or a directory node to search. Can be NULL to start at + * root node. + * @param ino + * inode + * @return + * struct dfs_node * or NULL if not found + */ +struct dfs_node *__rte_experimental dfs_find_inode(fuse_ino_t ino); + +/** + * Search the local and bin directories for a command + * + * @param path + * String for the command to use + * @return + * Pointer to the command node or NULL + */ +struct dfs_node *__rte_experimental dfs_find_cmd(const char *path); + +/** + * Count the number of type(s) of nodes available in a given node + * + * @param n + * node or NULL for current working directory + * @return + * Number of nodes found of this type in the directory + */ +uint32_t __rte_experimental dfs_dir_item_count(struct dfs_node *node, + uint32_t types); + +/** + * Count the number of commands in the execute path + * + * @return + * Number of nodes found of this type in the directory + */ +uint32_t __rte_experimental dfs_path_cmd_count(void); + +/** + * Return a list of nodes matching given information + * + * @param node + * Node to start search or use the path list. + * @param flags + * Type of nodes to return + * @param ret + * Pointer to an array of pointer for return value + * @return + * Number of nodes found of this type in the directory + */ +uint32_t __rte_experimental dfs_node_list_with_type(struct dfs_node *node, + uint32_t flags, void **ret); + +/** + * Free a node back to the free list + * + * @param node + * Pointer to the node to free + * @return + * N/A + */ +void __rte_experimental dfs_node_list_free(void *nodes); + +/** + * Scan a directory for a given string matching name + * + * @param dir + * Pointer to directory node to start with in scanning + * @param name + * String to match the nodes with + * @param type + * Type of nodes to include in scan. + * @return + * Number of nodes found of this type in the directory + */ +struct dfs_node *__rte_experimental dfs_search_dir(struct dfs_node *dir, + const char *name, uint32_t type); + +/** + * Scan the directory given by the path + * + * @param path + * The path string to use + * @param func + * The function to call when a match is found. + * @param flags + * Type of files to include in match + * @param args + * Arguments to include with function call. + * @return + * Number of nodes found of this type in the directory + */ +int __rte_experimental dfs_scan_path(const char *path, dfs_scan_t func, + uint32_t flags, args_t *args); + +/** + * Find and return the last directory node in a give path string + * + * @param path + * Path string to scan + * @param cwd + * Current working directory or NULL for root + * @return + * Pointer to last directory node in path + */ +struct dfs_node *__rte_experimental dfs_last_dir_in_path(const char *path, + struct dfs_node *cwd); + +#ifdef __cplusplus +} +#endif + +#endif /* _DFS_SEARCH_H_ */ diff --git a/lib/librte_dfs/dfs_timer.c b/lib/librte_dfs/dfs_timer.c new file mode 100644 index 000000000..9fc029f51 --- /dev/null +++ b/lib/librte_dfs/dfs_timer.c @@ -0,0 +1,58 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2018 Intel Corporation. + */ + +#include +#include +#include +#include + +#include +#include +#include + +#include "dfs.h" +#include "dfs_timer.h" + +static int +timer_info(struct dfs_node *node, oper_t opt) +{ + switch (opt) { + case DFS_FILE_INIT: + dfs_node_init(node, NULL, 0, 0); + break; + + case DFS_FILE_INFO: + if (dfs_tmpfile_create(node, DFS_TIMER_INFO_FILE, + rte_timer_dump_stats)) + return -1; + break; + + case DFS_FILE_RELEASE: + dfs_tmpfile_release(node); + break; + + default: + break; + } + + return 0; +} + +static struct dfs_tree timer_tree[] = { + u_dir("timer"), + u_file("dump", timer_info, DFS_FILE_RDONLY, 0), + u_end() +}; + +static int +_timer_init(void) +{ + DFS_LOG(DEBUG, "DFS Setup TIMER\n"); + return dfs_add_tree(NULL, timer_tree); +} + +RTE_INIT(dfs_timer_init) +{ + dfs_register("Timer", _timer_init); +} diff --git a/lib/librte_dfs/dfs_timer.h b/lib/librte_dfs/dfs_timer.h new file mode 100644 index 000000000..cb9fdf38b --- /dev/null +++ b/lib/librte_dfs/dfs_timer.h @@ -0,0 +1,25 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2018 Intel Corporation. + */ + +#ifndef _DFS_TIMER_H_ +#define _DFS_TIMER_H_ + +/** + * @file + * RTE DFS TIMER Operations + * + * These routines supply the TIMER usage support routines. + */ + +#ifdef __cplusplus +extern "C" { +#endif + +#define DFS_TIMER_INFO_FILE "timer-info.tmp" + +#ifdef __cplusplus +} +#endif + +#endif /* _DFS_TIMER_H_ */ diff --git a/lib/librte_dfs/meson.build b/lib/librte_dfs/meson.build new file mode 100644 index 000000000..021af3d21 --- /dev/null +++ b/lib/librte_dfs/meson.build @@ -0,0 +1,47 @@ +# SPDX-License-Identifier: BSD-3-Clause +# Copyright(c) 2018 Intel Corporation + +version = 1 +allow_experimental_apis = true +sources = files( + 'dfs.c', + 'dfs_cryptodev.c', + 'dfs_dpdk.c', + 'dfs_eal.c', + 'dfs_ethdev.c', + 'dfs_mempool.c', + 'dfs_oper.c', + 'dfs_rawdev.c', + 'dfs_ring.c', + 'dfs_search.c', + 'dfs_timer.c') +headers = files( + 'dfs_cryptodev.h', + 'dfs_dpdk.h', + 'dfs_eal.h', + 'dfs_ethdev.h', + 'dfs.h', + 'dfs_mempool.h', + 'dfs_oper.h', + 'dfs_rawdev.h', + 'dfs_ring.h', + 'dfs_search.h', + 'dfs_timer.h') +deps += ['eal', 'ethdev', 'mempool', 'timer', + 'pci', 'hash', 'cryptodev', 'rawdev'] + +fuse = cc.find_library('fuse3', required: false) +if fuse.found() + ext_deps += fuse + dpdk_app_link_libraries += ['dfs'] +else + build = false +endif + +jansson = cc.find_library('jansson', required: false) +if jansson.found() + ext_deps += jansson + dpdk_app_link_libraries += ['dfs'] +else + build = false +endif diff --git a/lib/librte_dfs/rte_dfs_version.map b/lib/librte_dfs/rte_dfs_version.map new file mode 100644 index 000000000..3f963c830 --- /dev/null +++ b/lib/librte_dfs/rte_dfs_version.map @@ -0,0 +1,29 @@ +DPDK_18.11 { + global: *; + + local: *; +}; + +EXPERIMENTAL { + global: + + dfs_add_dir; + dfs_add_alias; + dfs_add_file; + dfs_remove_node; + dfs_add_tree; + dfs_dump; + dfs_tmpfile_create; + dfs_tmpfile_release; + dfs_execute_cmd; + dfs_ops_create; + dfs_ops_destroy; + dfs_scan_directory; + dfs_find_node; + dfs_find_inode; + dfs_dir_item_count; + dfs_search_dir; + dfs_scan_path; + dfs_last_dir_in_path; + dfs_dpdk_init; +} DPDK_18.11; diff --git a/lib/meson.build b/lib/meson.build index bb7f443f9..9a64f969e 100644 --- a/lib/meson.build +++ b/lib/meson.build @@ -25,7 +25,7 @@ libraries = [ 'compat', # just a header, used for versioning # add pkt framework libs which use other libs from above 'port', 'table', 'pipeline', # flow_classify lib depends on pkt framework table lib - 'flow_classify', 'bpf', 'telemetry'] + 'flow_classify', 'bpf', 'telemetry', 'dfs'] default_cflags = machine_args if cc.has_argument('-Wno-format-truncation') diff --git a/mk/rte.app.mk b/mk/rte.app.mk index 5699d979d..0fb759f79 100644 --- a/mk/rte.app.mk +++ b/mk/rte.app.mk @@ -94,6 +94,7 @@ _LDLIBS-$(CONFIG_RTE_LIBRTE_EAL) += -lrte_eal _LDLIBS-$(CONFIG_RTE_LIBRTE_CMDLINE) += -lrte_cmdline _LDLIBS-$(CONFIG_RTE_LIBRTE_REORDER) += -lrte_reorder _LDLIBS-$(CONFIG_RTE_LIBRTE_SCHED) += -lrte_sched +_LDLIBS-y += -lrte_dfs ifeq ($(CONFIG_RTE_EXEC_ENV_LINUXAPP),y) _LDLIBS-$(CONFIG_RTE_LIBRTE_KNI) += -lrte_kni @@ -310,6 +311,8 @@ _LDLIBS-$(CONFIG_RTE_LIBRTE_SCHED) += -lm _LDLIBS-$(CONFIG_RTE_LIBRTE_SCHED) += -lrt _LDLIBS-$(CONFIG_RTE_LIBRTE_MEMBER) += -lm _LDLIBS-$(CONFIG_RTE_LIBRTE_METER) += -lm +_LDLIBS-$(CONFIG_RTE_LIBRTE_DFS) += $(shell pkg-config --libs-only-l fuse3) +_LDLIBS-$(CONFIG_RTE_LIBRTE_DFS) += $(shell pkg-config --libs-only-l jansson) ifeq ($(CONFIG_RTE_LIBRTE_VHOST_NUMA),y) _LDLIBS-$(CONFIG_RTE_LIBRTE_VHOST) += -lnuma endif