From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from EUR02-HE1-obe.outbound.protection.outlook.com (mail-eopbgr10074.outbound.protection.outlook.com [40.107.1.74]) by dpdk.org (Postfix) with ESMTP id C146A1322C for ; Tue, 2 Jan 2018 18:03:00 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=Mellanox.com; s=selector1; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version; bh=ttC/lUCnqFaL4+a+ZUoSpglLmWHLtaEYQ8OuEzQ0XvQ=; b=nNpqtt5kVCmySacPz3aaQ0PH/+cl2wdtdvEUp43ad6yXgQaUkoWyx++NkEYO0zcueRd79nI3bCFqYGTGEfw3QTR3WAHcG0uSlUVmzuuN/GJyb9rJw4LtLUv9kGdCOvj34Frx9uFhxGipjAyTHZCpkn+zjkg1G724xhiTsl4o3ug= Received: from AM6PR0502MB3797.eurprd05.prod.outlook.com (52.133.21.26) by HE1PR05MB3212.eurprd05.prod.outlook.com (10.170.243.10) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384_P256) id 15.20.366.8; Tue, 2 Jan 2018 17:02:56 +0000 Received: from AM6PR0502MB3797.eurprd05.prod.outlook.com ([fe80::b4b4:7de8:cf70:aa3a]) by AM6PR0502MB3797.eurprd05.prod.outlook.com ([fe80::b4b4:7de8:cf70:aa3a%13]) with mapi id 15.20.0366.009; Tue, 2 Jan 2018 17:02:56 +0000 From: Matan Azrad To: Jeff Guo , "stephen@networkplumber.org" , "bruce.richardson@intel.com" , "ferruh.yigit@intel.com" , "gaetan.rivet@6wind.com" CC: "konstantin.ananyev@intel.com" , "jblunck@infradead.org" , "shreyansh.jain@nxp.com" , "jingjing.wu@intel.com" , "dev@dpdk.org" , Thomas Monjalon , "helin.zhang@intel.com" , Mordechay Haimovsky Thread-Topic: [dpdk-dev] [PATCH v7 1/2] eal: add uevent monitor for hot plug Thread-Index: AQHTg64SECBQ/TN3RUeAgZfnB8cQC6NgekaQ Date: Tue, 2 Jan 2018 17:02:55 +0000 Message-ID: References: <1509567405-27439-3-git-send-email-jia.guo@intel.com> <1514943736-5931-1-git-send-email-jia.guo@intel.com> <1514943736-5931-2-git-send-email-jia.guo@intel.com> In-Reply-To: <1514943736-5931-2-git-send-email-jia.guo@intel.com> Accept-Language: en-US, he-IL Content-Language: en-US X-MS-Has-Attach: X-MS-TNEF-Correlator: authentication-results: spf=none (sender IP is ) smtp.mailfrom=matan@mellanox.com; x-originating-ip: [193.47.165.251] x-ms-publictraffictype: Email x-microsoft-exchange-diagnostics: 1; HE1PR05MB3212; 6:KbBOcO9e83F5rv0z0XCPfZjh6lshFE5p0fW6xewiMk7vE5HAEWXBIKrLcoW0C4MSC0wbzTt+uOfHK62g0a11Z+wdEdXlj9pi5jLZMSn18xMxl+15K52QxzkdH6udK2ykcUCtARkBQZkFZj13+Cg0gWAhQEaC3wrE791f4QGdz+4cBEHyKgj+uV79Js0xwL3a6+7NWHlNnfZqmMaA/RBoWLWdyIjI1nPrfn+HXUcH9sHOU7LL4a8HrbwYWgUtNvE466V2RHO3PhHCObMikH+afLA8BPlVctIRmJfYukn30+id1Zph8H60NrTQKk7JY2s0PUIcncoQxRRflqfZitA5U3iAinyK+MIKnHHDdAylbffpneSzGc9RLp2nM/zlYvYc; 5:zexHrvOJ3btQroDJ+QOnfNMXuF5geq5EBXCeMPliaaAN39QeTjVu1sJXGqsO2AQh7ON/gvqsJHnFJ2Ea4RrhGgQylXlL6wczXTHYkwrQ3ROAIsXQ3ViNZ0dVSWzLGaxpEZ58lkD3Nz9OfOkfsOkoRZUKT2srfGGx9jgTW53gDjo=; 24:N5Zmqypjnec2hjj2iiZ1v2sBFoZkw/qlJkR5IpF7IGBX7FuLw6uVswmkdLuLm60VkI8H4Z8hbARN3JkU7mOQ3k3NKBJ6GP1+VxuTSM2UoME=; 7:t+oBc7/NWYy/YV0Xc/3kcKGrfm47UEMqwnjscqeMsp/iyzJ3uPE8JnmK5J6cxO4TqiTRIvoe+uUSFjOKhVBk4Pxj6/couqZrAaLF0vSYUPvib0dKRzaAb6PEaXk77ynhcXM3m9lM3+JbExDkQSu8a5lai38M6CVeUKIUISbCDCv1L73Q/GIF6ZwjFopPMIU7F1hqJkufDJ89NcR3aETb9hSwtX3eIzo7J1cYVfIElb4UyhZHZnWmmrkRlAyfXgMA x-ms-exchange-antispam-srfa-diagnostics: SSOS; x-ms-office365-filtering-ht: Tenant x-ms-office365-filtering-correlation-id: 1e403eac-e213-43e4-1c62-08d55202ab50 x-microsoft-antispam: UriScan:; BCL:0; PCL:0; RULEID:(4534020)(4602075)(4627115)(201703031133081)(201702281549075)(48565401081)(5600026)(4604075)(3008032)(2017052603307)(7153060); SRVR:HE1PR05MB3212; x-ms-traffictypediagnostic: HE1PR05MB3212: x-ld-processed: a652971c-7d2e-4d9b-a6a4-d149256f461b,ExtAddr x-microsoft-antispam-prvs: x-exchange-antispam-report-test: UriScan:(72170088055959)(209352067349851)(228905959029699); x-exchange-antispam-report-cfa-test: BCL:0; PCL:0; RULEID:(6040470)(2401047)(8121501046)(5005006)(3002001)(3231023)(944501075)(93006095)(93001095)(10201501046)(6055026)(6041268)(20161123564045)(20161123558120)(20161123562045)(20161123560045)(201703131423095)(201702281528075)(20161123555045)(201703061421075)(201703061406153)(6072148)(201708071742011); SRVR:HE1PR05MB3212; BCL:0; PCL:0; RULEID:(100000803101)(100110400095); SRVR:HE1PR05MB3212; x-forefront-prvs: 0540846A1D x-forefront-antispam-report: SFV:NSPM; SFS:(10009020)(366004)(39380400002)(376002)(39860400002)(396003)(346002)(199004)(189003)(16200700003)(5250100002)(6436002)(53936002)(76176011)(5890100001)(7416002)(2501003)(575784001)(86362001)(7696005)(6246003)(229853002)(107886003)(2201001)(53946003)(2906002)(106356001)(66066001)(55016002)(59450400001)(6506007)(105586002)(9686003)(14454004)(74316002)(7736002)(4326008)(305945005)(102836004)(5660300001)(25786009)(478600001)(110136005)(68736007)(54906003)(81166006)(81156014)(99286004)(3846002)(2950100002)(33656002)(3660700001)(8676002)(2900100001)(6116002)(8656006)(3280700002)(97736004)(316002)(8936002)(579004)(559001)(569006); DIR:OUT; SFP:1101; SCL:1; SRVR:HE1PR05MB3212; H:AM6PR0502MB3797.eurprd05.prod.outlook.com; FPR:; SPF:None; PTR:InfoNoRecords; A:1; MX:1; LANG:en; received-spf: None (protection.outlook.com: mellanox.com does not designate permitted sender hosts) x-microsoft-antispam-message-info: NL0X3rX5HyL9kPpUFXuKqqw1DTVOeuosiZQxa0oJQZor2UoJIsPxJIfGTznaytZusePBbn162EEwnyTnRlvM8A== spamdiagnosticoutput: 1:99 spamdiagnosticmetadata: NSPM Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: quoted-printable MIME-Version: 1.0 X-OriginatorOrg: Mellanox.com X-MS-Exchange-CrossTenant-Network-Message-Id: 1e403eac-e213-43e4-1c62-08d55202ab50 X-MS-Exchange-CrossTenant-originalarrivaltime: 02 Jan 2018 17:02:55.8797 (UTC) X-MS-Exchange-CrossTenant-fromentityheader: Hosted X-MS-Exchange-CrossTenant-id: a652971c-7d2e-4d9b-a6a4-d149256f461b X-MS-Exchange-Transport-CrossTenantHeadersStamped: HE1PR05MB3212 Subject: Re: [dpdk-dev] [PATCH v7 1/2] eal: add uevent monitor for hot plug 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: , X-List-Received-Date: Tue, 02 Jan 2018 17:03:01 -0000 Hi Jeff Maybe I'm touching in previous discussions but please see some comments\que= stions. From: Jeff Guo: > This patch aim to add a general uevent mechanism in eal device layer, > to enable all linux kernel object hot plug monitoring, so user could use = these > APIs to monitor and read out the device status info that sent from the ke= rnel > side, then corresponding to handle it, such as detach or attach the > device, and even benefit to use it to do smoothly fail safe work. >=20 > 1) About uevent monitoring: > a: add one epolling to poll the netlink socket, to monitor the uevent of > the device, add device_state in struct of rte_device, to identify the > device state machine. > b: add enum of rte_eal_dev_event_type and struct of rte_eal_uevent. > c: add below API in rte eal device common layer. > rte_eal_dev_monitor_enable > rte_dev_callback_register > rte_dev_callback_unregister > _rte_dev_callback_process > rte_dev_monitor_start > rte_dev_monitor_stop >=20 > 2) About failure handler, use pci uio for example, > add pci_remap_device in bus layer and below function to process it: > rte_pci_remap_device > pci_uio_remap_resource > pci_map_private_resource > add rte_pci_dev_bind_driver to bind pci device with explicit driver. >=20 > Signed-off-by: Jeff Guo > --- > v7->v6: > a.modify vdev part according to the vdev rework > b.re-define and split the func into common and bus specific code > c.fix some incorrect issue. > b.fix the system hung after send packcet issue. > --- > drivers/bus/pci/bsd/pci.c | 30 ++ > drivers/bus/pci/linux/pci.c | 87 +++++ > drivers/bus/pci/linux/pci_init.h | 1 + > drivers/bus/pci/pci_common.c | 43 +++ > drivers/bus/pci/pci_common_uio.c | 28 ++ > drivers/bus/pci/private.h | 12 + > drivers/bus/pci/rte_bus_pci.h | 25 ++ > drivers/bus/vdev/vdev.c | 36 +++ > lib/librte_eal/bsdapp/eal/eal_dev.c | 64 ++++ > .../bsdapp/eal/include/exec-env/rte_dev.h | 106 ++++++ > lib/librte_eal/common/eal_common_bus.c | 30 ++ > lib/librte_eal/common/eal_common_dev.c | 169 ++++++++++ > lib/librte_eal/common/include/rte_bus.h | 69 ++++ > lib/librte_eal/common/include/rte_dev.h | 89 ++++++ > lib/librte_eal/linuxapp/eal/Makefile | 3 +- > lib/librte_eal/linuxapp/eal/eal_alarm.c | 5 + > lib/librte_eal/linuxapp/eal/eal_dev.c | 356 > +++++++++++++++++++++ > .../linuxapp/eal/include/exec-env/rte_dev.h | 106 ++++++ > lib/librte_eal/linuxapp/igb_uio/igb_uio.c | 6 + > lib/librte_pci/rte_pci.c | 20 ++ > lib/librte_pci/rte_pci.h | 17 + > 21 files changed, 1301 insertions(+), 1 deletion(-) > create mode 100644 lib/librte_eal/bsdapp/eal/eal_dev.c > create mode 100644 lib/librte_eal/bsdapp/eal/include/exec-env/rte_dev.h > create mode 100644 lib/librte_eal/linuxapp/eal/eal_dev.c > create mode 100644 lib/librte_eal/linuxapp/eal/include/exec-env/rte_dev.= h >=20 > diff --git a/drivers/bus/pci/bsd/pci.c b/drivers/bus/pci/bsd/pci.c > index b8e2178..d58dbf6 100644 > --- a/drivers/bus/pci/bsd/pci.c > +++ b/drivers/bus/pci/bsd/pci.c > @@ -126,6 +126,29 @@ rte_pci_unmap_device(struct rte_pci_device *dev) > } > } >=20 > +/* re-map pci device */ > +int > +rte_pci_remap_device(struct rte_pci_device *dev) > +{ > + int ret; > + > + if (dev =3D=3D NULL) > + return -EINVAL; > + > + switch (dev->kdrv) { > + case RTE_KDRV_NIC_UIO: > + ret =3D pci_uio_remap_resource(dev); > + break; > + default: > + RTE_LOG(DEBUG, EAL, > + " Not managed by a supported kernel driver, > skipped\n"); > + ret =3D 1; > + break; > + } > + > + return ret; > +} > + > void > pci_uio_free_resource(struct rte_pci_device *dev, > struct mapped_pci_resource *uio_res) > @@ -678,3 +701,10 @@ rte_pci_ioport_unmap(struct rte_pci_ioport *p) >=20 > return ret; > } > + > +int > +rte_pci_dev_bind_driver(const char *dev_name, const char *drv_type) > +{ > + return -1; > +} > + > diff --git a/drivers/bus/pci/linux/pci.c b/drivers/bus/pci/linux/pci.c > index 5da6728..792fd2c 100644 > --- a/drivers/bus/pci/linux/pci.c > +++ b/drivers/bus/pci/linux/pci.c > @@ -145,6 +145,38 @@ rte_pci_unmap_device(struct rte_pci_device *dev) > } > } >=20 > +/* Map pci device */ > +int > +rte_pci_remap_device(struct rte_pci_device *dev) > +{ > + int ret =3D -1; > + > + if (dev =3D=3D NULL) > + return -EINVAL; > + > + switch (dev->kdrv) { > + case RTE_KDRV_VFIO: > +#ifdef VFIO_PRESENT > + /* no thing to do */ > +#endif > + break; > + case RTE_KDRV_IGB_UIO: > + case RTE_KDRV_UIO_GENERIC: > + if (rte_eal_using_phys_addrs()) { > + /* map resources for devices that use uio */ > + ret =3D pci_uio_remap_resource(dev); > + } > + break; > + default: > + RTE_LOG(DEBUG, EAL, > + " Not managed by a supported kernel driver, > skipped\n"); > + ret =3D 1; > + break; > + } > + > + return ret; > +} > + > void * > pci_find_max_end_va(void) > { > @@ -386,6 +418,8 @@ pci_scan_one(const char *dirname, const struct > rte_pci_addr *addr) > rte_pci_add_device(dev); > } >=20 > + dev->device.state =3D DEVICE_PARSED; > + TAILQ_INIT(&(dev->device.uev_cbs)); > return 0; > } >=20 > @@ -854,3 +888,56 @@ rte_pci_ioport_unmap(struct rte_pci_ioport *p) >=20 > return ret; > } > + > +int > +rte_pci_dev_bind_driver(const char *dev_name, const char *drv_type) > +{ > + char drv_bind_path[1024]; > + char drv_override_path[1024]; /* contains the /dev/uioX */ > + int drv_override_fd; > + int drv_bind_fd; > + > + RTE_SET_USED(drv_type); > + > + snprintf(drv_override_path, sizeof(drv_override_path), > + "/sys/bus/pci/devices/%s/driver_override", dev_name); > + > + /* specify the driver for a device by writing to driver_override */ > + drv_override_fd =3D open(drv_override_path, O_WRONLY); > + if (drv_override_fd < 0) { > + RTE_LOG(ERR, EAL, "Cannot open %s: %s\n", > + drv_override_path, strerror(errno)); > + goto err; > + } > + > + if (write(drv_override_fd, drv_type, sizeof(drv_type)) < 0) { > + RTE_LOG(ERR, EAL, > + "Error: bind failed - Cannot write " > + "driver %s to device %s\n", drv_type, dev_name); > + goto err; > + } > + > + close(drv_override_fd); > + > + snprintf(drv_bind_path, sizeof(drv_bind_path), > + "/sys/bus/pci/drivers/%s/bind", drv_type); > + > + /* do the bind by writing device to the specific driver */ > + drv_bind_fd =3D open(drv_bind_path, O_WRONLY | O_APPEND); > + if (drv_bind_fd < 0) { > + RTE_LOG(ERR, EAL, "Cannot open %s: %s\n", > + drv_bind_path, strerror(errno)); > + goto err; > + } > + > + if (write(drv_bind_fd, dev_name, sizeof(dev_name)) < 0) > + goto err; > + > + close(drv_bind_fd); > + return 0; > +err: > + close(drv_override_fd); > + close(drv_bind_fd); > + return -1; > +} > + > diff --git a/drivers/bus/pci/linux/pci_init.h b/drivers/bus/pci/linux/pci= _init.h > index f342c47..5838402 100644 > --- a/drivers/bus/pci/linux/pci_init.h > +++ b/drivers/bus/pci/linux/pci_init.h > @@ -58,6 +58,7 @@ int pci_uio_alloc_resource(struct rte_pci_device *dev, > struct mapped_pci_resource **uio_res); > void pci_uio_free_resource(struct rte_pci_device *dev, > struct mapped_pci_resource *uio_res); > +int pci_uio_remap_resource(struct rte_pci_device *dev); > int pci_uio_map_resource_by_index(struct rte_pci_device *dev, int > res_idx, > struct mapped_pci_resource *uio_res, int map_idx); >=20 > diff --git a/drivers/bus/pci/pci_common.c b/drivers/bus/pci/pci_common.c > index 104fdf9..5417b32 100644 > --- a/drivers/bus/pci/pci_common.c > +++ b/drivers/bus/pci/pci_common.c > @@ -282,6 +282,7 @@ pci_probe_all_drivers(struct rte_pci_device *dev) > if (rc > 0) > /* positive value means driver doesn't support it */ > continue; > + dev->device.state =3D DEVICE_PROBED; > return 0; > } > return 1; > @@ -481,6 +482,7 @@ rte_pci_insert_device(struct rte_pci_device > *exist_pci_dev, > void > rte_pci_remove_device(struct rte_pci_device *pci_dev) > { > + RTE_LOG(DEBUG, EAL, " rte_pci_remove_device for device list\n"); > TAILQ_REMOVE(&rte_pci_bus.device_list, pci_dev, next); > } >=20 > @@ -502,6 +504,44 @@ pci_find_device(const struct rte_device *start, > rte_dev_cmp_t cmp, > return NULL; > } >=20 > +static struct rte_device * > +pci_find_device_by_name(const struct rte_device *start, > + rte_dev_cmp_name_t cmp_name, > + const void *data) > +{ > + struct rte_pci_device *dev; > + > + FOREACH_DEVICE_ON_PCIBUS(dev) { > + if (start && &dev->device =3D=3D start) { > + start =3D NULL; /* starting point found */ > + continue; > + } > + if (cmp_name(dev->device.name, data) =3D=3D 0) > + return &dev->device; > + } > + > + return NULL; > +} > + > +static int > +pci_remap_device(struct rte_device *dev) > +{ > + struct rte_pci_device *pdev; > + int ret; > + > + if (dev =3D=3D NULL) > + return -EINVAL; > + > + pdev =3D RTE_DEV_TO_PCI(dev); > + > + /* remap resources for devices that use igb_uio */ > + ret =3D rte_pci_remap_device(pdev); > + if (ret !=3D 0) > + RTE_LOG(ERR, EAL, "failed to remap device %s", > + dev->name); > + return ret; > +} > + > static int > pci_plug(struct rte_device *dev) > { > @@ -528,10 +568,13 @@ struct rte_pci_bus rte_pci_bus =3D { > .scan =3D rte_pci_scan, > .probe =3D rte_pci_probe, > .find_device =3D pci_find_device, > + .find_device_by_name =3D pci_find_device_by_name, > .plug =3D pci_plug, > .unplug =3D pci_unplug, > .parse =3D pci_parse, > .get_iommu_class =3D rte_pci_get_iommu_class, > + .remap_device =3D pci_remap_device, > + .bind_driver =3D rte_pci_dev_bind_driver, > }, > .device_list =3D TAILQ_HEAD_INITIALIZER(rte_pci_bus.device_list), > .driver_list =3D TAILQ_HEAD_INITIALIZER(rte_pci_bus.driver_list), > diff --git a/drivers/bus/pci/pci_common_uio.c > b/drivers/bus/pci/pci_common_uio.c > index 0671131..8cb4009 100644 > --- a/drivers/bus/pci/pci_common_uio.c > +++ b/drivers/bus/pci/pci_common_uio.c > @@ -176,6 +176,34 @@ pci_uio_unmap(struct mapped_pci_resource > *uio_res) > } > } >=20 > +/* remap the PCI resource of a PCI device in private virtual memory */ > +int > +pci_uio_remap_resource(struct rte_pci_device *dev) > +{ > + int i; > + uint64_t phaddr; > + void *map_address; > + > + /* Map all BARs */ > + for (i =3D 0; i !=3D PCI_MAX_RESOURCE; i++) { > + /* skip empty BAR */ > + phaddr =3D dev->mem_resource[i].phys_addr; > + if (phaddr =3D=3D 0) > + continue; > + map_address =3D pci_map_private_resource( > + dev->mem_resource[i].addr, 0, > + (size_t)dev->mem_resource[i].len); > + if (map_address =3D=3D MAP_FAILED) > + goto error; > + memset(map_address, 0xFF, (size_t)dev- > >mem_resource[i].len); > + dev->mem_resource[i].addr =3D map_address; > + } > + > + return 0; > +error: > + return -1; > +} > + > static struct mapped_pci_resource * > pci_uio_find_resource(struct rte_pci_device *dev) > { > diff --git a/drivers/bus/pci/private.h b/drivers/bus/pci/private.h > index 2283f09..10baa1a 100644 > --- a/drivers/bus/pci/private.h > +++ b/drivers/bus/pci/private.h > @@ -202,6 +202,18 @@ void pci_uio_free_resource(struct rte_pci_device > *dev, > struct mapped_pci_resource *uio_res); >=20 > /** > + * remap the pci uio resource.. > + * > + * @param dev > + * Point to the struct rte pci device. > + * @return > + * - On success, zero. > + * - On failure, a negative value. > + */ > +int > +pci_uio_remap_resource(struct rte_pci_device *dev); > + > +/** > * Map device memory to uio resource > * > * This function is private to EAL. > diff --git a/drivers/bus/pci/rte_bus_pci.h b/drivers/bus/pci/rte_bus_pci.= h > index d4a2996..1662f3b 100644 > --- a/drivers/bus/pci/rte_bus_pci.h > +++ b/drivers/bus/pci/rte_bus_pci.h > @@ -52,6 +52,8 @@ extern "C" { > #include > #include > #include > +#include > +#include >=20 > #include > #include > @@ -197,6 +199,15 @@ int rte_pci_map_device(struct rte_pci_device *dev); > void rte_pci_unmap_device(struct rte_pci_device *dev); >=20 > /** > + * Remap this device > + * > + * @param dev > + * A pointer to a rte_pci_device structure describing the device > + * to use > + */ > +int rte_pci_remap_device(struct rte_pci_device *dev); > + > +/** > * Dump the content of the PCI bus. > * > * @param f > @@ -333,6 +344,20 @@ void rte_pci_ioport_read(struct rte_pci_ioport *p, > void rte_pci_ioport_write(struct rte_pci_ioport *p, > const void *data, size_t len, off_t offset); >=20 > +/** > + * It can be used to bind a device to a specific type of driver. > + * > + * @param dev_name > + * The device name. > + * @param drv_type > + * The specific driver's type. > + * > + * @return > + * - On success, zero. > + * - On failure, a negative value. > + */ > +int rte_pci_dev_bind_driver(const char *dev_name, const char *drv_type); > + > #ifdef __cplusplus > } > #endif > diff --git a/drivers/bus/vdev/vdev.c b/drivers/bus/vdev/vdev.c > index fd7736d..773f6e0 100644 > --- a/drivers/bus/vdev/vdev.c > +++ b/drivers/bus/vdev/vdev.c > @@ -323,6 +323,39 @@ vdev_find_device(const struct rte_device *start, > rte_dev_cmp_t cmp, > return NULL; > } >=20 > +static struct rte_device * > +vdev_find_device_by_name(const struct rte_device *start, > + rte_dev_cmp_name_t cmp_name, > + const void *data) > +{ > + struct rte_vdev_device *dev; > + > + TAILQ_FOREACH(dev, &vdev_device_list, next) { > + if (start && &dev->device =3D=3D start) { > + start =3D NULL; > + continue; > + } > + if (cmp_name(dev->device.name, data) =3D=3D 0) > + return &dev->device; > + } > + return NULL; > +} > + > +static int > +vdev_remap_device(struct rte_device *dev) > +{ > + RTE_SET_USED(dev); > + return 0; > +} > + > +static int > +vdev_bind_driver(const char *dev_name, const char *drv_type) > +{ > + RTE_SET_USED(dev_name); > + RTE_SET_USED(drv_type); > + return 0; > +} > + > static int > vdev_plug(struct rte_device *dev) > { > @@ -339,9 +372,12 @@ static struct rte_bus rte_vdev_bus =3D { > .scan =3D vdev_scan, > .probe =3D vdev_probe, > .find_device =3D vdev_find_device, > + .find_device_by_name =3D vdev_find_device_by_name, > .plug =3D vdev_plug, > .unplug =3D vdev_unplug, > .parse =3D vdev_parse, > + .remap_device =3D vdev_remap_device, > + .bind_driver =3D vdev_bind_driver, > }; >=20 > RTE_REGISTER_BUS(vdev, rte_vdev_bus); > diff --git a/lib/librte_eal/bsdapp/eal/eal_dev.c > b/lib/librte_eal/bsdapp/eal/eal_dev.c > new file mode 100644 > index 0000000..6ea9a74 > --- /dev/null > +++ b/lib/librte_eal/bsdapp/eal/eal_dev.c > @@ -0,0 +1,64 @@ > +/*- > + * Copyright(c) 2010-2017 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 copyrig= ht > + * 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. > + */ > + > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > + > +#include > +#include > +#include > +#include > +#include > +#include > + > +#include "eal_thread.h" > + > +int > +rte_dev_monitor_start(void) > +{ > + return -1; > +} > + > +int > +rte_dev_monitor_stop(void) > +{ > + return -1; > +} > diff --git a/lib/librte_eal/bsdapp/eal/include/exec-env/rte_dev.h > b/lib/librte_eal/bsdapp/eal/include/exec-env/rte_dev.h > new file mode 100644 > index 0000000..6a6feb5 > --- /dev/null > +++ b/lib/librte_eal/bsdapp/eal/include/exec-env/rte_dev.h > @@ -0,0 +1,106 @@ > +/*- > + * BSD LICENSE > + * > + * Copyright(c) 2010-2014 Intel Corporation. All rights reserved. > + * 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 copyrig= ht > + * 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. > + */ > + > +#ifndef _RTE_DEV_H_ > +#error "don't include this file directly, please include generic " > +#endif > + > +#ifndef _RTE_LINUXAPP_DEV_H_ > +#define _RTE_LINUXAPP_DEV_H_ > + > +#include > + > +#include > + > +#define RTE_EAL_UEV_MSG_LEN 4096 > +#define RTE_EAL_UEV_MSG_ELEM_LEN 128 > + > +enum uev_subsystem { > + UEV_SUBSYSTEM_UIO, > + UEV_SUBSYSTEM_VFIO, > + UEV_SUBSYSTEM_PCI, > + UEV_SUBSYSTEM_MAX > +}; > + > +enum uev_monitor_netlink_group { > + UEV_MONITOR_KERNEL, > + UEV_MONITOR_UDEV, > +}; > + > +/** > + * The device event type. > + */ > +enum rte_eal_dev_event_type { > + RTE_EAL_DEV_EVENT_UNKNOWN, /**< unknown event type */ > + RTE_EAL_DEV_EVENT_ADD, /**< device adding event */ > + RTE_EAL_DEV_EVENT_REMOVE, > + /**< device removing event */ > + RTE_EAL_DEV_EVENT_CHANGE, > + /**< device status change event */ > + RTE_EAL_DEV_EVENT_MOVE, /**< device sys path move > event */ > + RTE_EAL_DEV_EVENT_ONLINE, /**< device online event */ > + RTE_EAL_DEV_EVENT_OFFLINE, /**< device offline event */ > + RTE_EAL_DEV_EVENT_MAX /**< max value of this enum > */ > +}; > + > +struct rte_eal_uevent { > + enum rte_eal_dev_event_type type; /**< device event type */ > + int subsystem; /**< subsystem id */ > + char *devname; /**< device name */ > + enum uev_monitor_netlink_group group; /**< device netlink > group */ > +}; > + > +/** > + * Start the device uevent monitoring. > + * > + * @param none > + * @return > + * - On success, zero. > + * - On failure, a negative value. > + */ > +int > +rte_dev_monitor_start(void); > + > +/** > + * Stop the device uevent monitoring . > + * > + * @param none > + * @return > + * - On success, zero. > + * - On failure, a negative value. > + */ > + > +int > +rte_dev_monitor_stop(void); > + > +#endif /* _RTE_LINUXAPP_DEV_H_ */ > diff --git a/lib/librte_eal/common/eal_common_bus.c > b/lib/librte_eal/common/eal_common_bus.c > index 3e022d5..b7219c9 100644 > --- a/lib/librte_eal/common/eal_common_bus.c > +++ b/lib/librte_eal/common/eal_common_bus.c > @@ -51,8 +51,11 @@ rte_bus_register(struct rte_bus *bus) > RTE_VERIFY(bus->scan); > RTE_VERIFY(bus->probe); > RTE_VERIFY(bus->find_device); > + RTE_VERIFY(bus->find_device_by_name); > /* Buses supporting driver plug also require unplug. */ > RTE_VERIFY(!bus->plug || bus->unplug); > + RTE_VERIFY(bus->remap_device); > + RTE_VERIFY(bus->bind_driver); >=20 > TAILQ_INSERT_TAIL(&rte_bus_list, bus, next); > RTE_LOG(DEBUG, EAL, "Registered [%s] bus.\n", bus->name); > @@ -170,6 +173,14 @@ cmp_rte_device(const struct rte_device *dev1, > const void *_dev2) > } >=20 > static int > +cmp_rte_device_name(const char *dev_name1, const void *_dev_name2) > +{ > + const char *dev_name2 =3D _dev_name2; > + > + return strcmp(dev_name1, dev_name2); > +} > + > +static int > bus_find_device(const struct rte_bus *bus, const void *_dev) > { > struct rte_device *dev; > @@ -178,6 +189,25 @@ bus_find_device(const struct rte_bus *bus, const > void *_dev) > return dev =3D=3D NULL; > } >=20 > +static struct rte_device * > +bus_find_device_by_name(const struct rte_bus *bus, const void > *_dev_name) > +{ > + struct rte_device *dev; > + > + dev =3D bus->find_device_by_name(NULL, cmp_rte_device_name, > _dev_name); > + return dev; > +} > + > +struct rte_device * > + > +rte_bus_find_device(const struct rte_bus *bus, const void *_dev_name) > +{ > + struct rte_device *dev; > + > + dev =3D bus_find_device_by_name(bus, _dev_name); > + return dev; > +} > + > struct rte_bus * > rte_bus_find_by_device(const struct rte_device *dev) > { > diff --git a/lib/librte_eal/common/eal_common_dev.c > b/lib/librte_eal/common/eal_common_dev.c > index dda8f58..47909e8 100644 > --- a/lib/librte_eal/common/eal_common_dev.c > +++ b/lib/librte_eal/common/eal_common_dev.c > @@ -42,9 +42,31 @@ > #include > #include > #include > +#include > +#include >=20 > #include "eal_private.h" >=20 > +/* spinlock for device callbacks */ > +static rte_spinlock_t rte_dev_cb_lock =3D RTE_SPINLOCK_INITIALIZER; > + > +/** > + * The user application callback description. > + * > + * It contains callback address to be registered by user application, > + * the pointer to the parameters for callback, and the event type. > + */ > +struct rte_eal_dev_callback { > + TAILQ_ENTRY(rte_eal_dev_callback) next; /**< Callbacks list */ > + rte_eal_dev_cb_fn cb_fn; /**< Callback address */ > + void *cb_arg; /**< Parameter for callback */ > + void *ret_param; /**< Return parameter */ > + enum rte_eal_dev_event_type event; /**< device event type */ > + uint32_t active; /**< Callback is executing */ > +}; > + > +static struct rte_eal_dev_callback *dev_add_cb; > + > static int cmp_detached_dev_name(const struct rte_device *dev, > const void *_name) > { > @@ -234,3 +256,150 @@ int rte_eal_hotplug_remove(const char *busname, > const char *devname) > rte_eal_devargs_remove(busname, devname); > return ret; > } > + > +int > +rte_eal_dev_monitor_enable(void) > +{ > + int ret; > + > + ret =3D rte_dev_monitor_start(); > + if (ret) > + RTE_LOG(ERR, EAL, "Can not init device monitor\n"); > + return ret; > +} > + > +int > +rte_dev_callback_register(struct rte_device *device, > + enum rte_eal_dev_event_type event, > + rte_eal_dev_cb_fn cb_fn, void *cb_arg) > +{ > + struct rte_eal_dev_callback *user_cb; > + > + if (!cb_fn) > + return -EINVAL; > + What's about checking the device pointer is not NULL ? > + rte_spinlock_lock(&rte_dev_cb_lock); > + > + if (TAILQ_EMPTY(&(device->uev_cbs))) > + TAILQ_INIT(&(device->uev_cbs)); > + > + if (event =3D=3D RTE_EAL_DEV_EVENT_ADD) { > + user_cb =3D NULL; > + } else { > + TAILQ_FOREACH(user_cb, &(device->uev_cbs), next) { > + if (user_cb->cb_fn =3D=3D cb_fn && > + user_cb->cb_arg =3D=3D cb_arg && > + user_cb->event =3D=3D event) { > + break; > + } > + } > + } > + > + /* create a new callback. */ > + if (user_cb =3D=3D NULL) { > + /* allocate a new interrupt callback entity */ > + user_cb =3D rte_zmalloc("eal device event", > + sizeof(*user_cb), 0); > + if (user_cb =3D=3D NULL) { > + RTE_LOG(ERR, EAL, "Can not allocate memory\n"); Missing rte_spinlock_unlock. > + return -ENOMEM; > + } > + user_cb->cb_fn =3D cb_fn; > + user_cb->cb_arg =3D cb_arg; > + user_cb->event =3D event; > + if (event =3D=3D RTE_EAL_DEV_EVENT_ADD) > + dev_add_cb =3D user_cb; Only one dpdk entity can register to ADD callback? I suggest to add option to register all devices maybe by using dummy device= which will include all the "ALL_DEVICES" callbacks per event. =20 All means past, present and future devices, by this way 1 callback can be c= alled for all the devices and more than one dpdk entity could register to = an ADD\NEW event. What's about NEW instead of ADD? I also suggest to add the device pointer as a parameter to the callback(whi= ch will be managed by EAL). > + else > + TAILQ_INSERT_TAIL(&(device->uev_cbs), user_cb, > next); > + } > + > + rte_spinlock_unlock(&rte_dev_cb_lock); > + return 0; > +} > + > +int > +rte_dev_callback_unregister(struct rte_device *device, > + enum rte_eal_dev_event_type event, > + rte_eal_dev_cb_fn cb_fn, void *cb_arg) > +{ > + int ret; > + struct rte_eal_dev_callback *cb, *next; > + > + if (!cb_fn) > + return -EINVAL; > + > + rte_spinlock_lock(&rte_dev_cb_lock); > + > + ret =3D 0; > + if (event =3D=3D RTE_EAL_DEV_EVENT_ADD) { > + rte_free(dev_add_cb); > + dev_add_cb =3D NULL; > + } else { Device NULL checking? > + for (cb =3D TAILQ_FIRST(&(device->uev_cbs)); cb !=3D NULL; > + cb =3D next) { > + > + next =3D TAILQ_NEXT(cb, next); > + > + if (cb->cb_fn !=3D cb_fn || cb->event !=3D event || > + (cb->cb_arg !=3D (void *)-1 && > + cb->cb_arg !=3D cb_arg)) > + continue; > + > + /* > + * if this callback is not executing right now, > + * then remove it. > + */ > + if (cb->active =3D=3D 0) { > + TAILQ_REMOVE(&(device->uev_cbs), cb, > next); > + rte_free(cb); > + } else { > + ret =3D -EAGAIN; > + } > + } > + } > + rte_spinlock_unlock(&rte_dev_cb_lock); > + return ret; > +} > + > +int > +_rte_dev_callback_process(struct rte_device *device, > + enum rte_eal_dev_event_type event, > + void *cb_arg, void *ret_param) > +{ > + struct rte_eal_dev_callback dev_cb; > + struct rte_eal_dev_callback *cb_lst; > + int rc =3D 0; > + > + rte_spinlock_lock(&rte_dev_cb_lock); > + if (event =3D=3D RTE_EAL_DEV_EVENT_ADD) { > + if (cb_arg !=3D NULL) > + dev_add_cb->cb_arg =3D cb_arg; > + > + if (ret_param !=3D NULL) > + dev_add_cb->ret_param =3D ret_param; > + > + rte_spinlock_unlock(&rte_dev_cb_lock); Can't someone free it when it running? I suggest to keep the lock locked. Callbacks are not allowed to use this mechanism to prevent deadlock.=20 > + rc =3D dev_add_cb->cb_fn(dev_add_cb->event, > + dev_add_cb->cb_arg, dev_add_cb- > >ret_param); > + rte_spinlock_lock(&rte_dev_cb_lock); > + } else { > + TAILQ_FOREACH(cb_lst, &(device->uev_cbs), next) { > + if (cb_lst->cb_fn =3D=3D NULL || cb_lst->event !=3D event) > + continue; > + dev_cb =3D *cb_lst; > + cb_lst->active =3D 1; > + if (cb_arg !=3D NULL) > + dev_cb.cb_arg =3D cb_arg; > + if (ret_param !=3D NULL) > + dev_cb.ret_param =3D ret_param; > + > + rte_spinlock_unlock(&rte_dev_cb_lock); The current active flag doesn't do it thread safe here, I suggest to keep = the lock locked. Scenario: 1. Thread A see active =3D 0 in unregister function. 2. Context switch. 3. Thread B start the callback. 4. Context switch. 5. Thread A free it. 6. Context switch. 7. Seg fault in Thread B. > + rc =3D dev_cb.cb_fn(dev_cb.event, > + dev_cb.cb_arg, dev_cb.ret_param); > + rte_spinlock_lock(&rte_dev_cb_lock); > + cb_lst->active =3D 0; > + } > + } > + rte_spinlock_unlock(&rte_dev_cb_lock); > + return rc; > +} > diff --git a/lib/librte_eal/common/include/rte_bus.h > b/lib/librte_eal/common/include/rte_bus.h > index 6fb0834..6c4ae31 100644 > --- a/lib/librte_eal/common/include/rte_bus.h > +++ b/lib/librte_eal/common/include/rte_bus.h > @@ -122,6 +122,34 @@ typedef struct rte_device * > const void *data); >=20 > /** > + * Device iterator to find a device on a bus. > + * > + * This function returns an rte_device if one of those held by the bus > + * matches the data passed as parameter. > + * > + * If the comparison function returns zero this function should stop ite= rating > + * over any more devices. To continue a search the device of a previous > search > + * can be passed via the start parameter. > + * > + * @param cmp > + * the device name comparison function. > + * > + * @param data > + * Data to compare each device against. > + * > + * @param start > + * starting point for the iteration > + * > + * @return > + * The first device matching the data, NULL if none exists. > + */ > +typedef struct rte_device * > +(*rte_bus_find_device_by_name_t)(const struct rte_device *start, > + rte_dev_cmp_name_t cmp, > + const void *data); > + > + > +/** > * Implementation specific probe function which is responsible for linki= ng > * devices on that bus with applicable drivers. > * > @@ -168,6 +196,37 @@ typedef int (*rte_bus_unplug_t)(struct rte_device > *dev); > typedef int (*rte_bus_parse_t)(const char *name, void *addr); >=20 > /** > + * Implementation specific remap function which is responsible for > remmaping > + * devices on that bus from original share memory resource to a private > memory > + * resource for the sake of device has been removal. > + * > + * @param dev > + * Device pointer that was returned by a previous call to find_device. > + * > + * @return > + * 0 on success. > + * !0 on error. > + */ > +typedef int (*rte_bus_remap_device_t)(struct rte_device *dev); > + > +/** > + * Implementation specific bind driver function which is responsible for= bind > + * a explicit type of driver with a devices on that bus. > + * > + * @param dev_name > + * device textual description. > + * > + * @param drv_type > + * driver type textual description. > + * > + * @return > + * 0 on success. > + * !0 on error. > + */ > +typedef int (*rte_bus_bind_driver_t)(const char *dev_name, > + const char *drv_type); > + > +/** > * Bus scan policies > */ > enum rte_bus_scan_mode { > @@ -206,9 +265,13 @@ struct rte_bus { > rte_bus_scan_t scan; /**< Scan for devices attached to bus */ > rte_bus_probe_t probe; /**< Probe devices on bus */ > rte_bus_find_device_t find_device; /**< Find a device on the bus */ > + rte_bus_find_device_by_name_t find_device_by_name; > + /**< Find a device on the bus */ > rte_bus_plug_t plug; /**< Probe single device for drivers */ > rte_bus_unplug_t unplug; /**< Remove single device from driver > */ > rte_bus_parse_t parse; /**< Parse a device name */ > + rte_bus_remap_device_t remap_device; /**< remap a device */ > + rte_bus_bind_driver_t bind_driver; /**< bind a driver for bus device > */ > struct rte_bus_conf conf; /**< Bus configuration */ > rte_bus_get_iommu_class_t get_iommu_class; /**< Get iommu > class */ > }; > @@ -306,6 +369,12 @@ struct rte_bus *rte_bus_find(const struct rte_bus > *start, rte_bus_cmp_t cmp, > struct rte_bus *rte_bus_find_by_device(const struct rte_device *dev); >=20 > /** > + * Find the registered bus for a particular device. > + */ > +struct rte_device *rte_bus_find_device(const struct rte_bus *bus, > + const void *dev_name); > + > +/** > * Find the registered bus for a given name. > */ > struct rte_bus *rte_bus_find_by_name(const char *busname); > diff --git a/lib/librte_eal/common/include/rte_dev.h > b/lib/librte_eal/common/include/rte_dev.h > index 9342e0c..19971d0 100644 > --- a/lib/librte_eal/common/include/rte_dev.h > +++ b/lib/librte_eal/common/include/rte_dev.h > @@ -51,6 +51,15 @@ extern "C" { >=20 > #include >=20 > +#include > + > +typedef int (*rte_eal_dev_cb_fn)(enum rte_eal_dev_event_type event, > + void *cb_arg, void *ret_param); > + > +struct rte_eal_dev_callback; > +/** @internal Structure to keep track of registered callbacks */ > +TAILQ_HEAD(rte_eal_dev_cb_list, rte_eal_dev_callback); > + > __attribute__((format(printf, 2, 0))) > static inline void > rte_pmd_debug_trace(const char *func_name, const char *fmt, ...) > @@ -157,6 +166,13 @@ struct rte_driver { > */ > #define RTE_DEV_NAME_MAX_LEN 64 >=20 > +enum device_state { > + DEVICE_UNDEFINED, > + DEVICE_FAULT, > + DEVICE_PARSED, > + DEVICE_PROBED, > +}; > + > /** > * A structure describing a generic device. > */ > @@ -166,6 +182,9 @@ struct rte_device { > const struct rte_driver *driver;/**< Associated driver */ > int numa_node; /**< NUMA node connection */ > struct rte_devargs *devargs; /**< Device user arguments */ > + enum device_state state; /**< Device state */ > + /** User application callbacks for device event */ > + struct rte_eal_dev_cb_list uev_cbs; > }; >=20 > /** > @@ -248,6 +267,8 @@ int rte_eal_hotplug_remove(const char *busname, > const char *devname); > */ > typedef int (*rte_dev_cmp_t)(const struct rte_device *dev, const void > *data); >=20 > +typedef int (*rte_dev_cmp_name_t)(const char *dev_name, const void > *data); > + > #define RTE_PMD_EXPORT_NAME_ARRAY(n, idx) n##idx[] >=20 > #define RTE_PMD_EXPORT_NAME(name, idx) \ > @@ -293,4 +314,72 @@ __attribute__((used)) =3D str > } > #endif >=20 > +/** > + * It enable the device event monitoring for a specific event. > + * > + * @param none > + * @return > + * - On success, zero. > + * - On failure, a negative value. > + */ > +int > +rte_eal_dev_monitor_enable(void); > +/** > + * It registers the callback for the specific event. Multiple > + * callbacks cal be registered at the same time. > + * @param event > + * The device event type. > + * @param cb_fn > + * callback address. > + * @param cb_arg > + * address of parameter for callback. > + * > + * @return > + * - On success, zero. > + * - On failure, a negative value. > + */ > +int rte_dev_callback_register(struct rte_device *device, > + enum rte_eal_dev_event_type event, > + rte_eal_dev_cb_fn cb_fn, void *cb_arg); > + > +/** > + * It unregisters the callback according to the specified event. > + * > + * @param event > + * The event type which corresponding to the callback. > + * @param cb_fn > + * callback address. > + * address of parameter for callback, (void *)-1 means to remove all > + * registered which has the same callback address. > + * > + * @return > + * - On success, return the number of callback entities removed. > + * - On failure, a negative value. > + */ > +int rte_dev_callback_unregister(struct rte_device *device, > + enum rte_eal_dev_event_type event, > + rte_eal_dev_cb_fn cb_fn, void *cb_arg); > + > +/** > + * @internal Executes all the user application registered callbacks for > + * the specific device. It is for DPDK internal user only. User > + * application should not call it directly. > + * > + * @param event > + * The device event type. > + * @param cb_arg > + * callback parameter. > + * @param ret_param > + * To pass data back to user application. > + * This allows the user application to decide if a particular function > + * is permitted or not. > + * > + * @return > + * - On success, return zero. > + * - On failure, a negative value. > + */ > +int > +_rte_dev_callback_process(struct rte_device *device, > + enum rte_eal_dev_event_type event, > + void *cb_arg, void *ret_param); > #endif /* _RTE_DEV_H_ */ > diff --git a/lib/librte_eal/linuxapp/eal/Makefile > b/lib/librte_eal/linuxapp/eal/Makefile > index 5a7b8b2..05a2437 100644 > --- a/lib/librte_eal/linuxapp/eal/Makefile > +++ b/lib/librte_eal/linuxapp/eal/Makefile > @@ -67,6 +67,7 @@ SRCS-$(CONFIG_RTE_EXEC_ENV_LINUXAPP) +=3D > eal_lcore.c > SRCS-$(CONFIG_RTE_EXEC_ENV_LINUXAPP) +=3D eal_timer.c > SRCS-$(CONFIG_RTE_EXEC_ENV_LINUXAPP) +=3D eal_interrupts.c > SRCS-$(CONFIG_RTE_EXEC_ENV_LINUXAPP) +=3D eal_alarm.c > +SRCS-$(CONFIG_RTE_EXEC_ENV_LINUXAPP) +=3D eal_dev.c >=20 > # from common dir > SRCS-$(CONFIG_RTE_EXEC_ENV_LINUXAPP) +=3D eal_common_lcore.c > @@ -120,7 +121,7 @@ ifeq ($(CONFIG_RTE_TOOLCHAIN_GCC),y) > CFLAGS_eal_thread.o +=3D -Wno-return-type > endif >=20 > -INC :=3D rte_kni_common.h > +INC :=3D rte_kni_common.h rte_dev.h >=20 > SYMLINK-$(CONFIG_RTE_EXEC_ENV_LINUXAPP)-include/exec-env :=3D \ > $(addprefix include/exec-env/,$(INC)) > diff --git a/lib/librte_eal/linuxapp/eal/eal_alarm.c > b/lib/librte_eal/linuxapp/eal/eal_alarm.c > index 8e4a775..29e73a7 100644 > --- a/lib/librte_eal/linuxapp/eal/eal_alarm.c > +++ b/lib/librte_eal/linuxapp/eal/eal_alarm.c > @@ -209,6 +209,7 @@ rte_eal_alarm_cancel(rte_eal_alarm_callback cb_fn, > void *cb_arg) > int count =3D 0; > int err =3D 0; > int executing; > + int ret; >=20 > if (!cb_fn) { > rte_errno =3D EINVAL; > @@ -259,6 +260,10 @@ rte_eal_alarm_cancel(rte_eal_alarm_callback cb_fn, > void *cb_arg) > } > ap_prev =3D ap; > } > + > + ret |=3D rte_intr_callback_unregister(&intr_handle, > + eal_alarm_callback, NULL); > + > rte_spinlock_unlock(&alarm_list_lk); > } while (executing !=3D 0); >=20 > diff --git a/lib/librte_eal/linuxapp/eal/eal_dev.c > b/lib/librte_eal/linuxapp/eal/eal_dev.c > new file mode 100644 > index 0000000..49fd0dc > --- /dev/null > +++ b/lib/librte_eal/linuxapp/eal/eal_dev.c > @@ -0,0 +1,356 @@ > +/*- > + * Copyright(c) 2010-2017 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 copyrig= ht > + * 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. > + */ > + > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > + > +#include > +#include > +#include > +#include > +#include > +#include > + > +#include "eal_thread.h" > + > +/* uev monitoring thread */ > +static pthread_t uev_monitor_thread; > + > +bool udev_exit =3D true; > + > +bool no_request_thread =3D true; > + > +static void sig_handler(int signum) > +{ > + if (signum =3D=3D SIGINT || signum =3D=3D SIGTERM) > + rte_dev_monitor_stop(); > +} > + > +static int > +dev_monitor_fd_new(void) > +{ > + > + int uevent_fd; > + > + uevent_fd =3D socket(PF_NETLINK, SOCK_RAW | SOCK_CLOEXEC | > + SOCK_NONBLOCK, > + NETLINK_KOBJECT_UEVENT); > + if (uevent_fd < 0) { > + RTE_LOG(ERR, EAL, "create uevent fd failed\n"); > + return -1; > + } > + return uevent_fd; > +} > + > +static int > +dev_monitor_enable(int netlink_fd) > +{ > + struct sockaddr_nl addr; > + int ret; > + int size =3D 64 * 1024; > + int nonblock =3D 1; > + > + memset(&addr, 0, sizeof(addr)); > + addr.nl_family =3D AF_NETLINK; > + addr.nl_pid =3D 0; > + addr.nl_groups =3D 0xffffffff; > + > + if (bind(netlink_fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) { > + RTE_LOG(ERR, EAL, "bind failed\n"); > + goto err; > + } > + > + setsockopt(netlink_fd, SOL_SOCKET, SO_PASSCRED, &size, > sizeof(size)); > + > + ret =3D ioctl(netlink_fd, FIONBIO, &nonblock); > + if (ret !=3D 0) { > + RTE_LOG(ERR, EAL, "ioctl(FIONBIO) failed\n"); > + goto err; > + } > + return 0; > +err: > + close(netlink_fd); > + return -1; > +} > + > +static void > +dev_uev_parse(const char *buf, struct rte_eal_uevent *event) > +{ > + char action[RTE_EAL_UEV_MSG_ELEM_LEN]; > + char subsystem[RTE_EAL_UEV_MSG_ELEM_LEN]; > + char dev_path[RTE_EAL_UEV_MSG_ELEM_LEN]; > + char pci_slot_name[RTE_EAL_UEV_MSG_ELEM_LEN]; > + int i =3D 0; > + > + memset(action, 0, RTE_EAL_UEV_MSG_ELEM_LEN); > + memset(subsystem, 0, RTE_EAL_UEV_MSG_ELEM_LEN); > + memset(dev_path, 0, RTE_EAL_UEV_MSG_ELEM_LEN); > + memset(pci_slot_name, 0, RTE_EAL_UEV_MSG_ELEM_LEN); > + > + while (i < RTE_EAL_UEV_MSG_LEN) { > + for (; i < RTE_EAL_UEV_MSG_LEN; i++) { > + if (*buf) > + break; > + buf++; > + } > + if (!strncmp(buf, "libudev", 7)) { > + buf +=3D 7; > + i +=3D 7; > + event->group =3D UEV_MONITOR_UDEV; > + } > + if (!strncmp(buf, "ACTION=3D", 7)) { > + buf +=3D 7; > + i +=3D 7; > + snprintf(action, sizeof(action), "%s", buf); > + } else if (!strncmp(buf, "DEVPATH=3D", 8)) { > + buf +=3D 8; > + i +=3D 8; > + snprintf(dev_path, sizeof(dev_path), "%s", buf); > + } else if (!strncmp(buf, "SUBSYSTEM=3D", 10)) { > + buf +=3D 10; > + i +=3D 10; > + snprintf(subsystem, sizeof(subsystem), "%s", buf); > + } else if (!strncmp(buf, "PCI_SLOT_NAME=3D", 14)) { > + buf +=3D 14; > + i +=3D 14; > + snprintf(pci_slot_name, sizeof(subsystem), "%s", > buf); > + event->devname =3D pci_slot_name; > + } > + for (; i < RTE_EAL_UEV_MSG_LEN; i++) { > + if (*buf =3D=3D '\0') > + break; > + buf++; > + } > + } > + > + if (!strncmp(subsystem, "pci", 3)) > + event->subsystem =3D UEV_SUBSYSTEM_PCI; > + if (!strncmp(action, "add", 3)) > + event->type =3D RTE_EAL_DEV_EVENT_ADD; > + if (!strncmp(action, "remove", 6)) > + event->type =3D RTE_EAL_DEV_EVENT_REMOVE; > + event->devname =3D pci_slot_name; > +} > + > +static int > +dev_uev_receive(int fd, struct rte_eal_uevent *uevent) > +{ > + int ret; > + char buf[RTE_EAL_UEV_MSG_LEN]; > + > + memset(uevent, 0, sizeof(struct rte_eal_uevent)); > + memset(buf, 0, RTE_EAL_UEV_MSG_LEN); > + > + ret =3D recv(fd, buf, RTE_EAL_UEV_MSG_LEN - 1, MSG_DONTWAIT); > + if (ret < 0) { > + RTE_LOG(ERR, EAL, > + "Socket read error(%d): %s\n", > + errno, strerror(errno)); > + return -1; > + } else if (ret =3D=3D 0) > + /* connection closed */ > + return -1; > + > + dev_uev_parse(buf, uevent); > + > + return 0; > +} > + > +static int > +dev_uev_process(struct epoll_event *events, int nfds) > +{ > + struct rte_bus *bus; > + struct rte_device *dev; > + struct rte_eal_uevent uevent; > + int ret; > + int i; > + > + for (i =3D 0; i < nfds; i++) { > + /** > + * check device uevent from kernel side, no need to check > + * uevent from udev. > + */ > + if ((dev_uev_receive(events[i].data.fd, &uevent)) || > + (uevent.group =3D=3D UEV_MONITOR_UDEV)) > + return 0; > + > + /* default handle all pci devcie when is being hot plug */ > + if (uevent.subsystem =3D=3D UEV_SUBSYSTEM_PCI) { > + bus =3D rte_bus_find_by_name("pci"); > + dev =3D rte_bus_find_device(bus, uevent.devname); > + if (uevent.type =3D=3D RTE_EAL_DEV_EVENT_REMOVE) { > + > + if ((!dev) || dev->state =3D=3D > DEVICE_UNDEFINED) > + return 0; > + dev->state =3D DEVICE_FAULT; > + > + /** > + * remap the resource to be fake > + * before user's removal processing > + */ > + ret =3D bus->remap_device(dev); > + if (!ret) > + > return(_rte_dev_callback_process(dev, > + RTE_EAL_DEV_EVENT_REMOVE, > + NULL, NULL)); What is the reason to keep this device in EAL device list after the removal= ? I suggest to remove it (driver remove, bus remove and EAL remove) after the= callbacks running. By this way EAL can initiate all device removals. > + } else if (uevent.type =3D=3D RTE_EAL_DEV_EVENT_ADD) > { > + if (dev =3D=3D NULL) { > + /** > + * bind the driver to the device > + * before user's add processing > + */ > + bus->bind_driver( > + uevent.devname, > + "igb_uio"); > + Similar comments here: EAL can initiate all device probe operations by adding the device and probi= ng it here before the callback running. Then, also the device pointer can be passed to the callbacks. > return(_rte_dev_callback_process(NULL, > + RTE_EAL_DEV_EVENT_ADD, > + uevent.devname, NULL)); > + } > + } > + } > + } > + return 0; > +} > + > +/** > + * It builds/rebuilds up the epoll file descriptor with all the > + * file descriptors being waited on. Then handles the interrupts. > + * > + * @param arg > + * pointer. (unused) > + * > + * @return > + * never return; > + */ > +static __attribute__((noreturn)) void * > +dev_uev_monitoring(__rte_unused void *arg) > +{ > + struct sigaction act; > + sigset_t mask; > + int netlink_fd; > + struct epoll_event ep_kernel; > + int fd_ep; > + > + udev_exit =3D false; > + > + /* set signal handlers */ > + memset(&act, 0x00, sizeof(struct sigaction)); > + act.sa_handler =3D sig_handler; > + sigemptyset(&act.sa_mask); > + act.sa_flags =3D SA_RESTART; > + sigaction(SIGINT, &act, NULL); > + sigaction(SIGTERM, &act, NULL); > + sigemptyset(&mask); > + sigaddset(&mask, SIGINT); > + sigaddset(&mask, SIGTERM); > + sigprocmask(SIG_UNBLOCK, &mask, NULL); > + > + fd_ep =3D epoll_create1(EPOLL_CLOEXEC); > + if (fd_ep < 0) { > + RTE_LOG(ERR, EAL, "error creating epoll fd: %m\n"); > + goto out; > + } > + > + netlink_fd =3D dev_monitor_fd_new(); > + > + if (dev_monitor_enable(netlink_fd) < 0) { > + RTE_LOG(ERR, EAL, "error subscribing to kernel events\n"); > + goto out; > + } > + > + memset(&ep_kernel, 0, sizeof(struct epoll_event)); > + ep_kernel.events =3D EPOLLIN | EPOLLPRI | EPOLLRDHUP | EPOLLHUP; > + ep_kernel.data.fd =3D netlink_fd; > + if (epoll_ctl(fd_ep, EPOLL_CTL_ADD, netlink_fd, > + &ep_kernel) < 0) { > + RTE_LOG(ERR, EAL, "error addding fd to epoll: %m\n"); > + goto out; > + } > + > + while (!udev_exit) { > + int fdcount; > + struct epoll_event ev[1]; > + > + fdcount =3D epoll_wait(fd_ep, ev, 1, -1); > + if (fdcount < 0) { > + if (errno !=3D EINTR) > + RTE_LOG(ERR, EAL, "error receiving uevent " > + "message: %m\n"); > + continue; > + } > + > + /* epoll_wait has at least one fd ready to read */ > + if (dev_uev_process(ev, fdcount) < 0) { > + if (errno !=3D EINTR) > + RTE_LOG(ERR, EAL, "error processing uevent > " > + "message: %m\n"); > + } > + } > +out: > + if (fd_ep >=3D 0) > + close(fd_ep); > + if (netlink_fd >=3D 0) > + close(netlink_fd); > + rte_panic("uev monitoring fail\n"); > +} > + > +int > +rte_dev_monitor_start(void) > +{ Maybe add option to run it also by new EAL command line parameter? > + int ret; > + > + if (!no_request_thread) > + return 0; > + no_request_thread =3D false; > + > + /* create the host thread to wait/handle the uevent from kernel */ > + ret =3D pthread_create(&uev_monitor_thread, NULL, > + dev_uev_monitoring, NULL); What is the reason to open new thread for hotplug? Why not to use the current dpdk host thread by the alarm mechanism?=20 > + return ret; > +} > + > +int > +rte_dev_monitor_stop(void) > +{ > + udev_exit =3D true; > + no_request_thread =3D true; > + return 0; > +} > diff --git a/lib/librte_eal/linuxapp/eal/include/exec-env/rte_dev.h > b/lib/librte_eal/linuxapp/eal/include/exec-env/rte_dev.h > new file mode 100644 > index 0000000..6a6feb5 > --- /dev/null > +++ b/lib/librte_eal/linuxapp/eal/include/exec-env/rte_dev.h > @@ -0,0 +1,106 @@ > +/*- > + * BSD LICENSE > + * > + * Copyright(c) 2010-2014 Intel Corporation. All rights reserved. > + * 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 copyrig= ht > + * 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. > + */ > + > +#ifndef _RTE_DEV_H_ > +#error "don't include this file directly, please include generic " > +#endif > + > +#ifndef _RTE_LINUXAPP_DEV_H_ > +#define _RTE_LINUXAPP_DEV_H_ > + > +#include > + > +#include > + > +#define RTE_EAL_UEV_MSG_LEN 4096 > +#define RTE_EAL_UEV_MSG_ELEM_LEN 128 > + > +enum uev_subsystem { > + UEV_SUBSYSTEM_UIO, > + UEV_SUBSYSTEM_VFIO, > + UEV_SUBSYSTEM_PCI, > + UEV_SUBSYSTEM_MAX > +}; > + > +enum uev_monitor_netlink_group { > + UEV_MONITOR_KERNEL, > + UEV_MONITOR_UDEV, > +}; > + > +/** > + * The device event type. > + */ > +enum rte_eal_dev_event_type { > + RTE_EAL_DEV_EVENT_UNKNOWN, /**< unknown event type */ > + RTE_EAL_DEV_EVENT_ADD, /**< device adding event */ > + RTE_EAL_DEV_EVENT_REMOVE, > + /**< device removing event */ > + RTE_EAL_DEV_EVENT_CHANGE, > + /**< device status change event */ > + RTE_EAL_DEV_EVENT_MOVE, /**< device sys path move > event */ > + RTE_EAL_DEV_EVENT_ONLINE, /**< device online event */ > + RTE_EAL_DEV_EVENT_OFFLINE, /**< device offline event */ > + RTE_EAL_DEV_EVENT_MAX /**< max value of this enum > */ > +}; > + > +struct rte_eal_uevent { > + enum rte_eal_dev_event_type type; /**< device event type */ > + int subsystem; /**< subsystem id */ > + char *devname; /**< device name */ > + enum uev_monitor_netlink_group group; /**< device netlink > group */ > +}; > + > +/** > + * Start the device uevent monitoring. > + * > + * @param none > + * @return > + * - On success, zero. > + * - On failure, a negative value. > + */ > +int > +rte_dev_monitor_start(void); > + > +/** > + * Stop the device uevent monitoring . > + * > + * @param none > + * @return > + * - On success, zero. > + * - On failure, a negative value. > + */ > + > +int > +rte_dev_monitor_stop(void); > + > +#endif /* _RTE_LINUXAPP_DEV_H_ */ > diff --git a/lib/librte_eal/linuxapp/igb_uio/igb_uio.c > b/lib/librte_eal/linuxapp/igb_uio/igb_uio.c > index a3a98c1..d0e07b4 100644 > --- a/lib/librte_eal/linuxapp/igb_uio/igb_uio.c > +++ b/lib/librte_eal/linuxapp/igb_uio/igb_uio.c > @@ -354,6 +354,12 @@ igbuio_pci_release(struct uio_info *info, struct > inode *inode) > struct rte_uio_pci_dev *udev =3D info->priv; > struct pci_dev *dev =3D udev->pdev; >=20 > + /* check if device have been remove before release */ > + if ((&dev->dev.kobj)->state_remove_uevent_sent =3D=3D 1) { > + pr_info("The device have been removed\n"); > + return -1; > + } > + > /* disable interrupts */ > igbuio_pci_disable_interrupts(udev); >=20 > diff --git a/lib/librte_pci/rte_pci.c b/lib/librte_pci/rte_pci.c > index 0160fc1..feb5fd7 100644 > --- a/lib/librte_pci/rte_pci.c > +++ b/lib/librte_pci/rte_pci.c > @@ -172,6 +172,26 @@ rte_pci_addr_parse(const char *str, struct > rte_pci_addr *addr) > return -1; > } >=20 > +/* map a private resource from an address*/ > +void * > +pci_map_private_resource(void *requested_addr, off_t offset, size_t size= ) > +{ > + void *mapaddr; > + > + mapaddr =3D mmap(requested_addr, size, > + PROT_READ | PROT_WRITE, > + MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, > -1, 0); > + if (mapaddr =3D=3D MAP_FAILED) { > + RTE_LOG(ERR, EAL, "%s(): cannot mmap(%p, 0x%lx, 0x%lx): " > + "%s (%p)\n", > + __func__, requested_addr, > + (unsigned long)size, (unsigned long)offset, > + strerror(errno), mapaddr); > + } else > + RTE_LOG(DEBUG, EAL, " PCI memory mapped at %p\n", > mapaddr); > + > + return mapaddr; > +} >=20 > /* map a particular resource from a file */ > void * > diff --git a/lib/librte_pci/rte_pci.h b/lib/librte_pci/rte_pci.h > index 4f2cd18..f6091a6 100644 > --- a/lib/librte_pci/rte_pci.h > +++ b/lib/librte_pci/rte_pci.h > @@ -227,6 +227,23 @@ int rte_pci_addr_cmp(const struct rte_pci_addr > *addr, > int rte_pci_addr_parse(const char *str, struct rte_pci_addr *addr); >=20 > /** > + * @internal > + * Map to a particular private resource. > + * > + * @param requested_addr > + * The starting address for the new mapping range. > + * @param offset > + * The offset for the mapping range. > + * @param size > + * The size for the mapping range. > + * @return > + * - On success, the function returns a pointer to the mapped area. > + * - On error, the value MAP_FAILED is returned. > + */ > +void *pci_map_private_resource(void *requested_addr, off_t offset, > + size_t size); > + > +/** > * Map a particular resource from a file. > * > * @param requested_addr > -- > 2.7.4