From mboxrd@z Thu Jan 1 00:00:00 1970 From: Shreyansh Jain Subject: [PATCH 04/13] eal/bus: add scan and match support Date: Sun, 4 Dec 2016 15:41:19 +0530 Message-ID: <1480846288-2517-5-git-send-email-shreyansh.jain@nxp.com> References: <1480846288-2517-1-git-send-email-shreyansh.jain@nxp.com> Mime-Version: 1.0 Content-Type: text/plain Cc: , , Shreyansh Jain To: Return-path: Received: from NAM03-CO1-obe.outbound.protection.outlook.com (mail-co1nam03on0055.outbound.protection.outlook.com [104.47.40.55]) by dpdk.org (Postfix) with ESMTP id 066612BF6 for ; Sun, 4 Dec 2016 11:09:16 +0100 (CET) In-Reply-To: <1480846288-2517-1-git-send-email-shreyansh.jain@nxp.com> List-Id: DPDK patches and discussions List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: dev-bounces@dpdk.org Sender: "dev" When a PMD is registred, it will associate itself with a bus. A bus is responsible for 'scan' of all the devices attached to it. All the scanned devices are attached to bus specific device_list. During the probe operation, 'match' of the drivers and devices would be done. This patch also adds necessary test framework to test the scan and match callbacks. Signed-off-by: Shreyansh Jain --- app/test/test_bus.c | 265 ++++++++++++++++++++++++++++++++ lib/librte_eal/common/eal_common_bus.c | 3 + lib/librte_eal/common/include/rte_bus.h | 46 ++++++ 3 files changed, 314 insertions(+) diff --git a/app/test/test_bus.c b/app/test/test_bus.c index 760d40a..ed95479 100644 --- a/app/test/test_bus.c +++ b/app/test/test_bus.c @@ -80,12 +80,32 @@ struct dummy_bus { struct rte_bus_list orig_bus_list = TAILQ_HEAD_INITIALIZER(orig_bus_list); +/* Forward declarations for callbacks from bus */ + +/* Bus A + * Scan would register devA1 and devA2 to bus + */ +int scan_fn_for_busA(struct rte_bus *bus); + +/* Bus B + * Scan would register devB1 and devB2 to bus + */ +int scan_fn_for_busB(struct rte_bus *bus); + +/* generic implementations wrapped around by above declarations */ +static int generic_scan_fn(struct rte_bus *bus); +static int generic_match_fn(struct rte_driver *drv, struct rte_device *dev); + struct rte_bus busA = { .name = "busA", /* "busA" */ + .scan = scan_fn_for_busA, + .match = generic_match_fn, }; struct rte_bus busB = { .name = "busB", /* "busB */ + .scan = scan_fn_for_busB, + .match = generic_match_fn, }; struct rte_driver driverA = { @@ -184,6 +204,92 @@ dump_device_tree(void) printf("------>8-------\n"); } +/* @internal + * Move over the dummy_buses and find the entry matching the bus object + * passed as argument. + * For each device in that dummy_buses list, register. + * + * @param bus + * bus to scan againt test entry + * @return + * 0 for successful scan, even if no devices are found + * !0 for any error in scanning (like, invalid bus) + */ +static int +generic_scan_fn(struct rte_bus *bus) +{ + int i = 0; + struct rte_device *dev = NULL; + struct dummy_bus *db = NULL; + + if (!bus) + return -1; + + /* Extract the device tree node using the bus passed */ + for (i = 0; dummy_buses[i].name; i++) { + if (!strcmp(dummy_buses[i].name, bus->name)) { + db = &dummy_buses[i]; + break; + } + } + + if (!db) + return -1; + + /* For all the devices in the device tree (dummy_buses), add device */ + for (i = 0; db->devices[i]; i++) { + dev = &(db->devices[i]->dev); + rte_eal_bus_add_device(bus, dev); + } + + return 0; +} + +/* @internal + * Obtain bus from driver object. Match the address of rte_device object + * with all the devices associated with that bus. + * + * Being a test function, all this does is validate that device object + * provided is available on the same bus to which driver is registered. + * + * @param drv + * driver to which matching is to be performed + * @param dev + * device object to match with driver + * @return + * 0 for successful match + * !0 for failed match + */ +static int +generic_match_fn(struct rte_driver *drv, struct rte_device *dev) +{ + struct rte_bus *bus; + struct rte_device *dev_p = NULL; + + /* Match is based entirely on address of 'dev' and 'dev_p' extracted + * from bus->device_list. + */ + + /* a driver is registered with the bus *before* the scan. */ + bus = drv->bus; + TAILQ_FOREACH(dev_p, &bus->device_list, next) { + if (dev == dev_p) + return 0; + } + + return 1; +} + +int +scan_fn_for_busA(struct rte_bus *bus) { + return generic_scan_fn(bus); +} + +int +scan_fn_for_busB(struct rte_bus *bus) { + return generic_scan_fn(bus); +} + static int test_bus_setup(void) { @@ -391,6 +497,155 @@ test_driver_unregistration_on_bus(void) } +static int +test_device_unregistration_on_bus(void) +{ + int i; + struct rte_bus *bus = NULL; + struct rte_device *dev; + + for (i = 0; dummy_buses[i].name; i++) { + bus = rte_eal_get_bus(dummy_buses[i].name); + if (!bus) { + printf("Unable to find bus (%s)\n", + dummy_buses[i].name); + return -1; + } + + /* For bus 'bus', unregister all devices */ + TAILQ_FOREACH(dev, &bus->device_list, next) { + rte_eal_bus_remove_device(dev); + } + } + + for (i = 0; dummy_buses[i].name; i++) { + bus = rte_eal_get_bus(dummy_buses[i].name); + + if (!TAILQ_EMPTY(&bus->device_list)) { + printf("Unable to remove all devices on bus (%s)\n", + bus->name); + return -1; + } + } + + /* All devices from all buses have been removed */ + printf("All devices on all buses unregistered.\n"); + dump_device_tree(); + + return 0; +} + +/* @internal + * For each bus registered, call the scan function to identify devices + * on the bus. + * + * @param void + * @return + * 0 for successful scan + * !0 for unsuccessful scan + * + */ +static int +test_bus_scan(void) +{ + int ret; + struct rte_bus *bus; + + TAILQ_FOREACH(bus, &rte_bus_list, next) { + /* Call the scan function for each bus */ + ret = bus->scan(bus); + if (ret) { + printf("Scan of buses failed.\n"); + return -1; + } + } + + printf("Scan of all buses completed.\n"); + dump_device_tree(); + + return 0; +} + +/* @internal + * Function to perform 'probe' and link devices and drivers on a bus. + * This would work over all the buses registered, and all devices and drivers + * registered with it - call match on each pair. + * Aim is to test the match_fn for each bus. + * + * @param void + * @return + * 0 for successful probe + * !0 for failure in probe + * + */ +static int +test_probe_on_bus(void) +{ + int ret = 0; + int i, j; + struct rte_bus *bus = NULL; + struct rte_device *dev = NULL; + struct rte_driver *drv = NULL; + + /* In case of this test: + * 1. for each bus in rte_bus_list + * 2. for each device in bus->device_list + * 3. for each driver in bus->driver_list + * 4. call match + * 5. link driver and device + * 6. Verify the linkage. + */ + for (i = 0; dummy_buses[i].name; i++) { + /* get bus pointer from dummy_buses itself rather than + * rte_eal_get_bus + */ + bus = dummy_buses[i].bus; + + TAILQ_FOREACH(dev, &bus->device_list, next) { + TAILQ_FOREACH(drv, &bus->driver_list, next) { + if (!bus->match) { + printf("Incorrect bus without match " + "fn: (%s).\n", bus->name); + return -1; + } + + ret = bus->match(drv, dev); + if (ret) { + printf("Device and driver don't " + "belong to same bus.\n"); + return -1; + } + dev->driver = drv; + + /* As match is generic, it always results in + * dev->drv pointing to first driver entry in + * dummy_buses[i] + */ + } + } + } + + /* Verify the linkage. All devices belonging to a dummy_buses[i] + * should have same driver (first driver entry of dummy_buses[i]) + */ + for (i = 0; dummy_buses[i].name; i++) { + drv = dummy_buses[i].drivers[0]; + + for (j = 0; dummy_buses[i].devices[j]; j++) { + dev = &(dummy_buses[i].devices[j]->dev); + if (dev->driver != drv) { + printf("Incorrect driver<->device linkage.\n"); + return -1; + } + } + } + + printf("Probe on all buses successful.\n"); + dump_device_tree(); + + return 0; +} + int test_bus(void) { @@ -407,6 +662,16 @@ test_bus(void) if (test_driver_registration_on_bus()) return -1; + if (test_bus_scan()) + return -1; + + /* Now that the devices and drivers are registered, perform probe */ + if (test_probe_on_bus()) + return -1; + + if (test_device_unregistration_on_bus()) + return -1; + if (test_driver_unregistration_on_bus()) return -1; diff --git a/lib/librte_eal/common/eal_common_bus.c b/lib/librte_eal/common/eal_common_bus.c index 612f64e..2473fe4 100644 --- a/lib/librte_eal/common/eal_common_bus.c +++ b/lib/librte_eal/common/eal_common_bus.c @@ -132,6 +132,9 @@ rte_eal_bus_register(struct rte_bus *bus) { RTE_VERIFY(bus); RTE_VERIFY(bus->name && strlen(bus->name)); + /* A bus should mandatorily have the scan and match implemented */ + RTE_VERIFY(bus->scan); + RTE_VERIFY(bus->match); /* Initialize the driver and device list associated with the bus */ TAILQ_INIT(&(bus->driver_list)); diff --git a/lib/librte_eal/common/include/rte_bus.h b/lib/librte_eal/common/include/rte_bus.h index f0297a9..b41105c 100644 --- a/lib/librte_eal/common/include/rte_bus.h +++ b/lib/librte_eal/common/include/rte_bus.h @@ -59,6 +59,49 @@ TAILQ_HEAD(rte_bus_list, rte_bus); /* Global Bus list */ extern struct rte_bus_list rte_bus_list; +/** + * Bus specific scan for devices attached on the bus. + * For each bus object, the scan would be reponsible for finding devices and + * adding them to its private device list. + * + * Successful detection of a device results in rte_device object which is + * embedded within the respective device type (rte_pci_device, for example). + * Thereafter, PCI specific bus would need to perform + * container_of(rte_pci_device) to obtain PCI device object. + * + * Scan failure of a bus is not treated as exit criteria for application. Scan + * for all other buses would still continue. + * + * A bus should mandatorily implement this method. + * + * @param bus + * Reference to the bus on which device is added + * @return + * 0 for successful scan + * !0 (<0) for unsuccessful scan with error value + */ +typedef int (*bus_scan_t)(struct rte_bus *bus); + +/** + * Bus specific match for devices and drivers which can service them. + * For each scanned device, rte_driver->probe would be called for driver + * specific initialization of the device. + * + * It is the work of each bus handler to obtain the specific device object + * using container_of (or typecasting, as a less preferred way). + * + * A bus should mandatorily implement this method. + * + * @param drv + * Driver object attached to the bus + * @param dev + * Device object which is being probed. + * @return + * 0 for successful match + * !0 for unsuccessful match + */ +typedef int (*bus_match_t)(struct rte_driver *drv, struct rte_device *dev); + struct rte_bus { TAILQ_ENTRY(rte_bus) next; /**< Next bus object in linked list */ struct rte_driver_list driver_list; @@ -66,6 +109,9 @@ struct rte_bus { struct rte_device_list device_list; /**< List of all devices on bus */ const char *name; /**< Name of the bus */ + bus_scan_t scan; /**< Scan for devices attached to bus */ + bus_match_t match; + /**< Match device with drivers associated with the bus */ }; /** @internal -- 2.7.4