1.. SPDX-License-Identifier: BSD-3-Clause 2 Copyright(c) 2019 Intel Corporation. 3 4.. include:: <isonum.txt> 5 6IOAT Rawdev Driver 7=================== 8 9The ``ioat`` rawdev driver provides a poll-mode driver (PMD) for Intel\ |reg| 10Data Streaming Accelerator `(Intel DSA) 11<https://01.org/blogs/2019/introducing-intel-data-streaming-accelerator>`_ and for Intel\ |reg| 12QuickData Technology, part of Intel\ |reg| I/O Acceleration Technology 13`(Intel I/OAT) 14<https://www.intel.com/content/www/us/en/wireless-network/accel-technology.html>`_. 15This PMD, when used on supported hardware, allows data copies, for example, 16cloning packet data, to be accelerated by that hardware rather than having to 17be done by software, freeing up CPU cycles for other tasks. 18 19Hardware Requirements 20---------------------- 21 22The ``dpdk-devbind.py`` script, included with DPDK, 23can be used to show the presence of supported hardware. 24Running ``dpdk-devbind.py --status-dev misc`` will show all the miscellaneous, 25or rawdev-based devices on the system. 26For Intel\ |reg| QuickData Technology devices, the hardware will be often listed as "Crystal Beach DMA", 27or "CBDMA". 28For Intel\ |reg| DSA devices, they are currently (at time of writing) appearing as devices with type "0b25", 29due to the absence of pci-id database entries for them at this point. 30 31Compilation 32------------ 33 34For builds using ``meson`` and ``ninja``, the driver will be built when the target platform is x86-based. 35No additional compilation steps are necessary. 36 37Device Setup 38------------- 39 40Depending on support provided by the PMD, HW devices can either use the kernel configured driver 41or be bound to a user-space IO driver for use. 42For example, Intel\ |reg| DSA devices can use the IDXD kernel driver or DPDK-supported drivers, 43such as ``vfio-pci``. 44 45Intel\ |reg| DSA devices using idxd kernel driver 46~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 47 48To use a Intel\ |reg| DSA device bound to the IDXD kernel driver, the device must first be configured. 49The `accel-config <https://github.com/intel/idxd-config>`_ utility library can be used for configuration. 50 51.. note:: 52 The device configuration can also be done by directly interacting with the sysfs nodes. 53 An example of how this may be done can be seen in the script ``dpdk_idxd_cfg.py`` 54 included in the driver source directory. 55 56There are some mandatory configuration steps before being able to use a device with an application. 57The internal engines, which do the copies or other operations, 58and the work-queues, which are used by applications to assign work to the device, 59need to be assigned to groups, and the various other configuration options, 60such as priority or queue depth, need to be set for each queue. 61 62To assign an engine to a group:: 63 64 $ accel-config config-engine dsa0/engine0.0 --group-id=0 65 $ accel-config config-engine dsa0/engine0.1 --group-id=1 66 67To assign work queues to groups for passing descriptors to the engines a similar accel-config command can be used. 68However, the work queues also need to be configured depending on the use-case. 69Some configuration options include: 70 71* mode (Dedicated/Shared): Indicates whether a WQ may accept jobs from multiple queues simultaneously. 72* priority: WQ priority between 1 and 15. Larger value means higher priority. 73* wq-size: the size of the WQ. Sum of all WQ sizes must be less that the total-size defined by the device. 74* type: WQ type (kernel/mdev/user). Determines how the device is presented. 75* name: identifier given to the WQ. 76 77Example configuration for a work queue:: 78 79 $ accel-config config-wq dsa0/wq0.0 --group-id=0 \ 80 --mode=dedicated --priority=10 --wq-size=8 \ 81 --type=user --name=dpdk_app1 82 83Once the devices have been configured, they need to be enabled:: 84 85 $ accel-config enable-device dsa0 86 $ accel-config enable-wq dsa0/wq0.0 87 88Check the device configuration:: 89 90 $ accel-config list 91 92Devices using VFIO/UIO drivers 93~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 94 95The HW devices to be used will need to be bound to a user-space IO driver for use. 96The ``dpdk-devbind.py`` script can be used to view the state of the devices 97and to bind them to a suitable DPDK-supported driver, such as ``vfio-pci``. 98For example:: 99 100 $ dpdk-devbind.py -b vfio-pci 00:04.0 00:04.1 101 102Device Probing and Initialization 103~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 104 105For devices bound to a suitable DPDK-supported VFIO/UIO driver, the HW devices will 106be found as part of the device scan done at application initialization time without 107the need to pass parameters to the application. 108 109For Intel\ |reg| DSA devices, DPDK will automatically configure the device with the 110maximum number of workqueues available on it, partitioning all resources equally 111among the queues. 112If fewer workqueues are required, then the ``max_queues`` parameter may be passed to 113the device driver on the EAL commandline, via the ``allowlist`` or ``-a`` flag e.g.:: 114 115 $ dpdk-test -a <b:d:f>,max_queues=4 116 117For devices bound to the IDXD kernel driver, 118the DPDK ioat driver will automatically perform a scan for available workqueues to use. 119Any workqueues found listed in ``/dev/dsa`` on the system will be checked in ``/sys``, 120and any which have ``dpdk_`` prefix in their name will be automatically probed by the 121driver to make them available to the application. 122Alternatively, to support use by multiple DPDK processes simultaneously, 123the value used as the DPDK ``--file-prefix`` parameter may be used as a workqueue name prefix, 124instead of ``dpdk_``, 125allowing each DPDK application instance to only use a subset of configured queues. 126 127Once probed successfully, irrespective of kernel driver, the device will appear as a ``rawdev``, 128that is a "raw device type" inside DPDK, and can be accessed using APIs from the 129``rte_rawdev`` library. 130 131Using IOAT Rawdev Devices 132-------------------------- 133 134To use the devices from an application, the rawdev API can be used, along 135with definitions taken from the device-specific header file 136``rte_ioat_rawdev.h``. This header is needed to get the definition of 137structure parameters used by some of the rawdev APIs for IOAT rawdev 138devices, as well as providing key functions for using the device for memory 139copies. 140 141Getting Device Information 142~~~~~~~~~~~~~~~~~~~~~~~~~~~ 143 144Basic information about each rawdev device can be queried using the 145``rte_rawdev_info_get()`` API. For most applications, this API will be 146needed to verify that the rawdev in question is of the expected type. For 147example, the following code snippet can be used to identify an IOAT 148rawdev device for use by an application: 149 150.. code-block:: C 151 152 for (i = 0; i < count && !found; i++) { 153 struct rte_rawdev_info info = { .dev_private = NULL }; 154 found = (rte_rawdev_info_get(i, &info, 0) == 0 && 155 strcmp(info.driver_name, 156 IOAT_PMD_RAWDEV_NAME_STR) == 0); 157 } 158 159When calling the ``rte_rawdev_info_get()`` API for an IOAT rawdev device, 160the ``dev_private`` field in the ``rte_rawdev_info`` struct should either 161be NULL, or else be set to point to a structure of type 162``rte_ioat_rawdev_config``, in which case the size of the configured device 163input ring will be returned in that structure. 164 165Device Configuration 166~~~~~~~~~~~~~~~~~~~~~ 167 168Configuring an IOAT rawdev device is done using the 169``rte_rawdev_configure()`` API, which takes the same structure parameters 170as the, previously referenced, ``rte_rawdev_info_get()`` API. The main 171difference is that, because the parameter is used as input rather than 172output, the ``dev_private`` structure element cannot be NULL, and must 173point to a valid ``rte_ioat_rawdev_config`` structure, containing the ring 174size to be used by the device. The ring size must be a power of two, 175between 64 and 4096. 176If it is not needed, the tracking by the driver of user-provided completion 177handles may be disabled by setting the ``hdls_disable`` flag in 178the configuration structure also. 179 180The following code shows how the device is configured in 181``test_ioat_rawdev.c``: 182 183.. code-block:: C 184 185 #define IOAT_TEST_RINGSIZE 512 186 struct rte_ioat_rawdev_config p = { .ring_size = -1 }; 187 struct rte_rawdev_info info = { .dev_private = &p }; 188 189 /* ... */ 190 191 p.ring_size = IOAT_TEST_RINGSIZE; 192 if (rte_rawdev_configure(dev_id, &info, sizeof(p)) != 0) { 193 printf("Error with rte_rawdev_configure()\n"); 194 return -1; 195 } 196 197Once configured, the device can then be made ready for use by calling the 198``rte_rawdev_start()`` API. 199 200Performing Data Copies 201~~~~~~~~~~~~~~~~~~~~~~~ 202 203To perform data copies using IOAT rawdev devices, the functions 204``rte_ioat_enqueue_copy()`` and ``rte_ioat_perform_ops()`` should be used. 205Once copies have been completed, the completion will be reported back when 206the application calls ``rte_ioat_completed_ops()``. 207 208The ``rte_ioat_enqueue_copy()`` function enqueues a single copy to the 209device ring for copying at a later point. The parameters to that function 210include the IOVA addresses of both the source and destination buffers, 211as well as two "handles" to be returned to the user when the copy is 212completed. These handles can be arbitrary values, but two are provided so 213that the library can track handles for both source and destination on 214behalf of the user, e.g. virtual addresses for the buffers, or mbuf 215pointers if packet data is being copied. 216 217While the ``rte_ioat_enqueue_copy()`` function enqueues a copy operation on 218the device ring, the copy will not actually be performed until after the 219application calls the ``rte_ioat_perform_ops()`` function. This function 220informs the device hardware of the elements enqueued on the ring, and the 221device will begin to process them. It is expected that, for efficiency 222reasons, a burst of operations will be enqueued to the device via multiple 223enqueue calls between calls to the ``rte_ioat_perform_ops()`` function. 224 225The following code from ``test_ioat_rawdev.c`` demonstrates how to enqueue 226a burst of copies to the device and start the hardware processing of them: 227 228.. code-block:: C 229 230 struct rte_mbuf *srcs[32], *dsts[32]; 231 unsigned int j; 232 233 for (i = 0; i < RTE_DIM(srcs); i++) { 234 char *src_data; 235 236 srcs[i] = rte_pktmbuf_alloc(pool); 237 dsts[i] = rte_pktmbuf_alloc(pool); 238 srcs[i]->data_len = srcs[i]->pkt_len = length; 239 dsts[i]->data_len = dsts[i]->pkt_len = length; 240 src_data = rte_pktmbuf_mtod(srcs[i], char *); 241 242 for (j = 0; j < length; j++) 243 src_data[j] = rand() & 0xFF; 244 245 if (rte_ioat_enqueue_copy(dev_id, 246 srcs[i]->buf_iova + srcs[i]->data_off, 247 dsts[i]->buf_iova + dsts[i]->data_off, 248 length, 249 (uintptr_t)srcs[i], 250 (uintptr_t)dsts[i]) != 1) { 251 printf("Error with rte_ioat_enqueue_copy for buffer %u\n", 252 i); 253 return -1; 254 } 255 } 256 rte_ioat_perform_ops(dev_id); 257 258To retrieve information about completed copies, the API 259``rte_ioat_completed_ops()`` should be used. This API will return to the 260application a set of completion handles passed in when the relevant copies 261were enqueued. 262 263The following code from ``test_ioat_rawdev.c`` shows the test code 264retrieving information about the completed copies and validating the data 265is correct before freeing the data buffers using the returned handles: 266 267.. code-block:: C 268 269 if (rte_ioat_completed_ops(dev_id, 64, (void *)completed_src, 270 (void *)completed_dst) != RTE_DIM(srcs)) { 271 printf("Error with rte_ioat_completed_ops\n"); 272 return -1; 273 } 274 for (i = 0; i < RTE_DIM(srcs); i++) { 275 char *src_data, *dst_data; 276 277 if (completed_src[i] != srcs[i]) { 278 printf("Error with source pointer %u\n", i); 279 return -1; 280 } 281 if (completed_dst[i] != dsts[i]) { 282 printf("Error with dest pointer %u\n", i); 283 return -1; 284 } 285 286 src_data = rte_pktmbuf_mtod(srcs[i], char *); 287 dst_data = rte_pktmbuf_mtod(dsts[i], char *); 288 for (j = 0; j < length; j++) 289 if (src_data[j] != dst_data[j]) { 290 printf("Error with copy of packet %u, byte %u\n", 291 i, j); 292 return -1; 293 } 294 rte_pktmbuf_free(srcs[i]); 295 rte_pktmbuf_free(dsts[i]); 296 } 297 298 299Filling an Area of Memory 300~~~~~~~~~~~~~~~~~~~~~~~~~~ 301 302The IOAT driver also has support for the ``fill`` operation, where an area 303of memory is overwritten, or filled, with a short pattern of data. 304Fill operations can be performed in much the same was as copy operations 305described above, just using the ``rte_ioat_enqueue_fill()`` function rather 306than the ``rte_ioat_enqueue_copy()`` function. 307 308 309Querying Device Statistics 310~~~~~~~~~~~~~~~~~~~~~~~~~~~ 311 312The statistics from the IOAT rawdev device can be got via the xstats 313functions in the ``rte_rawdev`` library, i.e. 314``rte_rawdev_xstats_names_get()``, ``rte_rawdev_xstats_get()`` and 315``rte_rawdev_xstats_by_name_get``. The statistics returned for each device 316instance are: 317 318* ``failed_enqueues`` 319* ``successful_enqueues`` 320* ``copies_started`` 321* ``copies_completed`` 322