[dpdk-dev] [PATCH v3 06/15] eal/soc: implement probing of drivers

Jan Viktorin viktorin at rehivetech.com
Fri Sep 16 14:27:03 CEST 2016


On Fri, 9 Sep 2016 14:13:50 +0530
Shreyansh Jain <shreyansh.jain at nxp.com> wrote:

> Each SoC PMD registers a set of callback for scanning its own bus/infra and
> matching devices to drivers when probe is called.
> This patch introduces the infra for calls to SoC scan on rte_eal_soc_init()
> and match on rte_eal_soc_probe().
> 
> Patch also adds test case for scan and probe.
> 
> Signed-off-by: Jan Viktorin <viktorin at rehivetech.com>
> Signed-off-by: Shreyansh Jain <shreyansh.jain at nxp.com>
> Signed-off-by: Hemant Agrawal <hemant.agrawal at nxp.com>
> ---
>  app/test/test_soc.c                             | 138 ++++++++++++++-
>  lib/librte_eal/bsdapp/eal/rte_eal_version.map   |   4 +
>  lib/librte_eal/common/eal_common_soc.c          | 215 ++++++++++++++++++++++++
>  lib/librte_eal/common/include/rte_soc.h         |  51 ++++++
>  lib/librte_eal/linuxapp/eal/eal.c               |   5 +
>  lib/librte_eal/linuxapp/eal/eal_soc.c           |  16 ++
>  lib/librte_eal/linuxapp/eal/rte_eal_version.map |   4 +
>  7 files changed, 432 insertions(+), 1 deletion(-)
> 
> diff --git a/app/test/test_soc.c b/app/test/test_soc.c
> index ac03e64..d2b9462 100644
> --- a/app/test/test_soc.c
> +++ b/app/test/test_soc.c
> @@ -87,14 +87,45 @@ static int test_compare_addr(void)
>   */
>  struct test_wrapper {
>  	struct rte_soc_driver soc_drv;
> +	struct rte_soc_device soc_dev;
>  };
>  
> +static int empty_pmd0_devinit(struct rte_soc_driver *drv,
> +			      struct rte_soc_device *dev);
> +static int empty_pmd0_devuninit(struct rte_soc_device *dev);

I prefer an empty line here.


What is the prupose of the scan here? What device does it provide
to the test? I'd prefer to call it e.g. "allways_find_device0" or
something describing the purpose and explaining what is the goal
of the related test.

Probably a comment explaining "provide a device named 'empty_pmd0_dev'
would be helpful.

> +static void test_soc_scan_dev0_cb(void);

Similar here, something like "match_by_name".

> +static int test_soc_match_dev0_cb(struct rte_soc_driver *drv,
> +				  struct rte_soc_device *dev);

I prefer an empty line here.


ditto...

> +static void test_soc_scan_dev1_cb(void);

ditto...

> +static int test_soc_match_dev1_cb(struct rte_soc_driver *drv,
> +				  struct rte_soc_device *dev);
> +
> +static int
> +empty_pmd0_devinit(struct rte_soc_driver *drv __rte_unused,
> +		   struct rte_soc_device *dev __rte_unused)
> +{
> +	return 0;
> +}
> +
> +static int
> +empty_pmd0_devuninit(struct rte_soc_device *dev)
> +{
> +	/* Release the memory associated with dev->addr.name */
> +	free(dev->addr.name);
> +
> +	return 0;
> +}
> +
>  struct test_wrapper empty_pmd0 = {
>  	.soc_drv = {
>  		.driver = {
>  			.name = "empty_pmd0"
>  		},
> -	},
> +		.devinit = empty_pmd0_devinit,
> +		.devuninit = empty_pmd0_devuninit,
> +		.scan_fn = test_soc_scan_dev0_cb,
> +		.match_fn = test_soc_match_dev0_cb,
> +	}
>  };
>  
>  struct test_wrapper empty_pmd1 = {
> @@ -102,9 +133,54 @@ struct test_wrapper empty_pmd1 = {
>  		.driver = {
>  			.name = "empty_pmd1"
>  		},
> +		.scan_fn = test_soc_scan_dev1_cb,
> +		.match_fn = test_soc_match_dev1_cb,
>  	},
>  };
>  
> +static void
> +test_soc_scan_dev0_cb(void)
> +{
> +	/* SoC's scan would scan devices on its bus and add to
> +	 * soc_device_list
> +	 */
> +	empty_pmd0.soc_dev.addr.name = strdup("empty_pmd0_dev");
> +
> +	TAILQ_INSERT_TAIL(&soc_device_list, &empty_pmd0.soc_dev, next);
> +}
> +
> +static int
> +test_soc_match_dev0_cb(struct rte_soc_driver *drv __rte_unused,
> +		       struct rte_soc_device *dev)
> +{
> +	if (!dev->addr.name || strcmp(dev->addr.name, "empty_pmd0_dev"))
> +		return 0;
> +
> +	return 1;
> +}
> +
> +
> +static void
> +test_soc_scan_dev1_cb(void)
> +{
> +	/* SoC's scan would scan devices on its bus and add to
> +	 * soc_device_list
> +	 */
> +	empty_pmd0.soc_dev.addr.name = strdup("empty_pmd1_dev");
> +
> +	TAILQ_INSERT_TAIL(&soc_device_list, &empty_pmd1.soc_dev, next);
> +}
> +
> +static int
> +test_soc_match_dev1_cb(struct rte_soc_driver *drv __rte_unused,
> +		       struct rte_soc_device *dev)
> +{
> +	if (!dev->addr.name || strcmp(dev->addr.name, "empty_pmd1_dev"))
> +		return 0;
> +
> +	return 1;
> +}
> +
>  static int
>  count_registered_socdrvs(void)
>  {
> @@ -148,13 +224,54 @@ test_register_unregister(void)
>  	return 0;
>  }
>  
> +/* Test Probe (scan and match) functionality */
> +static int
> +test_soc_init_and_probe(void)

You say to test scan and match. I'd prefer to reflect this in the name
of the test. Otherwise, it seems you are testing init and probe which
is not true, I think.

Do you test that "match principle works" or that "match functions are OK"
or "match functions are called as expected", ...?

> +{
> +	struct rte_soc_driver *drv;
> +
> +	/* Registering dummy drivers */
> +	rte_eal_soc_register(&empty_pmd0.soc_drv);
> +	rte_eal_soc_register(&empty_pmd1.soc_drv);
> +	/* Assuming that test_register_unregister is working, not verifying
> +	 * that drivers are indeed registered
> +	*/
> +
> +	/* rte_eal_soc_init is called by rte_eal_init, which in turn calls the
> +	 * scan_fn of each driver.
> +	 */
> +	TAILQ_FOREACH(drv, &soc_driver_list, next) {
> +		if (drv && drv->scan_fn)
> +			drv->scan_fn();
> +	}

Here, I suppose you mimic the rte_eal_soc_init?

> +
> +	/* rte_eal_init() would perform other inits here */
> +
> +	/* Probe would link the SoC devices<=>drivers */
> +	rte_eal_soc_probe();
> +
> +	/* Unregistering dummy drivers */
> +	rte_eal_soc_unregister(&empty_pmd0.soc_drv);
> +	rte_eal_soc_unregister(&empty_pmd1.soc_drv);
> +
> +	free(empty_pmd0.soc_dev.addr.name);
> +
> +	printf("%s has been successful\n", __func__);

How you detect it is unsuccessful? Is it possible to fail in this test?
A test that can never fail is in fact not a test :).

> +	return 0;
> +}
> +
>  /* save real devices and drivers until the tests finishes */
>  struct soc_driver_list real_soc_driver_list =
>  	TAILQ_HEAD_INITIALIZER(real_soc_driver_list);
>  
> +/* save real devices and drivers until the tests finishes */
> +struct soc_device_list real_soc_device_list =
> +	TAILQ_HEAD_INITIALIZER(real_soc_device_list);
> +
>  static int test_soc_setup(void)
>  {
>  	struct rte_soc_driver *drv;
> +	struct rte_soc_device *dev;
>  
>  	/* no real drivers for the test */
>  	while (!TAILQ_EMPTY(&soc_driver_list)) {
> @@ -163,12 +280,20 @@ static int test_soc_setup(void)
>  		TAILQ_INSERT_TAIL(&real_soc_driver_list, drv, next);
>  	}
>  
> +	/* And, no real devices for the test */
> +	while (!TAILQ_EMPTY(&soc_device_list)) {
> +		dev = TAILQ_FIRST(&soc_device_list);
> +		TAILQ_REMOVE(&soc_device_list, dev, next);
> +		TAILQ_INSERT_TAIL(&real_soc_device_list, dev, next);
> +	}
> +
>  	return 0;
>  }
>  
>  static int test_soc_cleanup(void)
>  {
>  	struct rte_soc_driver *drv;
> +	struct rte_soc_device *dev;
>  
>  	/* bring back real drivers after the test */
>  	while (!TAILQ_EMPTY(&real_soc_driver_list)) {
> @@ -177,6 +302,13 @@ static int test_soc_cleanup(void)
>  		rte_eal_soc_register(drv);
>  	}
>  
> +	/* And, bring back real devices after the test */
> +	while (!TAILQ_EMPTY(&real_soc_device_list)) {
> +		dev = TAILQ_FIRST(&real_soc_device_list);
> +		TAILQ_REMOVE(&real_soc_device_list, dev, next);
> +		TAILQ_INSERT_TAIL(&soc_device_list, dev, next);
> +	}
> +
>  	return 0;
>  }
>  
> @@ -192,6 +324,10 @@ test_soc(void)
>  	if (test_register_unregister())
>  		return -1;
>  
> +	/* Assuming test_register_unregister has succeeded */
> +	if (test_soc_init_and_probe())
> +		return -1;
> +
>  	if (test_soc_cleanup())
>  		return -1;
>  
> diff --git a/lib/librte_eal/bsdapp/eal/rte_eal_version.map b/lib/librte_eal/bsdapp/eal/rte_eal_version.map
> index de38848..3c407be 100644
> --- a/lib/librte_eal/bsdapp/eal/rte_eal_version.map
> +++ b/lib/librte_eal/bsdapp/eal/rte_eal_version.map
> @@ -173,5 +173,9 @@ DPDK_16.11 {
>  	rte_eal_soc_register;
>  	rte_eal_soc_unregister;
>  	rte_eal_soc_dump;
> +	rte_eal_soc_match;
> +	rte_eal_soc_detach;
> +	rte_eal_soc_probe;
> +	rte_eal_soc_probe_one;
>  
>  } DPDK_16.07;
> diff --git a/lib/librte_eal/common/eal_common_soc.c b/lib/librte_eal/common/eal_common_soc.c
> index 5dcddc5..bb87a67 100644
> --- a/lib/librte_eal/common/eal_common_soc.c
> +++ b/lib/librte_eal/common/eal_common_soc.c
> @@ -36,6 +36,8 @@
>  #include <sys/queue.h>
>  
>  #include <rte_log.h>
> +#include <rte_common.h>
> +#include <rte_soc.h>
>  
>  #include "eal_private.h"
>  
> @@ -45,6 +47,213 @@ struct soc_driver_list soc_driver_list =
>  struct soc_device_list soc_device_list =
>  	TAILQ_HEAD_INITIALIZER(soc_device_list);
>  
> +/* Default SoC device<->Driver match handler function */

I think this comment is redundant. All this is already said in the rte_soc.h.

> +int
> +rte_eal_soc_match(struct rte_soc_driver *drv, struct rte_soc_device *dev)
> +{
> +	int i, j;
> +
> +	RTE_VERIFY(drv != NULL && drv->id_table != NULL);
> +	RTE_VERIFY(dev != NULL && dev->id != NULL);
> +
> +	for (i = 0; drv->id_table[i].compatible; ++i) {
> +		const char *drv_compat = drv->id_table[i].compatible;
> +
> +		for (j = 0; dev->id[j].compatible; ++j) {
> +			const char *dev_compat = dev->id[j].compatible;
> +
> +			if (!strcmp(drv_compat, dev_compat))
> +				return 0;
> +		}
> +	}
> +
> +	return 1;
> +}
> +
> +
> +static int
> +rte_eal_soc_probe_one_driver(struct rte_soc_driver *drv,
> +			     struct rte_soc_device *dev)
> +{
> +	int ret = 1;
> +

I think, the RTE_VERIFY(dev->match_fn) might be good here.
It avoids any doubts about the validity of the pointer.

> +	ret = drv->match_fn(drv, dev);
> +	if (ret) {
> +		RTE_LOG(DEBUG, EAL,
> +			" match function failed, skipping\n");

Is this a failure? I think it is not. Failure would be if the match
function cannot execute correctly. This is more like "no-match".

When debugging, I'd like to see more a message like "driver <name> does not match".

> +		return ret;
> +	}
> +
> +	dev->driver = drv;
> +	RTE_VERIFY(drv->devinit != NULL);
> +	return drv->devinit(drv, dev);
> +}
> +
> +static int
> +soc_probe_all_drivers(struct rte_soc_device *dev)
> +{
> +	struct rte_soc_driver *drv = NULL;
> +	int rc = 0;
> +
> +	if (dev == NULL)
> +		return -1;
> +
> +	TAILQ_FOREACH(drv, &soc_driver_list, next) {
> +		rc = rte_eal_soc_probe_one_driver(drv, dev);
> +		if (rc < 0)
> +			/* negative value is an error */
> +			return -1;
> +		if (rc > 0)
> +			/* positive value means driver doesn't support it */
> +			continue;
> +		return 0;
> +	}
> +	return 1;
> +}
> +
> +/* If the IDs match, call the devuninit() function of the driver. */

Again, I think this comment is redudant. I'd leave it if it explains some
implementation-specific detail but it does not seem to...

> +static int
> +rte_eal_soc_detach_dev(struct rte_soc_driver *drv,
> +		       struct rte_soc_device *dev)
> +{
> +	int ret;
> +
> +	if ((drv == NULL) || (dev == NULL))
> +		return -EINVAL;
> +
> +	ret = drv->match_fn(drv, dev);
> +	if (ret) {
> +		RTE_LOG(DEBUG, EAL,
> +			" match function failed, skipping\n");

When debugging, I'd like to see more "driver <name> does not match".

> +		return ret;
> +	}
> +
> +	RTE_LOG(DEBUG, EAL, "SoC device %s\n",
> +		dev->addr.name);
> +
> +	RTE_LOG(DEBUG, EAL, "  remove driver: %s\n", drv->driver.name);
> +
> +	if (drv->devuninit && (drv->devuninit(dev) < 0))
> +		return -1;	/* negative value is an error */
> +
> +	/* clear driver structure */
> +	dev->driver = NULL;
> +
> +	return 0;
> +}
> +
> +/*
> + * Call the devuninit() function of all registered drivers for the given
> + * device if their IDs match.

I think, the "IDs match" is obsolete becase the match_fn may work in a different way now.

> + *
> + * @return
> + *       0 when successful
> + *      -1 if deinitialization fails
> + *       1 if no driver is found for this device.
> + */
> +static int
> +soc_detach_all_drivers(struct rte_soc_device *dev)
> +{
> +	struct rte_soc_driver *dr = NULL;
> +	int rc = 0;
> +
> +	if (dev == NULL)
> +		return -1;
> +
> +	TAILQ_FOREACH(dr, &soc_driver_list, next) {
> +		rc = rte_eal_soc_detach_dev(dr, dev);
> +		if (rc < 0)
> +			/* negative value is an error */
> +			return -1;
> +		if (rc > 0)
> +			/* positive value means driver doesn't support it */
> +			continue;
> +		return 0;
> +	}
> +	return 1;
> +}
> +
> +/*
> + * Detach device specified by its SoC address.
> + */
> +int
> +rte_eal_soc_detach(const struct rte_soc_addr *addr)
> +{
> +	struct rte_soc_device *dev = NULL;
> +	int ret = 0;
> +
> +	if (addr == NULL)
> +		return -1;
> +
> +	TAILQ_FOREACH(dev, &soc_device_list, next) {
> +		if (rte_eal_compare_soc_addr(&dev->addr, addr))
> +			continue;
> +
> +		ret = soc_detach_all_drivers(dev);
> +		if (ret < 0)
> +			goto err_return;
> +
> +		TAILQ_REMOVE(&soc_device_list, dev, next);
> +		return 0;
> +	}
> +	return -1;
> +
> +err_return:
> +	RTE_LOG(WARNING, EAL, "Requested device %s cannot be used\n",
> +		dev->addr.name);
> +	return -1;
> +}
> +
> +int
> +rte_eal_soc_probe_one(const struct rte_soc_addr *addr)
> +{
> +	struct rte_soc_device *dev = NULL;
> +	int ret = 0;
> +
> +	if (addr == NULL)
> +		return -1;
> +
> +	/* unlike pci, in case of soc, it the responsibility of the soc driver
> +	 * to check during init whether device has been updated since last add.

Why? Can you give a more detailed explanation?

> +	 */
> +
> +	TAILQ_FOREACH(dev, &soc_device_list, next) {
> +		if (rte_eal_compare_soc_addr(&dev->addr, addr))
> +			continue;
> +
> +		ret = soc_probe_all_drivers(dev);
> +		if (ret < 0)
> +			goto err_return;
> +		return 0;
> +	}
> +	return -1;
> +
> +err_return:
> +	RTE_LOG(WARNING, EAL,
> +		"Requested device %s cannot be used\n", addr->name);
> +	return -1;
> +}
> +
> +/*
> + * Scan the SoC devices and call the devinit() function for all registered
> + * drivers that have a matching entry in its id_table for discovered devices.
> + */

Should be in header. Here it is redundant.

> +int
> +rte_eal_soc_probe(void)
> +{
> +	struct rte_soc_device *dev = NULL;
> +	int ret = 0;
> +
> +	TAILQ_FOREACH(dev, &soc_device_list, next) {
> +		ret = soc_probe_all_drivers(dev);
> +		if (ret < 0)
> +			rte_exit(EXIT_FAILURE, "Requested device %s"
> +				 " cannot be used\n", dev->addr.name);
> +	}
> +
> +	return 0;
> +}
> +
>  /* dump one device */
>  static int
>  soc_dump_one_device(FILE *f, struct rte_soc_device *dev)
> @@ -79,6 +288,12 @@ rte_eal_soc_dump(FILE *f)
>  void
>  rte_eal_soc_register(struct rte_soc_driver *driver)
>  {
> +	/* For a valid soc driver, match and scan function
> +	 * should be provided.
> +	 */

This comment should be in the header file.

> +	RTE_VERIFY(driver != NULL);
> +	RTE_VERIFY(driver->match_fn != NULL);
> +	RTE_VERIFY(driver->scan_fn != NULL);
>  	TAILQ_INSERT_TAIL(&soc_driver_list, driver, next);
>  }
>  
> diff --git a/lib/librte_eal/common/include/rte_soc.h b/lib/librte_eal/common/include/rte_soc.h
> index c6f98eb..bfb49a2 100644
> --- a/lib/librte_eal/common/include/rte_soc.h
> +++ b/lib/librte_eal/common/include/rte_soc.h
> @@ -97,6 +97,16 @@ typedef int (soc_devinit_t)(struct rte_soc_driver *, struct rte_soc_device *);
>  typedef int (soc_devuninit_t)(struct rte_soc_device *);
>  
>  /**
> + * SoC device scan callback, called from rte_eal_soc_init.

Can you explain what is the goal of the callback?
What is the expected behaviour.

It returns void so it seems it can never fail. Is this correct?
I can image that to scan for devices, I need to check some file-system
structure which can be unavailable...

> + */
> +typedef void (soc_scan_t)(void);

You are missing the '*' in (*soc_scan_t).

> +
> +/**
> + * Custom device<=>driver match callback for SoC

Can you explain the semantics (return values), please?

> + */
> +typedef int (soc_match_t)(struct rte_soc_driver *, struct rte_soc_device *);

You are missing the '*' in (*soc_match_t).

> +
> +/**
>   * A structure describing a SoC driver.
>   */
>  struct rte_soc_driver {
> @@ -104,6 +114,8 @@ struct rte_soc_driver {
>  	struct rte_driver driver;          /**< Inherit core driver. */
>  	soc_devinit_t *devinit;            /**< Device initialization */
>  	soc_devuninit_t *devuninit;        /**< Device uninitialization */

Those should be renamed to probe/remove.

> +	soc_scan_t *scan_fn;               /**< Callback for scanning SoC bus*/
> +	soc_match_t *match_fn;             /**< Callback to match dev<->drv */

Here the '*' would be redundant if you add them to the typedefs.

I think, we should tell the users that scan_fn and match_fn must be always set
to something.

>  	const struct rte_soc_id *id_table; /**< ID table, NULL terminated */
>  };
>  
> @@ -146,6 +158,45 @@ rte_eal_compare_soc_addr(const struct rte_soc_addr *a0,
>  }
>  
>  /**
> + * Default function for matching the Soc driver with device. Each driver can
> + * either use this function or define their own soc matching function.
> + * This function relies on the compatible string extracted from sysfs. But,
> + * a SoC might have different way of identifying its devices. Such SoC can
> + * override match_fn.
> + *
> + * @return
> + * 	 0 on success
> + *	-1 when no match found
> +  */
> +int
> +rte_eal_soc_match(struct rte_soc_driver *drv, struct rte_soc_device *dev);

What about naming it

	rte_eal_soc_match_default

or maybe better

	rte_eal_soc_match_compatible

what do you think?

> +
> +/**
> + * Probe SoC devices for registered drivers.
> + */
> +int rte_eal_soc_probe(void);
> +
> +/**
> + * Probe the single SoC device.
> + */
> +int rte_eal_soc_probe_one(const struct rte_soc_addr *addr);
> +
> +/**
> + * Close the single SoC device.
> + *
> + * Scan the SoC devices and find the SoC device specified by the SoC
> + * address, then call the devuninit() function for registered driver
> + * that has a matching entry in its id_table for discovered device.
> + *
> + * @param addr
> + *	The SoC address to close.
> + * @return
> + *   - 0 on success.
> + *   - Negative on error.
> + */
> +int rte_eal_soc_detach(const struct rte_soc_addr *addr);
> +
> +/**
>   * Dump discovered SoC devices.
>   */
>  void rte_eal_soc_dump(FILE *f);
> diff --git a/lib/librte_eal/linuxapp/eal/eal.c b/lib/librte_eal/linuxapp/eal/eal.c
> index 15c8c3d..147b601 100644
> --- a/lib/librte_eal/linuxapp/eal/eal.c
> +++ b/lib/librte_eal/linuxapp/eal/eal.c
> @@ -70,6 +70,7 @@
>  #include <rte_cpuflags.h>
>  #include <rte_interrupts.h>
>  #include <rte_pci.h>
> +#include <rte_soc.h>
>  #include <rte_dev.h>
>  #include <rte_devargs.h>
>  #include <rte_common.h>
> @@ -881,6 +882,10 @@ rte_eal_init(int argc, char **argv)
>  	if (rte_eal_pci_probe())
>  		rte_panic("Cannot probe PCI\n");
>  
> +	/* Probe & Initialize SoC devices */
> +	if (rte_eal_soc_probe())
> +		rte_panic("Cannot probe SoC\n");
> +
>  	rte_eal_mcfg_complete();
>  
>  	return fctret;
> diff --git a/lib/librte_eal/linuxapp/eal/eal_soc.c b/lib/librte_eal/linuxapp/eal/eal_soc.c
> index 04848b9..5f961c4 100644
> --- a/lib/librte_eal/linuxapp/eal/eal_soc.c
> +++ b/lib/librte_eal/linuxapp/eal/eal_soc.c
> @@ -52,5 +52,21 @@
>  int
>  rte_eal_soc_init(void)
>  {
> +	struct rte_soc_driver *drv;
> +
> +	/* for debug purposes, SoC can be disabled */
> +	if (internal_config.no_soc)
> +		return 0;
> +
> +	/* For each registered driver, call their scan routine to perform any
> +	 * custom scan for devices (for example, custom buses)
> +	 */
> +	TAILQ_FOREACH(drv, &soc_driver_list, next) {

Is it possible to have drv->scan_fn == NULL? I suppose, this is invalid.
I'd prefer to have RTE_VERIFY for this check.

> +		if (drv && drv->scan_fn) {
> +			drv->scan_fn();
> +			/* Ignore all errors from this */
> +		}

> +	}
> +
>  	return 0;
>  }
> diff --git a/lib/librte_eal/linuxapp/eal/rte_eal_version.map b/lib/librte_eal/linuxapp/eal/rte_eal_version.map
> index b9d1932..adcfe7d 100644
> --- a/lib/librte_eal/linuxapp/eal/rte_eal_version.map
> +++ b/lib/librte_eal/linuxapp/eal/rte_eal_version.map
> @@ -179,5 +179,9 @@ DPDK_16.11 {
>  	rte_eal_soc_register;
>  	rte_eal_soc_unregister;
>  	rte_eal_soc_dump;
> +	rte_eal_soc_match;
> +	rte_eal_soc_detach;
> +	rte_eal_soc_probe;
> +	rte_eal_soc_probe_one;
>  
>  } DPDK_16.07;

Regards
Jan


More information about the dev mailing list