Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
81 changes: 49 additions & 32 deletions docs/reference/kernel-abi/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -17,35 +17,43 @@ usage guide and a formal reference for each ioctl operation. Every ioctl entry f
structure: a top-level description, the C interface definition, the ioctl direction, preconditions
on inputs, postconditions on outputs, and return values.

The module uses the Linux ``miscdevice`` framework to create the following device files, which
allocates dynamic minor numbers under major 10. Userspace discovers device nodes by path, not by
major/minor number.
The module allocates one dynamic character-device major and creates a dedicated
``/sys/class/slash`` class. Device numbers within that major have a fixed layout:

``/dev/slash_ctl<N>`` / ``/sys/class/misc/slash_ctl_<BDF>/device``
- minor 0 is the global ``slash_hotplug`` device;
- board ``N`` uses minor ``2*N+1`` for ``slash_ctl<N>`` and minor ``2*N+2`` for
``slash_qdma_ctl<N>``;
- ``N`` is in the range 0–15, so one loaded module supports at most 16 cards.

PF1 and PF2 are matched by their board BDF (the ``DDDD:BB:SS`` portion) and share the same ``N``.
The board-to-``N`` assignment is retained while the module is loaded, including across PCI
remove/rescan cycles. Consequently the class entry name, ``/dev`` path, and device number
(``dev_t``) return unchanged after a remove/rescan. The major itself is dynamically allocated and
may change when the module is unloaded and loaded again.

``/dev/slash_ctl<N>`` / ``/sys/class/slash/slash_ctl_<BDF>/device``
Provides BAR enumeration, MMIO access, and PCI device identity. Associated with PF2 (device ID
``10EE:50B6``). Examples: ``/dev/slash_ctl0``, ``/dev/slash_ctl1``,
``/sys/class/misc/slash_ctl_0000:61:00.2/device``.
``/sys/class/slash/slash_ctl_0000:61:00.2/device``.

``/dev/slash_qdma_ctl<N>`` / ``/sys/class/misc/slash_qdma_ctl_<BDF>/device``
``/dev/slash_qdma_ctl<N>`` / ``/sys/class/slash/slash_qdma_ctl_<BDF>/device``
Manages DMA queue pairs for bulk data movement between host and card memory, as well as
reconfiguration. Associated with PF1 (device ID ``10EE:50B5``). Examples: ``/dev/slash_qdma_ctl0``,
``/dev/slash_qdma_ctl1``, ``/sys/class/misc/slash_qdma_ctl_0000:61:00.0/device``.
``/dev/slash_qdma_ctl1``, ``/sys/class/slash/slash_qdma_ctl_0000:61:00.1/device``.

``/dev/slash_hotplug``
``/dev/slash_hotplug`` / ``/sys/class/slash/slash_hotplug``
A single global instance created at module load. Provides privileged control over the PCIe
lifecycle of SLASH cards (remove, rescan, secondary bus reset).

The kernel module creates one ``slash_ctl`` and ``slash_qdma_ctl`` file for each card during discovery,
which persist across reconfiguration, but will be removed and readded during a remove+rescan cycle.
The mapping of one file path to a physical card is therefore not guaranteed across remove+rescan cycles
and userspace should always verify the BDF identity of the accessed card. Also, suffixes for one card are
not guaranteed to be identical for ``/dev/slash_ctl<N>`` and ``/dev/slash_qdma_ctl<N>``. For example,
the device files ``/dev/slash_ctl0`` and ``/dev/slash_qdma_ctl1`` may reference the same physical card.
Class entry names use the full function-level BDF, while ``DEVNAME`` in each entry's ``uevent``
file names the numeric ``/dev`` node. The entry's ``dev`` attribute and the ``st_rdev`` returned
by ``stat(2)`` on that node contain the same major and minor. Class entries are sysfs device
objects, not symlinks to ``/dev``.

The files in the ``/sys/class/misc/`` directory are symlinks to the respective files in ``/dev``,
and the placeholder ``<BDF>`` equates to the full, function-level BDF identifier of the physical
function. For example, the physical function 2 of board ``0000:61:00`` may be available as
``/sys/class/misc/slash_ctl_0000:61:00.2``.
During removal the affected class entry and ``/dev`` node disappear. An fd opened before removal
continues to refer to the old device instance and never rebinds to the rescanned instance;
device-specific ioctls on that old fd return ``-ENODEV``. After rescan, new opens through the stable
path reach the new instance.

Data Conventions
================
Expand All @@ -58,7 +66,9 @@ Every ioctl argument struct carries a leading ``__u32 size`` field. Callers must
copies ``min(user_size, kernel_size)`` bytes in. Fields the kernel knows about but the caller's
older struct does not include are zero-filled. The response is written back for
``min(user_size, kernel_size)`` bytes; if ``user_size > kernel_size``, the kernel zero-fills the
extra tail via ``clear_user()``. This allows the driver and library to evolve independently.
extra tail via ``clear_user()``. This supports append-only struct extension within a coordinated
release; it is not a promise that arbitrary kernel-module and userspace-library releases can be
mixed. ``slash.ko``, libslash, vrtd/VRT, and v80-smi must come from the same SLASH release.

Error Handling
--------------
Expand Down Expand Up @@ -100,11 +110,11 @@ BDF string and vendor/device IDs to correlate the control device with a physical
matching QDMA control device.

- **Device file name:** ``/dev/slash_ctl<N>`` (e.g. ``/dev/slash_ctl0``)
- **Sysfs name:** ``slash_ctl_<PCI-BDF>`` (e.g., ``/sys/class/misc/slash_ctl_slash_ctl_0000:61:00.2``)
- **Sysfs name:** ``slash_ctl_<PCI-BDF>`` (e.g., ``/sys/class/slash/slash_ctl_0000:61:00.2``)
- **Associated PCI function:** PF2, device ID ``10EE:50B6``
- **Permissions:** ``0600`` (owner read/write)
- **Creation:** one per card, created when PF2 is probed during module load or PCI rescan
- **File operations:** ``ioctl`` only — no ``open`` hook, no ``read``, ``write``, or ``mmap``
- **File operations:** ``open``, ``release``, and ``ioctl`` no ``read``, ``write``, or ``mmap``
on this fd itself. MMIO access is through a dma-buf fd returned by an ioctl.

Usage
Expand Down Expand Up @@ -340,14 +350,15 @@ the transfer channel: host buffers are registered once, and transfer ioctls name
buffer, buffer offset, device-side physical address, length, and direction.

- **Device file name:** ``/dev/slash_qdma_ctl<N>`` (e.g. ``/dev/slash_qdma_ctl0``)
- **Sysfs name:** ``slash_qdma_ctl_<PCI-BDF>`` (e.g. ``/sys/class/misc/slash_qdma_ctl_0000:61:00.1``)
- **Sysfs name:** ``slash_qdma_ctl_<PCI-BDF>`` (e.g. ``/sys/class/slash/slash_qdma_ctl_0000:61:00.1``)
- **Associated PCI function:** PF1, device ID ``10EE:50B5``
- **Permissions:** ``0600``
- **Creation:** one per card, created when PF1 is probed
- **File operations:** ``open``, ``release``, ``ioctl`` on the control fd. DMA I/O is done on
per-qpair anon-inode fds returned by an ioctl.

Same stable-``N`` mapping scheme as the control device, using a separate BDF-to-number map.
The QDMA and control functions use the same board-to-``N`` map, so
``slash_qdma_ctl<N>`` is always paired with ``slash_ctl<N>``.

Usage
-----
Expand Down Expand Up @@ -510,9 +521,9 @@ flag set) or the QDMA handle is not open.
``SLASH_QDMA_IOCTL_INFO``
~~~~~~~~~~~~~~~~~~~~~~~~~

Queries QDMA device capabilities. All output fields are currently zero; this ioctl is a placeholder
for future capability reporting. Callers should issue it during initialization but make no decisions
based on the returned values in the current implementation.
Queries the QDMA device's PCI identity and capabilities. ``bdf`` is always the full PF1 BDF and can
be matched with a control device by comparing the ``DDDD:BB:SS`` board portion. The capability
fields are reserved for future reporting and are currently zero.

**Interface:**

Expand All @@ -521,11 +532,12 @@ based on the returned values in the current implementation.
#define SLASH_QDMA_IOCTL_INFO _IOWR('v', 0x50, struct slash_qdma_info)

struct slash_qdma_info {
__u32 size; /* [in/out] ABI version */
__u32 qsets_max; /* [out] Max queue sets (currently always 0) */
__u32 msix_qvecs; /* [out] MSI-X vectors for queues (currently always 0) */
__u32 vf_max; /* [out] Max VFs (currently always 0) */
__u32 caps; /* [out] Capability bitmask (currently always 0) */
__u32 size; /* [in/out] ABI version */
char bdf[SLASH_PCI_BDF_LEN]; /* [out] Full PF1 BDF, e.g. "0000:61:00.1" */
__u32 qsets_max; /* [out] Max queue sets (currently 0) */
__u32 msix_qvecs; /* [out] Queue MSI-X vectors (currently 0) */
__u32 vf_max; /* [out] Max VFs (currently 0) */
__u32 caps; /* [out] Capability bitmask (currently 0) */
};

**Direction:** ``_IOWR`` — userspace writes ``size``; the kernel writes back all output fields.
Expand All @@ -538,7 +550,9 @@ based on the returned values in the current implementation.

**Postconditions:**

- All output fields are set to 0 in the current implementation.
- ``bdf`` is a NUL-terminated ``DDDD:BB:SS.1`` string with the full PCI domain.
- ``qsets_max``, ``msix_qvecs``, ``vf_max``, and ``caps`` are set to 0 in the current
implementation.
- The output is truncated to ``min(size, sizeof(struct))`` bytes. Fields whose tail lies beyond
the user-supplied ``size`` are not written; the corresponding bytes in the user buffer are left
untouched.
Expand Down Expand Up @@ -910,6 +924,8 @@ remove-and-rescan operation. These operations are used after loading a new FPGA
performing a full board reset.

- **Device file name:** ``/dev/slash_hotplug``
- **Sysfs name:** ``/sys/class/slash/slash_hotplug``
- **Device number:** the shared SLASH major, minor 0
- **Permissions:** ``0600``
- **Creation:** exactly one instance, created at module load, destroyed at module unload
- **File operations:** ``ioctl`` only (includes 32-bit compat path). No ``open``, ``release``,
Expand Down Expand Up @@ -1036,6 +1052,7 @@ callback. The corresponding ``/dev/slash_ctl<N>`` or ``/dev/slash_qdma_ctl<N>``
- Bus mastering is disabled on the device (``pci_clear_master()``).
- The device is removed from the PCI hierarchy (``pci_stop_and_remove_bus_device()``).
- The driver's ``.remove`` callback is invoked; associated device nodes disappear.
- A later rescan recreates the node with the same class name, ``/dev`` path, and ``dev_t``.

**Return values:**

Expand Down
12 changes: 11 additions & 1 deletion driver/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,8 @@ SLASH_HAVE_VM_FLAGS_SET ?= n
SLASH_HAVE_MODULE_IMPORT_NS_TOKEN ?= n
SLASH_HAVE_URING_CMD ?= n
SLASH_HAVE_URING_SQE_CMD ?= n
SLASH_HAVE_CLASS_CREATE_ONE_ARG ?= n
SLASH_HAVE_CLASS_DEVNODE_CONST ?= n

# Set GCOV=1 to instrument the module for kernel gcov coverage.
# Not set by default — never enable this in production builds.
Expand All @@ -72,7 +74,7 @@ GCOV_PROFILE := y
endif

obj-m := $(MODULE).o
$(MODULE)-objs := $(MODULE)_main.o $(MODULE)_ctldev.o $(MODULE)_pcie.o $(MODULE)_dmabuf.o $(MODULE)_hotplug.o $(MODULE)_qdma.o
$(MODULE)-objs := $(MODULE)_main.o $(MODULE)_chrdev.o $(MODULE)_ctldev.o $(MODULE)_pcie.o $(MODULE)_dmabuf.o $(MODULE)_hotplug.o $(MODULE)_qdma.o
ccflags-y += \
-I$(src)/libslash/include \
-I$(src)/libslash/include/slash/uapi \
Expand All @@ -96,6 +98,14 @@ ifeq ($(SLASH_HAVE_MODULE_IMPORT_NS_TOKEN),y)
ccflags-y += -DSLASH_HAVE_MODULE_IMPORT_NS_TOKEN
endif

ifeq ($(SLASH_HAVE_CLASS_CREATE_ONE_ARG),y)
ccflags-y += -DSLASH_HAVE_CLASS_CREATE_ONE_ARG
endif

ifeq ($(SLASH_HAVE_CLASS_DEVNODE_CONST),y)
ccflags-y += -DSLASH_HAVE_CLASS_DEVNODE_CONST
endif

# Optional io_uring uring_cmd async transfer path. Probed by kcompat; absent on
# kernels without CONFIG_IO_URING or uring_cmd support (e.g. RHEL 9, Ubuntu
# 22.04 GA), where the synchronous transfer ioctl remains the only path.
Expand Down
40 changes: 40 additions & 0 deletions driver/kcompat/class_create_one_arg.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/**
* Copyright (C) 2026 Advanced Micro Devices, Inc. All rights reserved.
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; version 2.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
* even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program; if
* not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/

/**
* Copyright (C) 2026 Advanced Micro Devices, Inc. All rights reserved.
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; version 2.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
* even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program; if
* not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/

#include <linux/device.h>
#include <linux/module.h>

static int __init conftest_init(void)
{
struct class *class = class_create("slash-conftest");

return PTR_ERR_OR_ZERO(class);
}

module_init(conftest_init);
MODULE_LICENSE("GPL");
48 changes: 48 additions & 0 deletions driver/kcompat/class_devnode_const.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
/**
* Copyright (C) 2026 Advanced Micro Devices, Inc. All rights reserved.
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; version 2.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
* even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program; if
* not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/

/**
* Copyright (C) 2026 Advanced Micro Devices, Inc. All rights reserved.
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; version 2.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
* even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program; if
* not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/

#include <linux/device.h>
#include <linux/module.h>

static char *conftest_devnode(const struct device *device, umode_t *mode)
{
return NULL;
}

static struct class conftest_class = {
.name = "slash-conftest",
.devnode = conftest_devnode,
};

static int __init conftest_init(void)
{
return conftest_class.name ? 0 : -EINVAL;
}

module_init(conftest_init);
MODULE_LICENSE("GPL");
2 changes: 1 addition & 1 deletion driver/libslash/include/slash/qdma.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
*
* Userspace API for slash QDMA (Queue-based DMA) devices.
*
* A QDMA device is a separate misc character device created for PF1,
* A QDMA device is a separate character device created for PF1,
* while the control device (ctldev) is created for PF2. Each PCI
* function gets at most one of each. Device nodes appear at
* /dev/slash_qdma_ctl0, /dev/slash_qdma_ctl1, etc.
Expand Down
1 change: 1 addition & 0 deletions driver/libslash/include/slash/uapi/slash_interface.h
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,7 @@ struct slash_qdma_info {
__u32 size; /**< Struct size for ABI versioning. */

/* Kernel to userspace */
char bdf[SLASH_PCI_BDF_LEN]; /**< [out] Full PF1 PCI BDF string, NUL-terminated. */
__u32 qsets_max; /**< [out] Maximum number of queue sets the hardware supports. */
__u32 msix_qvecs; /**< [out] Number of MSI-X vectors available for queues. */
__u32 vf_max; /**< [out] Maximum number of virtual functions. */
Expand Down
1 change: 1 addition & 0 deletions driver/libslash/src/qdma_mock.c
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,7 @@ int slash_qdma_mock_info_read(struct slash_qdma *qdma, struct slash_qdma_info *i

memset(info, 0, sizeof(*info));
info->size = sizeof(*info);
memcpy(info->bdf, "0000:00:00.1", sizeof("0000:00:00.1"));
info->qsets_max = QDMA_MOCK_MAX_QUEUES;
info->msix_qvecs = 1;

Expand Down
24 changes: 23 additions & 1 deletion driver/libslash/tests/qdma_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@

#include <gtest/gtest.h>

#include <cctype>
#include <cerrno>
#include <cstdlib>
#include <cstring>
Expand All @@ -32,6 +33,22 @@ extern "C" {
static constexpr const char *REAL_QDMA_PATH = "/dev/slash_qdma_ctl0";
static constexpr uint64_t DDR_BASE_ADDRESS = 0x60000000000ULL;

static bool isPf1Bdf(const char *bdf) {
if (bdf == nullptr || std::strlen(bdf) != 12 || bdf[4] != ':' || bdf[7] != ':' ||
bdf[10] != '.' || bdf[11] != '1') {
return false;
}
for (size_t i = 0; i < 12; ++i) {
if (i == 4 || i == 7 || i == 10) {
continue;
}
if (!std::isxdigit(static_cast<unsigned char>(bdf[i]))) {
return false;
}
}
return true;
}

// ─── Null / invalid argument tests (no hardware needed) ──────────────────────

TEST(QdmaNullTest, Open) {
Expand Down Expand Up @@ -167,7 +184,12 @@ TEST_P(ParametrizedQdmaTest, OpenSucceeds) {

TEST_P(ParametrizedQdmaTest, InfoRead) {
struct slash_qdma_info info{};
EXPECT_EQ(slash_qdma_info_read(qdma_, &info), 0);
ASSERT_EQ(slash_qdma_info_read(qdma_, &info), 0);
if (mock) {
EXPECT_STREQ(info.bdf, "0000:00:00.1");
} else {
EXPECT_TRUE(isPf1Bdf(info.bdf)) << "invalid PF1 BDF: " << info.bdf;
}
}

TEST_P(ParametrizedQdmaTest, QueueDmaTransfer) {
Expand Down
Loading
Loading