From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from dpdk.org (dpdk.org [92.243.14.124]) by inbox.dpdk.org (Postfix) with ESMTP id DD25AA2EDB for ; Fri, 6 Sep 2019 11:13:05 +0200 (CEST) Received: from [92.243.14.124] (localhost [127.0.0.1]) by dpdk.org (Postfix) with ESMTP id A67E61F22C; Fri, 6 Sep 2019 11:13:05 +0200 (CEST) Received: from mx0b-0016f401.pphosted.com (mx0b-0016f401.pphosted.com [67.231.156.173]) by dpdk.org (Postfix) with ESMTP id 77C9A1F22B for ; Fri, 6 Sep 2019 11:13:03 +0200 (CEST) Received: from pps.filterd (m0045851.ppops.net [127.0.0.1]) by mx0b-0016f401.pphosted.com (8.16.0.42/8.16.0.42) with SMTP id x8695fOQ019096; Fri, 6 Sep 2019 02:13:02 -0700 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=marvell.com; h=from : to : cc : subject : date : message-id : mime-version : content-type; s=pfpt0818; bh=24UlWL0hMY4XRXgeRTJtFRAmUI0KfxEaryM0Rgp4V7E=; b=FacObxxrvDrwKB3dbUNJChU7EQWKqPFbT4uZnUK9j6cGsZXDeLCtLbiBYZ6FLANj3Y8h Bsap2qv6qlYQMgELFAka5maY8+LDm2f2lGVdtffjpBwDC7ErH8QmvQMPP9zntS3xlkuE Rru5XoSrha9OQMrU6OhEWZhWUwxa+FjDXjqhFEPbl/fEExoa1ElTYzo3AxCuPxpA7Xyr trdu6YNzbIOYTIlD1l8cHqeS/MP6pchsVZFjjwABivqRsoNwjgRf5b7W2pRhKFcExv3M XAw5oF4XByCvRgdgCN1G8U1lp2Szx7MdARj2Iz5S1y2cIJJ+M/FDcCAXffoZaXdHxGpk 0g== Received: from sc-exch03.marvell.com ([199.233.58.183]) by mx0b-0016f401.pphosted.com with ESMTP id 2uqrdmpdtw-1 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-SHA384 bits=256 verify=NOT); Fri, 06 Sep 2019 02:13:02 -0700 Received: from SC-EXCH01.marvell.com (10.93.176.81) by SC-EXCH03.marvell.com (10.93.176.83) with Microsoft SMTP Server (TLS) id 15.0.1367.3; Fri, 6 Sep 2019 02:12:59 -0700 Received: from maili.marvell.com (10.93.176.43) by SC-EXCH01.marvell.com (10.93.176.81) with Microsoft SMTP Server id 15.0.1367.3 via Frontend Transport; Fri, 6 Sep 2019 02:12:59 -0700 Received: from hyd1vattunuru-dt.caveonetworks.com (unknown [10.29.52.72]) by maili.marvell.com (Postfix) with ESMTP id 1FE9C3F7043; Fri, 6 Sep 2019 02:12:56 -0700 (PDT) From: To: CC: , , Vamsi Attunuru Date: Fri, 6 Sep 2019 14:42:30 +0530 Message-ID: <20190906091230.13923-1-vattunuru@marvell.com> X-Mailer: git-send-email 2.8.4 MIME-Version: 1.0 Content-Type: text/plain X-Proofpoint-Virus-Version: vendor=fsecure engine=2.50.10434:6.0.70,1.0.8 definitions=2019-09-06_04:2019-09-04,2019-09-06 signatures=0 Subject: [dpdk-dev] [PATCH v1 1/1] kernel/linux: introduce vfio_pf kernel module 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" From: Vamsi Attunuru The DPDK use case such as VF representer or OVS offload etc would call for PF and VF PCIe devices to bind vfio-pci module to enable IOMMU protection. In addition to vSwitch use case, unlike, other PCI class of devices, Network class of PCIe devices would have additional responsibility on the PF devices such as promiscuous mode support etc. The above use cases demand VFIO needs bound to PF and its VF devices. This is use case is not supported in Linux kernel, due to a security issue where it is possible to have DoS in case if VF attached to guest over vfio-pci and netdev kernel driver runs on it and which something VF representer would like to enable it. Since we can not differentiate, the vfio-pci bounded VF devices runs DPDK application or netdev driver in guest, we can not introduce any scheme to fix DoS case and therefore not have proper support of this in the upstream kernel. The igb_uio enables such PF and VF binding support for non-iommu devices to make VF representer or OVS offload run on non-iommu devices with DoS vulnerability for netdev driver as VF. This kernel module, facilitate to enable SRIOV on PF devices, therefore, to run both PF and VF devices in VFIO mode knowing its impacts like igb_uio driver functions of non-iommu devices. Signed-off-by: Vamsi Attunuru Signed-off-by: Jerin Jacob --- MAINTAINERS | 6 + config/common_linux | 1 + doc/guides/prog_guide/index.rst | 1 + doc/guides/prog_guide/vfio_pf.rst | 90 ++++++++++ kernel/linux/Makefile | 1 + kernel/linux/meson.build | 2 +- kernel/linux/vfio_pf/Kbuild | 2 + kernel/linux/vfio_pf/Makefile | 24 +++ kernel/linux/vfio_pf/meson.build | 20 +++ kernel/linux/vfio_pf/vfio_pf.c | 360 ++++++++++++++++++++++++++++++++++++++ 10 files changed, 506 insertions(+), 1 deletion(-) diff --git a/MAINTAINERS b/MAINTAINERS index 4100260..49178a5 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -288,6 +288,12 @@ M: Anatoly Burakov F: lib/librte_eal/linux/eal/*vfio* F: drivers/bus/pci/linux/*vfio* +Linux VFIO_PF +M: Vamsi Attunuru +M: Jerin Jacob +F: kernel/linux/vfio_pf/ +F: doc/guides/prog_guide/vfio_pf.rst + FreeBSD EAL (with overlaps) M: Bruce Richardson F: lib/librte_eal/freebsd/Makefile diff --git a/config/common_linux b/config/common_linux index 6e25255..70f1e79 100644 --- a/config/common_linux +++ b/config/common_linux @@ -11,6 +11,7 @@ CONFIG_RTE_EAL_NUMA_AWARE_HUGEPAGES=y CONFIG_RTE_EAL_IGB_UIO=y CONFIG_RTE_EAL_VFIO=y CONFIG_RTE_KNI_KMOD=y +CONFIG_RTE_VFIO_PF_KMOD=y CONFIG_RTE_LIBRTE_KNI=y CONFIG_RTE_LIBRTE_PMD_KNI=y CONFIG_RTE_LIBRTE_VHOST=y diff --git a/doc/guides/prog_guide/index.rst b/doc/guides/prog_guide/index.rst index 692409a..601244c 100644 --- a/doc/guides/prog_guide/index.rst +++ b/doc/guides/prog_guide/index.rst @@ -43,6 +43,7 @@ Programmer's Guide pdump_lib multi_proc_support kernel_nic_interface + vfio_pf thread_safety_dpdk_functions eventdev event_ethernet_rx_adapter diff --git a/doc/guides/prog_guide/vfio_pf.rst b/doc/guides/prog_guide/vfio_pf.rst new file mode 100644 index 0000000..7e43cc7 --- /dev/null +++ b/doc/guides/prog_guide/vfio_pf.rst @@ -0,0 +1,90 @@ +.. SPDX-License-Identifier: BSD-3-Clause + Copyright(C) 2019 Marvell International Ltd. + +.. _vfio_pf: + +The DPDK VFIO_PF Kernel Module +------------------------------ + +The VFIO_PF kernel loadable module ``vfio_pf`` provides sysfs way of enabling +PCI VF devices when the PCI PF is bound to VFIO driver. + +DPDK use case such as VF representer or OVS offload etc would call for PF +and VF PCIe devices to bind vfio-pci module to enable IOMMU protection. + +In addition to vSwitch use case, unlike, other PCI class of devices, Network +class of PCIe devices would have additional responsibility on the PF devices +such as promiscuous mode support etc. The above use cases demand VFIO needs +bound to PF and its VF devices. + +These use case is not supported in Linux kernel, due to a security issue +where it is possible to have DoS in case if VF attached to guest over vfio-pci +and netdev kernel driver runs on it and which something VF representer would +like to enable it. + +The igb_uio enables such PF and VF binding support for non-iommu devices to +make VF representer or OVS offload run on non-iommu devices with DoS +vulnerability for netdev driver as VF. + +This kernel module facilitate to enable SRIOV on PF devices, therefore, to run +both PF and VF devices in VFIO mode knowing its impacts like igb_uio driver +functions of non-iommu devices. + +Example usage +------------- + +Following example demonstrates enabling vfs on Marvell's Octeontx2 platform. +RVU PF devices PF1 & PF2 are probed on BDFs "0002:02:00.0" and "0002:03:00.0" +respectively. 2 VFs of each PF are enabled after followng the below procedure. + +.. code-block:: console + + # echo "177d a063" > /sys/bus/pci/drivers/vfio-pci/new_id + # echo 0002:02:00.0 > /sys/bus/pci/devices/0002:02:00.0/driver/unbind + # echo 0002:02:00.0 > /sys/bus/pci/drivers/vfio-pci/bind + # echo 0002:03:00.0 > /sys/bus/pci/devices/0002:03:00.0/driver/unbind + # echo 0002:03:00.0 > /sys/bus/pci/drivers/vfio-pci/bind + + # lspci -k + 0002:02:00.0 Ethernet controller: Cavium, Inc. Device a063 (rev 01) + Subsystem: Cavium, Inc. Device b200 + Kernel driver in use: vfio-pci + 0002:03:00.0 Ethernet controller: Cavium, Inc. Device a063 (rev 01) + Subsystem: Cavium, Inc. Device b200 + Kernel driver in use: vfio-pci + + # insmod build/kernel/linux/vfio_pf/vfio_pf.ko + + # echo 0002:02:00.0 > /sys/vfio_pf/add_device + # echo 2 > /sys/vfio_pf/0002\:02\:00.0/num_vfs + # echo 0002:03:00.0 > /sys/vfio_pf/add_device + # echo 2 > /sys/vfio_pf/0002\:03\:00.0/num_vfs + + # lspci -k + 0002:02:00.0 Ethernet controller: Cavium, Inc. Device a063 (rev 01) + Subsystem: Cavium, Inc. Device b200 + Kernel driver in use: vfio-pci + 0002:02:00.1 Ethernet controller: Cavium, Inc. Device a064 (rev 01) + Subsystem: Cavium, Inc. Device b200 + 0002:02:00.2 Ethernet controller: Cavium, Inc. Device a064 (rev 01) + Subsystem: Cavium, Inc. Device b200 + 0002:03:00.0 Ethernet controller: Cavium, Inc. Device a063 (rev 01) + Subsystem: Cavium, Inc. Device b200 + Kernel driver in use: vfio-pci + 0002:03:00.1 Ethernet controller: Cavium, Inc. Device a064 (rev 01) + Subsystem: Cavium, Inc. Device b200 + 0002:03:00.2 Ethernet controller: Cavium, Inc. Device a064 (rev 01) + Subsystem: Cavium, Inc. Device b200 + + # echo 0 > /sys/vfio_pf/0002\:02\:00.0/num_vfs + # echo 0002:02:00.0 > /sys/vfio_pf/remove_device + # echo 0 > /sys/vfio_pf/0002\:03\:00.0/num_vfs + # echo 0002:03:00.0 > /sys/vfio_pf/remove_device + + # rmmod build/kernel/linux/vfio_pf/vfio_pf.ko + +Prerequisite +------------- + +PCI PF device needs to be bound to VFIO driver before enabling VFs using +vfio_pf kernel module. diff --git a/kernel/linux/Makefile b/kernel/linux/Makefile index c2c45a3..ea202ec 100644 --- a/kernel/linux/Makefile +++ b/kernel/linux/Makefile @@ -5,5 +5,6 @@ include $(RTE_SDK)/mk/rte.vars.mk DIRS-$(CONFIG_RTE_EAL_IGB_UIO) += igb_uio DIRS-$(CONFIG_RTE_KNI_KMOD) += kni +DIRS-$(CONFIG_RTE_VFIO_PF_KMOD) += vfio_pf include $(RTE_SDK)/mk/rte.subdir.mk diff --git a/kernel/linux/meson.build b/kernel/linux/meson.build index 1796cc6..50591dd 100644 --- a/kernel/linux/meson.build +++ b/kernel/linux/meson.build @@ -1,7 +1,7 @@ # SPDX-License-Identifier: BSD-3-Clause # Copyright(c) 2018 Intel Corporation -subdirs = ['igb_uio', 'kni'] +subdirs = ['igb_uio', 'kni', 'vfio_pf'] # if we are cross-compiling we need kernel_dir specified if get_option('kernel_dir') == '' and meson.is_cross_build() diff --git a/kernel/linux/vfio_pf/Kbuild b/kernel/linux/vfio_pf/Kbuild new file mode 100644 index 0000000..18365c8 --- /dev/null +++ b/kernel/linux/vfio_pf/Kbuild @@ -0,0 +1,2 @@ +ccflags-y := $(MODULE_CFLAGS) +obj-m := vfio_pf.o diff --git a/kernel/linux/vfio_pf/Makefile b/kernel/linux/vfio_pf/Makefile new file mode 100644 index 0000000..0ffbc6f --- /dev/null +++ b/kernel/linux/vfio_pf/Makefile @@ -0,0 +1,24 @@ +# SPDX-License-Identifier: BSD-3-Clause +# Copyright(C) 2019 Marvell International Ltd. + +include $(RTE_SDK)/mk/rte.vars.mk + +# +# module name and path +# +MODULE = vfio_pf + +# +# CFLAGS +# +MODULE_CFLAGS += -I$(SRCDIR) --param max-inline-insns-single=100 +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 := vfio_pf.c + +include $(RTE_SDK)/mk/rte.module.mk diff --git a/kernel/linux/vfio_pf/meson.build b/kernel/linux/vfio_pf/meson.build new file mode 100644 index 0000000..e485e4c --- /dev/null +++ b/kernel/linux/vfio_pf/meson.build @@ -0,0 +1,20 @@ +# SPDX-License-Identifier: BSD-3-Clause +# Copyright(C) 2019 Marvell International Ltd. + +mkfile = custom_target('vfio_pf_makefile', + output: 'Makefile', + command: ['touch', '@OUTPUT@']) + +custom_target('vfio_pf', + input: ['vfio_pf.c', 'Kbuild'], + output: 'vfio_pf.ko', + command: ['make', '-C', kernel_dir + '/build', + 'M=' + meson.current_build_dir(), + 'src=' + meson.current_source_dir(), + 'EXTRA_CFLAGS=-I' + meson.current_source_dir() + + '/../../../lib/librte_eal/common/include', + 'modules'], + depends: mkfile, + install: true, + install_dir: kernel_dir + '/extra/dpdk', + build_by_default: get_option('enable_kmods')) diff --git a/kernel/linux/vfio_pf/vfio_pf.c b/kernel/linux/vfio_pf/vfio_pf.c new file mode 100644 index 0000000..42f7f93 --- /dev/null +++ b/kernel/linux/vfio_pf/vfio_pf.c @@ -0,0 +1,360 @@ +/* SPDX-License-Identifier: GPL-2.0 + * Copyright(C) 2019 Marvell International Ltd. + */ + +#include +#include +#include +#include + +struct vfio_pf_group { + struct kobject *kobj; + struct kobj_attribute add_pf; + struct kobj_attribute remove_pf; + struct mutex lock; + struct list_head head; +}; + +static struct vfio_pf_group *pf_group; + +#define FMT_NVAL 4 +#define PCI_STR_SIZE sizeof("XXXXXXXX:XX:XX.X") + +struct pci_addr { + char name[PCI_STR_SIZE + 1]; + uint32_t domain; + uint8_t bus; + uint8_t devid; + uint8_t function; +}; + +struct pf_obj { + struct list_head node; + struct pci_dev *pdev; + struct kobject *kobj; + struct kobj_attribute sysfs; + struct pci_addr paddr; +}; + +static int +str_split(char *string, int stringlen, + char **tokens, int maxtokens, char delim) +{ + int tokstart = 1; + int i, tok = 0; + + if (string == NULL || tokens == NULL) + goto error; + + for (i = 0; i < stringlen; i++) { + if (string[i] == '\0' || tok >= maxtokens) + break; + if (tokstart) { + tokstart = 0; + tokens[tok++] = &string[i]; + } + if (string[i] == delim) { + string[i] = '\0'; + tokstart = 1; + } + } + return tok; + +error: + return -1; +} + +static int +parse_pci_addr(const char *buf, int bufsize, struct pci_addr *paddr) +{ + union splitaddr { + struct { + char *domain; + char *bus; + char *devid; + char *function; + }; + char *str[FMT_NVAL]; + } splitaddr; + + char *buf_copy = kstrndup(buf, bufsize, GFP_KERNEL); + if (buf_copy == NULL) + return -ENOMEM; + + if (str_split(buf_copy, bufsize, splitaddr.str, FMT_NVAL, ':') + != FMT_NVAL - 1) + goto error; + /* final split is on '.' between devid and function */ + splitaddr.function = strchr(splitaddr.devid, '.'); + if (splitaddr.function == NULL) + goto error; + *splitaddr.function++ = '\0'; + + if (kstrtou32(splitaddr.domain, 16, &paddr->domain) || + kstrtou8(splitaddr.bus, 16, &paddr->bus) || + kstrtou8(splitaddr.devid, 16, &paddr->devid) || + kstrtou8(splitaddr.function, 10, &paddr->function)) + goto error; + + snprintf(paddr->name, sizeof(paddr->name), "%.4x:%.2x:%.2x.%.x", + paddr->domain, paddr->bus, paddr->devid, paddr->function); + + kfree(buf_copy); + return 0; +error: + kfree(buf_copy); + return -EINVAL; +} + +static ssize_t +show_num_vfs(struct kobject *kobj, struct kobj_attribute *attr, char *buf) +{ + struct pf_obj *obj; + + obj = container_of(attr, struct pf_obj, sysfs); + + return snprintf(buf, 10, "%u\n", pci_num_vf(obj->pdev)); +} + +static ssize_t +store_num_vfs(struct kobject *kobj, struct kobj_attribute *attr, + const char *buf, size_t count) +{ + struct pf_obj *obj; + int num_vfs, err = 0; + + obj = container_of(attr, struct pf_obj, sysfs); + + if (kstrtoint(buf, 0, &num_vfs)) { + pr_err("Invalid %s, %s\n", attr->attr.name, buf); + err = -EIO; + } + if (num_vfs < 0) { + pr_err("Invalid %s, %d < 0\n", attr->attr.name, + num_vfs); + err = -EIO; + } + + if (num_vfs == 0) + pci_disable_sriov(obj->pdev); + else if (pci_num_vf(obj->pdev) == 0) + err = pci_enable_sriov(obj->pdev, num_vfs); + else + err = -EINVAL; + + return err ? err : count; +} + +static int +pf_sysfs_create(char *name, struct pf_obj *obj) +{ + int err; + + if (name == NULL || obj == NULL) + return -EINVAL; + + obj->sysfs.show = show_num_vfs; + obj->sysfs.store = store_num_vfs; + obj->sysfs.attr.name = name; + obj->sysfs.attr.mode = 0644; + + sysfs_attr_init(&obj->sysfs.attr); + err = sysfs_create_file(obj->kobj, &obj->sysfs.attr); + if (err) { + pr_err("Failed to create '%s' sysfs for '%s'\n", + name, kobject_name(obj->kobj)); + return -EFAULT; + } + + return 0; +} + +static int +probe_pf_dev(struct pf_obj *obj) +{ + struct pci_dev *pdev = NULL; + struct pci_addr *paddr; + + paddr = &obj->paddr; + + pdev = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, pdev); + if (pdev == NULL) + return -ENODEV; + + while (pdev) { + if ((paddr->domain == pci_domain_nr(pdev->bus)) && + (pdev->bus->number == paddr->bus) && + (PCI_SLOT(pdev->devfn) == paddr->devid) && + (PCI_FUNC(pdev->devfn) == paddr->function)) + break; + + pdev = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, pdev); + }; + + if (pdev) { + obj->pdev = pdev; + return 0; + } else + return -ENODEV; +} + +static ssize_t +add_device(struct kobject *kobj, struct kobj_attribute *attr, + const char *buf, size_t count) +{ + struct pf_obj *obj; + int err = 0; + + obj = kzalloc(sizeof(struct pf_obj), GFP_KERNEL); + if (obj == NULL) { + err = -ENOMEM; + goto exit; + } + + if (parse_pci_addr(buf, strlen(buf), &obj->paddr)) { + err = -EINVAL; + goto exit; + } + + if (probe_pf_dev(obj)) { + err = -ENXIO; + goto exit; + } + + obj->kobj = kobject_create_and_add(obj->paddr.name, pf_group->kobj); + + if (pf_sysfs_create("num_vfs", obj)) { + pci_dev_put(obj->pdev); + pr_err("Failed to create the sysfs for pdev:%s\n", + obj->paddr.name); + return -EFAULT; + } + + mutex_lock(&pf_group->lock); + list_add(&obj->node, &pf_group->head); + mutex_unlock(&pf_group->lock); + +exit: + if (err && obj) + kfree(obj); + + return err ? err : count; +} + +static void +remove_pf_obj(struct pf_obj *obj) +{ + if (pci_num_vf(obj->pdev)) + pci_disable_sriov(obj->pdev); + sysfs_remove_file(obj->kobj, &obj->sysfs.attr); + kobject_del(obj->kobj); + list_del(&obj->node); + pci_dev_put(obj->pdev); + kfree(obj); +} + +static ssize_t +remove_device(struct kobject *kobj, struct kobj_attribute *attr, + const char *buf, size_t count) +{ + struct list_head *pos, *tmp; + struct pci_addr paddr; + struct pf_obj *obj; + int err = 0; + + if (parse_pci_addr(buf, strlen(buf), &paddr)) { + err = -EINVAL; + goto exit; + } + + list_for_each_safe(pos, tmp, &pf_group->head) { + obj = list_entry(pos, struct pf_obj, node); + if (!strncmp(obj->paddr.name, paddr.name, sizeof(paddr.name))) { + remove_pf_obj(obj); + break; + } + } + +exit: + return err ? err : count; +} + +static void +destroy_pf_objs(void) +{ + struct list_head *pos, *tmp; + struct pf_obj *obj; + + list_for_each_safe(pos, tmp, &pf_group->head) { + obj = list_entry(pos, struct pf_obj, node); + remove_pf_obj(obj); + } +} + +static void +__exit vfio_pf_cleanup(void) +{ + if (pf_group == NULL) + return; + + destroy_pf_objs(); + sysfs_remove_file(pf_group->kobj, &pf_group->add_pf.attr); + sysfs_remove_file(pf_group->kobj, &pf_group->remove_pf.attr); + kobject_del(pf_group->kobj); + mutex_destroy(&pf_group->lock); + kfree(pf_group); +} + +static int +__init vfio_pf_init(void) +{ + int err; + + pf_group = kzalloc(sizeof(struct vfio_pf_group), GFP_KERNEL); + if (pf_group == NULL) + return -ENOMEM; + + pf_group->kobj = kobject_create_and_add("vfio_pf", NULL); + pf_group->add_pf.store = add_device; + pf_group->add_pf.attr.name = "add_device"; + pf_group->add_pf.attr.mode = 0644; + pf_group->remove_pf.store = remove_device; + pf_group->remove_pf.attr.name = "remove_device"; + pf_group->remove_pf.attr.mode = 0644; + + sysfs_attr_init(&pf_group->add_pf.attr); + err = sysfs_create_file(pf_group->kobj, &pf_group->add_pf.attr); + if (err) { + pr_err("Failed to create sysfs '%s' for '%s'\n", + pf_group->add_pf.attr.name, + kobject_name(pf_group->kobj)); + goto exit; + } + + sysfs_attr_init(&pf_group->remove_pf.attr); + err = sysfs_create_file(pf_group->kobj, &pf_group->remove_pf.attr); + if (err) { + pr_err("Failed to create sysfs '%s' for '%s'\n", + pf_group->remove_pf.attr.name, + kobject_name(pf_group->kobj)); + sysfs_remove_file(pf_group->kobj, &pf_group->add_pf.attr); + goto exit; + } + + INIT_LIST_HEAD(&pf_group->head); + + mutex_init(&pf_group->lock); + + return 0; + +exit: + kfree(pf_group); + return -EFAULT; +} + +module_init(vfio_pf_init); +module_exit(vfio_pf_cleanup); + +MODULE_DESCRIPTION("Kernel module for enabling SRIOV"); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Marvell International Ltd"); -- 2.8.4