diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-21 11:54:28 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-21 11:54:28 +0000 |
commit | e6918187568dbd01842d8d1d2c808ce16a894239 (patch) | |
tree | 64f88b554b444a49f656b6c656111a145cbbaa28 /src/spdk/dpdk/kernel/freebsd | |
parent | Initial commit. (diff) | |
download | ceph-e6918187568dbd01842d8d1d2c808ce16a894239.tar.xz ceph-e6918187568dbd01842d8d1d2c808ce16a894239.zip |
Adding upstream version 18.2.2.upstream/18.2.2
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/spdk/dpdk/kernel/freebsd')
-rw-r--r-- | src/spdk/dpdk/kernel/freebsd/BSDmakefile.meson | 18 | ||||
-rw-r--r-- | src/spdk/dpdk/kernel/freebsd/Makefile | 9 | ||||
-rw-r--r-- | src/spdk/dpdk/kernel/freebsd/contigmem/BSDmakefile | 8 | ||||
-rw-r--r-- | src/spdk/dpdk/kernel/freebsd/contigmem/Makefile | 24 | ||||
-rw-r--r-- | src/spdk/dpdk/kernel/freebsd/contigmem/contigmem.c | 361 | ||||
-rw-r--r-- | src/spdk/dpdk/kernel/freebsd/contigmem/meson.build | 4 | ||||
-rw-r--r-- | src/spdk/dpdk/kernel/freebsd/meson.build | 35 | ||||
-rw-r--r-- | src/spdk/dpdk/kernel/freebsd/nic_uio/BSDmakefile | 8 | ||||
-rw-r--r-- | src/spdk/dpdk/kernel/freebsd/nic_uio/Makefile | 24 | ||||
-rw-r--r-- | src/spdk/dpdk/kernel/freebsd/nic_uio/meson.build | 4 | ||||
-rw-r--r-- | src/spdk/dpdk/kernel/freebsd/nic_uio/nic_uio.c | 351 |
11 files changed, 846 insertions, 0 deletions
diff --git a/src/spdk/dpdk/kernel/freebsd/BSDmakefile.meson b/src/spdk/dpdk/kernel/freebsd/BSDmakefile.meson new file mode 100644 index 000000000..6839ac01c --- /dev/null +++ b/src/spdk/dpdk/kernel/freebsd/BSDmakefile.meson @@ -0,0 +1,18 @@ +# SPDX-License-Identifier: BSD-3-Clause +# Copyright(c) 2017 Intel Corporation + +# makefile for building kernel modules using meson +# takes parameters from the environment + +# source file is passed via KMOD_SRC as relative path, we only use final +# (tail) component of it (:T), as VPATH is used to find actual file. The +# VPATH is similarly extracted from the non-final (head) portion of the +# path (:H) converted to absolute path (:tA). This use of VPATH is to have +# the .o files placed in the build, not source directory + +VPATH := ${KMOD_SRC:H:tA} +SRCS := ${KMOD_SRC:T} device_if.h bus_if.h pci_if.h +CFLAGS += $(KMOD_CFLAGS) +.OBJDIR: ${KMOD_OBJDIR} + +.include <bsd.kmod.mk> diff --git a/src/spdk/dpdk/kernel/freebsd/Makefile b/src/spdk/dpdk/kernel/freebsd/Makefile new file mode 100644 index 000000000..522d3f68f --- /dev/null +++ b/src/spdk/dpdk/kernel/freebsd/Makefile @@ -0,0 +1,9 @@ +# SPDX-License-Identifier: BSD-3-Clause +# Copyright 2017 NXP + +include $(RTE_SDK)/mk/rte.vars.mk + +DIRS-$(CONFIG_RTE_EXEC_ENV_FREEBSD) += contigmem +DIRS-$(CONFIG_RTE_EXEC_ENV_FREEBSD) += nic_uio + +include $(RTE_SDK)/mk/rte.subdir.mk diff --git a/src/spdk/dpdk/kernel/freebsd/contigmem/BSDmakefile b/src/spdk/dpdk/kernel/freebsd/contigmem/BSDmakefile new file mode 100644 index 000000000..33ce83ee2 --- /dev/null +++ b/src/spdk/dpdk/kernel/freebsd/contigmem/BSDmakefile @@ -0,0 +1,8 @@ +# SPDX-License-Identifier: BSD-3-Clause +# Copyright(c) 2010-2014 Intel Corporation +# + +KMOD= contigmem +SRCS= contigmem.c device_if.h bus_if.h + +.include <bsd.kmod.mk> diff --git a/src/spdk/dpdk/kernel/freebsd/contigmem/Makefile b/src/spdk/dpdk/kernel/freebsd/contigmem/Makefile new file mode 100644 index 000000000..428a7edee --- /dev/null +++ b/src/spdk/dpdk/kernel/freebsd/contigmem/Makefile @@ -0,0 +1,24 @@ +# SPDX-License-Identifier: BSD-3-Clause +# Copyright(c) 2010-2014 Intel Corporation + +include $(RTE_SDK)/mk/rte.vars.mk + +# +# module name and path +# +MODULE = contigmem + +# +# CFLAGS +# +MODULE_CFLAGS += -I$(SRCDIR) +MODULE_CFLAGS += -I$(RTE_OUTPUT)/include +MODULE_CFLAGS += -Winline -Wall -Werror +MODULE_CFLAGS += -include $(RTE_OUTPUT)/include/rte_config.h + +# +# all source are stored in SRCS-y +# +SRCS-y := contigmem.c + +include $(RTE_SDK)/mk/rte.bsdmodule.mk diff --git a/src/spdk/dpdk/kernel/freebsd/contigmem/contigmem.c b/src/spdk/dpdk/kernel/freebsd/contigmem/contigmem.c new file mode 100644 index 000000000..bd72f4d62 --- /dev/null +++ b/src/spdk/dpdk/kernel/freebsd/contigmem/contigmem.c @@ -0,0 +1,361 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2010-2014 Intel Corporation + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/param.h> +#include <sys/bio.h> +#include <sys/bus.h> +#include <sys/conf.h> +#include <sys/kernel.h> +#include <sys/malloc.h> +#include <sys/module.h> +#include <sys/proc.h> +#include <sys/lock.h> +#include <sys/rwlock.h> +#include <sys/mutex.h> +#include <sys/systm.h> +#include <sys/sysctl.h> +#include <sys/vmmeter.h> +#include <sys/eventhandler.h> + +#include <machine/bus.h> + +#include <vm/vm.h> +#include <vm/pmap.h> +#include <vm/vm_param.h> +#include <vm/vm_object.h> +#include <vm/vm_page.h> +#include <vm/vm_pager.h> +#include <vm/vm_phys.h> + +struct contigmem_buffer { + void *addr; + int refcnt; + struct mtx mtx; +}; + +struct contigmem_vm_handle { + int buffer_index; +}; + +static int contigmem_load(void); +static int contigmem_unload(void); +static int contigmem_physaddr(SYSCTL_HANDLER_ARGS); + +static d_mmap_single_t contigmem_mmap_single; +static d_open_t contigmem_open; +static d_close_t contigmem_close; + +static int contigmem_num_buffers = RTE_CONTIGMEM_DEFAULT_NUM_BUFS; +static int64_t contigmem_buffer_size = RTE_CONTIGMEM_DEFAULT_BUF_SIZE; + +static eventhandler_tag contigmem_eh_tag; +static struct contigmem_buffer contigmem_buffers[RTE_CONTIGMEM_MAX_NUM_BUFS]; +static struct cdev *contigmem_cdev = NULL; +static int contigmem_refcnt; + +TUNABLE_INT("hw.contigmem.num_buffers", &contigmem_num_buffers); +TUNABLE_QUAD("hw.contigmem.buffer_size", &contigmem_buffer_size); + +static SYSCTL_NODE(_hw, OID_AUTO, contigmem, CTLFLAG_RD, 0, "contigmem"); + +SYSCTL_INT(_hw_contigmem, OID_AUTO, num_buffers, CTLFLAG_RD, + &contigmem_num_buffers, 0, "Number of contigmem buffers allocated"); +SYSCTL_QUAD(_hw_contigmem, OID_AUTO, buffer_size, CTLFLAG_RD, + &contigmem_buffer_size, 0, "Size of each contiguous buffer"); +SYSCTL_INT(_hw_contigmem, OID_AUTO, num_references, CTLFLAG_RD, + &contigmem_refcnt, 0, "Number of references to contigmem"); + +static SYSCTL_NODE(_hw_contigmem, OID_AUTO, physaddr, CTLFLAG_RD, 0, + "physaddr"); + +MALLOC_DEFINE(M_CONTIGMEM, "contigmem", "contigmem(4) allocations"); + +static int contigmem_modevent(module_t mod, int type, void *arg) +{ + int error = 0; + + switch (type) { + case MOD_LOAD: + error = contigmem_load(); + break; + case MOD_UNLOAD: + error = contigmem_unload(); + break; + default: + break; + } + + return error; +} + +moduledata_t contigmem_mod = { + "contigmem", + (modeventhand_t)contigmem_modevent, + 0 +}; + +DECLARE_MODULE(contigmem, contigmem_mod, SI_SUB_DRIVERS, SI_ORDER_ANY); +MODULE_VERSION(contigmem, 1); + +static struct cdevsw contigmem_ops = { + .d_name = "contigmem", + .d_version = D_VERSION, + .d_flags = D_TRACKCLOSE, + .d_mmap_single = contigmem_mmap_single, + .d_open = contigmem_open, + .d_close = contigmem_close, +}; + +static int +contigmem_load() +{ + char index_string[8], description[32]; + int i, error = 0; + void *addr; + + if (contigmem_num_buffers > RTE_CONTIGMEM_MAX_NUM_BUFS) { + printf("%d buffers requested is greater than %d allowed\n", + contigmem_num_buffers, RTE_CONTIGMEM_MAX_NUM_BUFS); + error = EINVAL; + goto error; + } + + if (contigmem_buffer_size < PAGE_SIZE || + (contigmem_buffer_size & (contigmem_buffer_size - 1)) != 0) { + printf("buffer size 0x%lx is not greater than PAGE_SIZE and " + "power of two\n", contigmem_buffer_size); + error = EINVAL; + goto error; + } + + for (i = 0; i < contigmem_num_buffers; i++) { + addr = contigmalloc(contigmem_buffer_size, M_CONTIGMEM, M_ZERO, + 0, BUS_SPACE_MAXADDR, contigmem_buffer_size, 0); + if (addr == NULL) { + printf("contigmalloc failed for buffer %d\n", i); + error = ENOMEM; + goto error; + } + + printf("%2u: virt=%p phys=%p\n", i, addr, + (void *)pmap_kextract((vm_offset_t)addr)); + + mtx_init(&contigmem_buffers[i].mtx, "contigmem", NULL, MTX_DEF); + contigmem_buffers[i].addr = addr; + contigmem_buffers[i].refcnt = 0; + + snprintf(index_string, sizeof(index_string), "%d", i); + snprintf(description, sizeof(description), + "phys addr for buffer %d", i); + SYSCTL_ADD_PROC(NULL, + &SYSCTL_NODE_CHILDREN(_hw_contigmem, physaddr), OID_AUTO, + index_string, CTLTYPE_U64 | CTLFLAG_RD, + (void *)(uintptr_t)i, 0, contigmem_physaddr, "LU", + description); + } + + contigmem_cdev = make_dev_credf(0, &contigmem_ops, 0, NULL, UID_ROOT, + GID_WHEEL, 0600, "contigmem"); + + return 0; + +error: + for (i = 0; i < contigmem_num_buffers; i++) { + if (contigmem_buffers[i].addr != NULL) { + contigfree(contigmem_buffers[i].addr, + contigmem_buffer_size, M_CONTIGMEM); + contigmem_buffers[i].addr = NULL; + } + if (mtx_initialized(&contigmem_buffers[i].mtx)) + mtx_destroy(&contigmem_buffers[i].mtx); + } + + return error; +} + +static int +contigmem_unload() +{ + int i; + + if (contigmem_refcnt > 0) + return EBUSY; + + if (contigmem_cdev != NULL) + destroy_dev(contigmem_cdev); + + if (contigmem_eh_tag != NULL) + EVENTHANDLER_DEREGISTER(process_exit, contigmem_eh_tag); + + for (i = 0; i < RTE_CONTIGMEM_MAX_NUM_BUFS; i++) { + if (contigmem_buffers[i].addr != NULL) + contigfree(contigmem_buffers[i].addr, + contigmem_buffer_size, M_CONTIGMEM); + if (mtx_initialized(&contigmem_buffers[i].mtx)) + mtx_destroy(&contigmem_buffers[i].mtx); + } + + return 0; +} + +static int +contigmem_physaddr(SYSCTL_HANDLER_ARGS) +{ + uint64_t physaddr; + int index = (int)(uintptr_t)arg1; + + physaddr = (uint64_t)vtophys(contigmem_buffers[index].addr); + return sysctl_handle_64(oidp, &physaddr, 0, req); +} + +static int +contigmem_open(struct cdev *cdev, int fflags, int devtype, + struct thread *td) +{ + + atomic_add_int(&contigmem_refcnt, 1); + + return 0; +} + +static int +contigmem_close(struct cdev *cdev, int fflags, int devtype, + struct thread *td) +{ + + atomic_subtract_int(&contigmem_refcnt, 1); + + return 0; +} + +static int +contigmem_cdev_pager_ctor(void *handle, vm_ooffset_t size, vm_prot_t prot, + vm_ooffset_t foff, struct ucred *cred, u_short *color) +{ + struct contigmem_vm_handle *vmh = handle; + struct contigmem_buffer *buf; + + buf = &contigmem_buffers[vmh->buffer_index]; + + atomic_add_int(&contigmem_refcnt, 1); + + mtx_lock(&buf->mtx); + if (buf->refcnt == 0) + memset(buf->addr, 0, contigmem_buffer_size); + buf->refcnt++; + mtx_unlock(&buf->mtx); + + return 0; +} + +static void +contigmem_cdev_pager_dtor(void *handle) +{ + struct contigmem_vm_handle *vmh = handle; + struct contigmem_buffer *buf; + + buf = &contigmem_buffers[vmh->buffer_index]; + + mtx_lock(&buf->mtx); + buf->refcnt--; + mtx_unlock(&buf->mtx); + + free(vmh, M_CONTIGMEM); + + atomic_subtract_int(&contigmem_refcnt, 1); +} + +static int +contigmem_cdev_pager_fault(vm_object_t object, vm_ooffset_t offset, int prot, + vm_page_t *mres) +{ + vm_paddr_t paddr; + vm_page_t m_paddr, page; + vm_memattr_t memattr, memattr1; + + memattr = object->memattr; + + VM_OBJECT_WUNLOCK(object); + + paddr = offset; + + m_paddr = vm_phys_paddr_to_vm_page(paddr); + if (m_paddr != NULL) { + memattr1 = pmap_page_get_memattr(m_paddr); + if (memattr1 != memattr) + memattr = memattr1; + } + + if (((*mres)->flags & PG_FICTITIOUS) != 0) { + /* + * If the passed in result page is a fake page, update it with + * the new physical address. + */ + page = *mres; + VM_OBJECT_WLOCK(object); + vm_page_updatefake(page, paddr, memattr); + } else { + /* + * Replace the passed in reqpage page with our own fake page and + * free up the original page. + */ + page = vm_page_getfake(paddr, memattr); + VM_OBJECT_WLOCK(object); +#if __FreeBSD__ >= 13 + vm_page_replace(page, object, (*mres)->pindex, *mres); +#else + vm_page_t mret = vm_page_replace(page, object, (*mres)->pindex); + KASSERT(mret == *mres, + ("invalid page replacement, old=%p, ret=%p", *mres, mret)); + vm_page_lock(mret); + vm_page_free(mret); + vm_page_unlock(mret); +#endif + *mres = page; + } + + page->valid = VM_PAGE_BITS_ALL; + + return VM_PAGER_OK; +} + +static struct cdev_pager_ops contigmem_cdev_pager_ops = { + .cdev_pg_ctor = contigmem_cdev_pager_ctor, + .cdev_pg_dtor = contigmem_cdev_pager_dtor, + .cdev_pg_fault = contigmem_cdev_pager_fault, +}; + +static int +contigmem_mmap_single(struct cdev *cdev, vm_ooffset_t *offset, vm_size_t size, + struct vm_object **obj, int nprot) +{ + struct contigmem_vm_handle *vmh; + uint64_t buffer_index; + + /* + * The buffer index is encoded in the offset. Divide the offset by + * PAGE_SIZE to get the index of the buffer requested by the user + * app. + */ + buffer_index = *offset / PAGE_SIZE; + if (buffer_index >= contigmem_num_buffers) + return EINVAL; + + if (size > contigmem_buffer_size) + return EINVAL; + + vmh = malloc(sizeof(*vmh), M_CONTIGMEM, M_NOWAIT | M_ZERO); + if (vmh == NULL) + return ENOMEM; + vmh->buffer_index = buffer_index; + + *offset = (vm_ooffset_t)vtophys(contigmem_buffers[buffer_index].addr); + *obj = cdev_pager_allocate(vmh, OBJT_DEVICE, &contigmem_cdev_pager_ops, + size, nprot, *offset, curthread->td_ucred); + + return 0; +} diff --git a/src/spdk/dpdk/kernel/freebsd/contigmem/meson.build b/src/spdk/dpdk/kernel/freebsd/contigmem/meson.build new file mode 100644 index 000000000..8fb2ab786 --- /dev/null +++ b/src/spdk/dpdk/kernel/freebsd/contigmem/meson.build @@ -0,0 +1,4 @@ +# SPDX-License-Identifier: BSD-3-Clause +# Copyright(c) 2017 Intel Corporation + +sources = files('contigmem.c') diff --git a/src/spdk/dpdk/kernel/freebsd/meson.build b/src/spdk/dpdk/kernel/freebsd/meson.build new file mode 100644 index 000000000..dc156a43f --- /dev/null +++ b/src/spdk/dpdk/kernel/freebsd/meson.build @@ -0,0 +1,35 @@ +# SPDX-License-Identifier: BSD-3-Clause +# Copyright(c) 2018 Intel Corporation + +kmods = ['contigmem', 'nic_uio'] + +# for building kernel modules, we use kernel build system using make, as +# with Linux. We have a skeleton BSDmakefile, which pulls many of its +# values from the environment. Each module only has a single source file +# right now, which allows us to simplify things. We pull in the sourcer +# files from the individual meson.build files, and then use a custom +# target to call make, passing in the values as env parameters. +kmod_cflags = ['-I' + meson.build_root(), + '-I' + join_paths(meson.source_root(), 'config'), + '-include rte_config.h'] + +# to avoid warnings due to race conditions with creating the dev_if.h, etc. +# files, serialize the kernel module builds. Each module will depend on +# previous ones +built_kmods = [] +foreach k:kmods + subdir(k) + built_kmods += custom_target(k, + input: [files('BSDmakefile.meson'), sources], + output: k + '.ko', + command: ['make', '-f', '@INPUT0@', + 'KMOD_OBJDIR=@OUTDIR@', + 'KMOD_SRC=@INPUT1@', + 'KMOD=' + k, + 'KMOD_CFLAGS=' + ' '.join(kmod_cflags), + 'CC=clang'], + depends: built_kmods, # make each module depend on prev + build_by_default: get_option('enable_kmods'), + install: get_option('enable_kmods'), + install_dir: '/boot/modules/') +endforeach diff --git a/src/spdk/dpdk/kernel/freebsd/nic_uio/BSDmakefile b/src/spdk/dpdk/kernel/freebsd/nic_uio/BSDmakefile new file mode 100644 index 000000000..b6f92d55e --- /dev/null +++ b/src/spdk/dpdk/kernel/freebsd/nic_uio/BSDmakefile @@ -0,0 +1,8 @@ +# SPDX-License-Identifier: BSD-3-Clause +# Copyright(c) 2010-2014 Intel Corporation +# + +KMOD= nic_uio +SRCS= nic_uio.c device_if.h bus_if.h pci_if.h + +.include <bsd.kmod.mk> diff --git a/src/spdk/dpdk/kernel/freebsd/nic_uio/Makefile b/src/spdk/dpdk/kernel/freebsd/nic_uio/Makefile new file mode 100644 index 000000000..376ef3a3a --- /dev/null +++ b/src/spdk/dpdk/kernel/freebsd/nic_uio/Makefile @@ -0,0 +1,24 @@ +# SPDX-License-Identifier: BSD-3-Clause +# Copyright(c) 2010-2014 Intel Corporation + +include $(RTE_SDK)/mk/rte.vars.mk + +# +# module name and path +# +MODULE = nic_uio + +# +# CFLAGS +# +MODULE_CFLAGS += -I$(SRCDIR) +MODULE_CFLAGS += -I$(RTE_OUTPUT)/include +MODULE_CFLAGS += -Winline -Wall -Werror +MODULE_CFLAGS += -include $(RTE_OUTPUT)/include/rte_config.h + +# +# all source are stored in SRCS-y +# +SRCS-y := nic_uio.c + +include $(RTE_SDK)/mk/rte.bsdmodule.mk diff --git a/src/spdk/dpdk/kernel/freebsd/nic_uio/meson.build b/src/spdk/dpdk/kernel/freebsd/nic_uio/meson.build new file mode 100644 index 000000000..4bdaf9694 --- /dev/null +++ b/src/spdk/dpdk/kernel/freebsd/nic_uio/meson.build @@ -0,0 +1,4 @@ +# SPDX-License-Identifier: BSD-3-Clause +# Copyright(c) 2017 Intel Corporation + +sources = files('nic_uio.c') diff --git a/src/spdk/dpdk/kernel/freebsd/nic_uio/nic_uio.c b/src/spdk/dpdk/kernel/freebsd/nic_uio/nic_uio.c new file mode 100644 index 000000000..7a81694c9 --- /dev/null +++ b/src/spdk/dpdk/kernel/freebsd/nic_uio/nic_uio.c @@ -0,0 +1,351 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2010-2014 Intel Corporation + */ +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/param.h> /* defines used in kernel.h */ +#include <sys/module.h> +#include <sys/kernel.h> /* types used in module initialization */ +#include <sys/conf.h> /* cdevsw struct */ +#include <sys/bus.h> /* structs, prototypes for pci bus stuff and DEVMETHOD */ +#include <sys/rman.h> +#include <sys/systm.h> +#include <sys/lock.h> +#include <sys/rwlock.h> +#include <sys/proc.h> + +#include <machine/bus.h> +#include <dev/pci/pcivar.h> /* For pci_get macros! */ +#include <dev/pci/pcireg.h> /* The softc holds our per-instance data. */ +#include <vm/vm.h> +#include <vm/uma.h> +#include <vm/vm_object.h> +#include <vm/vm_page.h> +#include <vm/vm_pager.h> + + +#define MAX_BARS (PCIR_MAX_BAR_0 + 1) + +#define MAX_DETACHED_DEVICES 128 +static device_t detached_devices[MAX_DETACHED_DEVICES] = {}; +static int num_detached = 0; + +struct nic_uio_softc { + device_t dev_t; + struct cdev *my_cdev; + int bar_id[MAX_BARS]; + struct resource *bar_res[MAX_BARS]; + u_long bar_start[MAX_BARS]; + u_long bar_size[MAX_BARS]; +}; + +/* Function prototypes */ +static d_open_t nic_uio_open; +static d_close_t nic_uio_close; +static d_mmap_t nic_uio_mmap; +static d_mmap_single_t nic_uio_mmap_single; +static int nic_uio_probe(device_t dev); +static int nic_uio_attach(device_t dev); +static int nic_uio_detach(device_t dev); +static int nic_uio_shutdown(void); +static int nic_uio_modevent(module_t mod, int type, void *arg); + +static struct cdevsw uio_cdevsw = { + .d_name = "nic_uio", + .d_version = D_VERSION, + .d_open = nic_uio_open, + .d_close = nic_uio_close, + .d_mmap = nic_uio_mmap, + .d_mmap_single = nic_uio_mmap_single, +}; + +static device_method_t nic_uio_methods[] = { + DEVMETHOD(device_probe, nic_uio_probe), + DEVMETHOD(device_attach, nic_uio_attach), + DEVMETHOD(device_detach, nic_uio_detach), + DEVMETHOD_END +}; + +struct device { + int vend; + int dev; +}; + +struct pci_bdf { + uint32_t bus; + uint32_t devid; + uint32_t function; +}; + +static devclass_t nic_uio_devclass; + +DEFINE_CLASS_0(nic_uio, nic_uio_driver, nic_uio_methods, sizeof(struct nic_uio_softc)); +DRIVER_MODULE(nic_uio, pci, nic_uio_driver, nic_uio_devclass, nic_uio_modevent, 0); + +static int +nic_uio_mmap(struct cdev *cdev, vm_ooffset_t offset, vm_paddr_t *paddr, + int prot, vm_memattr_t *memattr) +{ + *paddr = offset; + return 0; +} + +static int +nic_uio_mmap_single(struct cdev *cdev, vm_ooffset_t *offset, vm_size_t size, + struct vm_object **obj, int nprot) +{ + /* + * The BAR index is encoded in the offset. Divide the offset by + * PAGE_SIZE to get the index of the bar requested by the user + * app. + */ + unsigned bar = *offset/PAGE_SIZE; + struct nic_uio_softc *sc = cdev->si_drv1; + + if (bar >= MAX_BARS) + return EINVAL; + + if (sc->bar_res[bar] == NULL) { + sc->bar_id[bar] = PCIR_BAR(bar); + + if (PCI_BAR_IO(pci_read_config(sc->dev_t, sc->bar_id[bar], 4))) + sc->bar_res[bar] = bus_alloc_resource_any(sc->dev_t, SYS_RES_IOPORT, + &sc->bar_id[bar], RF_ACTIVE); + else + sc->bar_res[bar] = bus_alloc_resource_any(sc->dev_t, SYS_RES_MEMORY, + &sc->bar_id[bar], RF_ACTIVE); + } + if (sc->bar_res[bar] == NULL) + return ENXIO; + + sc->bar_start[bar] = rman_get_start(sc->bar_res[bar]); + sc->bar_size[bar] = rman_get_size(sc->bar_res[bar]); + + device_printf(sc->dev_t, "Bar %u @ %lx, size %lx\n", bar, + sc->bar_start[bar], sc->bar_size[bar]); + + *offset = sc->bar_start[bar]; + *obj = vm_pager_allocate(OBJT_DEVICE, cdev, size, nprot, *offset, + curthread->td_ucred); + return 0; +} + + +int +nic_uio_open(struct cdev *dev, int oflags, int devtype, struct thread *td) +{ + return 0; +} + +int +nic_uio_close(struct cdev *dev, int fflag, int devtype, struct thread *td) +{ + return 0; +} + +static int +nic_uio_probe (device_t dev) +{ + int i; + unsigned int bus = pci_get_bus(dev); + unsigned int device = pci_get_slot(dev); + unsigned int function = pci_get_function(dev); + + char bdf_str[256]; + char *token, *remaining; + + /* First check if we found this on load */ + for (i = 0; i < num_detached; i++) + if (bus == pci_get_bus(detached_devices[i]) && + device == pci_get_slot(detached_devices[i]) && + function == pci_get_function(detached_devices[i])) { + device_set_desc(dev, "DPDK PCI Device"); + return BUS_PROBE_SPECIFIC; + } + + /* otherwise check if it's a new device and if it matches the BDF */ + memset(bdf_str, 0, sizeof(bdf_str)); + TUNABLE_STR_FETCH("hw.nic_uio.bdfs", bdf_str, sizeof(bdf_str)); + remaining = bdf_str; + while (1) { + if (remaining == NULL || remaining[0] == '\0') + break; + token = strsep(&remaining, ",:"); + if (token == NULL) + break; + bus = strtol(token, NULL, 10); + token = strsep(&remaining, ",:"); + if (token == NULL) + break; + device = strtol(token, NULL, 10); + token = strsep(&remaining, ",:"); + if (token == NULL) + break; + function = strtol(token, NULL, 10); + + if (bus == pci_get_bus(dev) && + device == pci_get_slot(dev) && + function == pci_get_function(dev)) { + + if (num_detached < MAX_DETACHED_DEVICES) { + printf("%s: probed dev=%p\n", + __func__, dev); + detached_devices[num_detached++] = dev; + device_set_desc(dev, "DPDK PCI Device"); + return BUS_PROBE_SPECIFIC; + } else { + printf("%s: reached MAX_DETACHED_DEVICES=%d. dev=%p won't be reattached\n", + __func__, MAX_DETACHED_DEVICES, + dev); + break; + } + } + } + + return ENXIO; +} + +static int +nic_uio_attach(device_t dev) +{ + int i; + struct nic_uio_softc *sc; + + sc = device_get_softc(dev); + sc->dev_t = dev; + sc->my_cdev = make_dev(&uio_cdevsw, device_get_unit(dev), + UID_ROOT, GID_WHEEL, 0600, "uio@pci:%u:%u:%u", + pci_get_bus(dev), pci_get_slot(dev), pci_get_function(dev)); + if (sc->my_cdev == NULL) + return ENXIO; + sc->my_cdev->si_drv1 = sc; + + for (i = 0; i < MAX_BARS; i++) + sc->bar_res[i] = NULL; + + pci_enable_busmaster(dev); + + return 0; +} + +static int +nic_uio_detach(device_t dev) +{ + int i; + struct nic_uio_softc *sc; + sc = device_get_softc(dev); + + for (i = 0; i < MAX_BARS; i++) + if (sc->bar_res[i] != NULL) { + + if (PCI_BAR_IO(pci_read_config(dev, sc->bar_id[i], 4))) + bus_release_resource(dev, SYS_RES_IOPORT, sc->bar_id[i], + sc->bar_res[i]); + else + bus_release_resource(dev, SYS_RES_MEMORY, sc->bar_id[i], + sc->bar_res[i]); + } + + if (sc->my_cdev != NULL) + destroy_dev(sc->my_cdev); + return 0; +} + +static void +nic_uio_load(void) +{ + uint32_t bus, device, function; + device_t dev; + char bdf_str[256]; + char *token, *remaining; + + memset(bdf_str, 0, sizeof(bdf_str)); + TUNABLE_STR_FETCH("hw.nic_uio.bdfs", bdf_str, sizeof(bdf_str)); + remaining = bdf_str; + printf("nic_uio: hw.nic_uio.bdfs = '%s'\n", bdf_str); + /* + * Users should specify PCI BDFs in the format "b:d:f,b:d:f,b:d:f". + * But the code below does not try differentiate between : and , + * and just blindly uses 3 tokens at a time to construct a + * bus/device/function tuple. + * + * There is no checking on strtol() return values, but this should + * be OK. Worst case is it cannot convert and returns 0. This + * could give us a different BDF than intended, but as long as the + * PCI device/vendor ID does not match it will not matter. + */ + while (1) { + if (remaining == NULL || remaining[0] == '\0') + break; + token = strsep(&remaining, ",:"); + if (token == NULL) + break; + bus = strtol(token, NULL, 10); + token = strsep(&remaining, ",:"); + if (token == NULL) + break; + device = strtol(token, NULL, 10); + token = strsep(&remaining, ",:"); + if (token == NULL) + break; + function = strtol(token, NULL, 10); + + dev = pci_find_bsf(bus, device, function); + if (dev == NULL) + continue; + + if (num_detached < MAX_DETACHED_DEVICES) { + printf("nic_uio_load: detaching and storing dev=%p\n", + dev); + detached_devices[num_detached++] = dev; + } else { + printf("nic_uio_load: reached MAX_DETACHED_DEVICES=%d. dev=%p won't be reattached\n", + MAX_DETACHED_DEVICES, dev); + } + device_detach(dev); + } +} + +static void +nic_uio_unload(void) +{ + int i; + printf("nic_uio_unload: entered...\n"); + + for (i = 0; i < num_detached; i++) { + printf("nic_uio_unload: calling to device_probe_and_attach for dev=%p...\n", + detached_devices[i]); + device_probe_and_attach(detached_devices[i]); + printf("nic_uio_unload: done.\n"); + } + + printf("nic_uio_unload: leaving...\n"); +} + +static int +nic_uio_shutdown(void) +{ + return 0; +} + +static int +nic_uio_modevent(module_t mod, int type, void *arg) +{ + + switch (type) { + case MOD_LOAD: + nic_uio_load(); + break; + case MOD_UNLOAD: + nic_uio_unload(); + break; + case MOD_SHUTDOWN: + nic_uio_shutdown(); + break; + default: + break; + } + + return 0; +} |