[dpdk-dev,v14,12/13] eal/pci: Add rte_eal_dev_attach/detach() functions

Message ID 1424837093-5661-13-git-send-email-mukawa@igel.co.jp (mailing list archive)
State Changes Requested, archived
Headers

Commit Message

Tetsuya Mukawa Feb. 25, 2015, 4:04 a.m. UTC
  These functions are used for attaching or detaching a port.
When rte_eal_dev_attach() is called, the function tries to realize the
device name as pci address. If this is done successfully,
rte_eal_dev_attach() will attach physical device port. If not, attaches
virtual devive port.
When rte_eal_dev_detach() is called, the function gets the device type
of this port to know whether the port is come from physical or virtual.
And then specific detaching function will be called.

v14:
- Remove needless if statement.
  (Thanks to Maxime Leroy)
v13:
- Change log level when error occurs in rte_eal_vdev_init() and
  rte_eal_dev_init().
- Return value of driver init and uninit functions.
- Replace rte_panic by RTE_LOG in rte_eal_dev_init()
- Fix return value of rte_eal_vdev_uninit().
- Fix rte_eal_dev_attach_vdev to set port_id correctly.
  (Thanks to Maxime Leroy)
v11:
- Remove needless devargs handling codes.
- Replace get_vdev_name() by rte_eal_parse_devargs_str().
- Replace rte_eal_vdev_find_and_init by rte_eal_vdev_init()
- Replace rte_eal_vdev_find_and_uninit by rte_eal_vdev_uninit()
- Fix rte_eal_dev_init() to use rte_eal_vdev_init().
  (Thanks to Maxime Leroy)
v10:
- Add comments.
- Change order of version.map.
  (Thanks to Thomas Monjalon)
v9:
- Fix comments.
- Use strcmp() instead of strncmp().
- Remove RTE_EAL_INVOKE_TYPE_PROBE/CLOSE.
- Change definition of rte_dev_uninit_t.
  (Thanks to Thomas Monjalon and Maxime Leroy)
v8:
- Add missing symbol in version map.
  (Thanks to Qiu, Michael and Iremonger, Bernard)
v7:
- Fix typo of warning messages.
  (Thanks to Qiu, Michael)
v5:
- Change function names like below.
  rte_eal_dev_find_and_invoke() to rte_eal_vdev_find_and_invoke().
  rte_eal_dev_invoke() to rte_eal_vdev_invoke().
- Add code to handle a return value of rte_eal_devargs_remove().
- Fix pci address format in rte_eal_dev_detach().
v4:
- Fix comment.
- Add error checking.
- Fix indent of 'if' statement.
- Change function name.

Signed-off-by: Tetsuya Mukawa <mukawa@igel.co.jp>
---
 lib/librte_eal/common/eal_common_dev.c          | 278 ++++++++++++++++++++++--
 lib/librte_eal/common/eal_common_devargs.c      |  54 +++--
 lib/librte_eal/common/eal_private.h             |  11 +
 lib/librte_eal/common/include/rte_dev.h         |  33 +++
 lib/librte_eal/common/include/rte_devargs.h     |  28 +++
 lib/librte_eal/linuxapp/eal/Makefile            |   1 +
 lib/librte_eal/linuxapp/eal/eal_pci.c           |   6 +-
 lib/librte_eal/linuxapp/eal/rte_eal_version.map |   2 +
 8 files changed, 375 insertions(+), 38 deletions(-)
  

Comments

Thomas Monjalon Feb. 25, 2015, 11:21 a.m. UTC | #1
2015-02-25 13:04, Tetsuya Mukawa:
> --- a/lib/librte_eal/common/eal_common_dev.c
> +++ b/lib/librte_eal/common/eal_common_dev.c
> @@ -32,10 +32,13 @@
>   *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
>   */
>  
> +#include <stdio.h>
> +#include <limits.h>
>  #include <string.h>
>  #include <inttypes.h>
>  #include <sys/queue.h>
>  
> +#include <rte_ethdev.h>
>  #include <rte_dev.h>
>  #include <rte_devargs.h>

No, you must not include ethdev in EAL.
The ethdev layer is by design on top of EAL.
Maxime already asked why you did it. He was implicitly asking to remove it.
You said that you are calling ethdev_is_detachable() but you should
call a function eal_is_detachable() or something like that.
The detachable state must be only device-related, i.e. in EAL.
The ethdev API is only a wrapper (with port id) in such case.

> --- a/lib/librte_eal/linuxapp/eal/Makefile
> +++ b/lib/librte_eal/linuxapp/eal/Makefile
> @@ -45,6 +45,7 @@ CFLAGS += -I$(RTE_SDK)/lib/librte_eal/common/include
>  CFLAGS += -I$(RTE_SDK)/lib/librte_ring
>  CFLAGS += -I$(RTE_SDK)/lib/librte_mempool
>  CFLAGS += -I$(RTE_SDK)/lib/librte_malloc
> +CFLAGS += -I$(RTE_SDK)/lib/librte_mbuf

By removing ethdev dependency, you can remove this ugly mbuf dependency.

Thanks Tetsuya
  
Tetsuya Mukawa Feb. 25, 2015, 12:32 p.m. UTC | #2
2015-02-25 20:21 GMT+09:00 Thomas Monjalon <thomas.monjalon@6wind.com>:
> 2015-02-25 13:04, Tetsuya Mukawa:
>> --- a/lib/librte_eal/common/eal_common_dev.c
>> +++ b/lib/librte_eal/common/eal_common_dev.c
>> @@ -32,10 +32,13 @@
>>   *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
>>   */
>>
>> +#include <stdio.h>
>> +#include <limits.h>
>>  #include <string.h>
>>  #include <inttypes.h>
>>  #include <sys/queue.h>
>>
>> +#include <rte_ethdev.h>
>>  #include <rte_dev.h>
>>  #include <rte_devargs.h>
>
> No, you must not include ethdev in EAL.
> The ethdev layer is by design on top of EAL.
> Maxime already asked why you did it. He was implicitly asking to remove it.
> You said that you are calling ethdev_is_detachable() but you should
> call a function eal_is_detachable() or something like that.
> The detachable state must be only device-related, i.e. in EAL.
> The ethdev API is only a wrapper (with port id) in such case.
>

Hi Thomas,

If ethdev library is on top of EAL, hotplug functions like
rte_eal_dev_attach/detach should be implemented in ethdev library.
Is it right?

If so, I will move rte_eal_dev_attach/detach to ethdev library.
And I will change names like rte_eth_dev_attach/detach.
Also, I will add "rte_dev.h" and "rte_pci.h" in rte_ethdev.h, and call
below EAL functions from ethdev library.

- For virtual device initialization and finalization
-- rte_eth_vdev_init
-- rte_eth_vdev_uninit()
- For physical NIC initialization and finalization
-- rte_eal_pci_probe_one()
-- rte_eal_pci_close_one()

I guess this will fix this design violation.
Is this ok?

Thanks,
Tetsuya

>> --- a/lib/librte_eal/linuxapp/eal/Makefile
>> +++ b/lib/librte_eal/linuxapp/eal/Makefile
>> @@ -45,6 +45,7 @@ CFLAGS += -I$(RTE_SDK)/lib/librte_eal/common/include
>>  CFLAGS += -I$(RTE_SDK)/lib/librte_ring
>>  CFLAGS += -I$(RTE_SDK)/lib/librte_mempool
>>  CFLAGS += -I$(RTE_SDK)/lib/librte_malloc
>> +CFLAGS += -I$(RTE_SDK)/lib/librte_mbuf
>
> By removing ethdev dependency, you can remove this ugly mbuf dependency.
>
> Thanks Tetsuya
>
  
Thomas Monjalon Feb. 25, 2015, 2 p.m. UTC | #3
2015-02-25 21:32, Tetsuya Mukawa:
> 2015-02-25 20:21 GMT+09:00 Thomas Monjalon <thomas.monjalon@6wind.com>:
> > 2015-02-25 13:04, Tetsuya Mukawa:
> >> --- a/lib/librte_eal/common/eal_common_dev.c
> >> +++ b/lib/librte_eal/common/eal_common_dev.c
> >> @@ -32,10 +32,13 @@
> >>   *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
> >>   */
> >>
> >> +#include <stdio.h>
> >> +#include <limits.h>
> >>  #include <string.h>
> >>  #include <inttypes.h>
> >>  #include <sys/queue.h>
> >>
> >> +#include <rte_ethdev.h>
> >>  #include <rte_dev.h>
> >>  #include <rte_devargs.h>
> >
> > No, you must not include ethdev in EAL.
> > The ethdev layer is by design on top of EAL.
> > Maxime already asked why you did it. He was implicitly asking to remove it.
> > You said that you are calling ethdev_is_detachable() but you should
> > call a function eal_is_detachable() or something like that.
> > The detachable state must be only device-related, i.e. in EAL.
> > The ethdev API is only a wrapper (with port id) in such case.
> >
> 
> Hi Thomas,
> 
> If ethdev library is on top of EAL, hotplug functions like
> rte_eal_dev_attach/detach should be implemented in ethdev library.
> Is it right?

Yes you're right.

> If so, I will move rte_eal_dev_attach/detach to ethdev library.
> And I will change names like rte_eth_dev_attach/detach.

It seems to be the right thing to do.

> Also, I will add "rte_dev.h" and "rte_pci.h" in rte_ethdev.h, and call
> below EAL functions from ethdev library.
> 
> - For virtual device initialization and finalization
> -- rte_eth_vdev_init
> -- rte_eth_vdev_uninit()
> - For physical NIC initialization and finalization
> -- rte_eal_pci_probe_one()
> -- rte_eal_pci_close_one()
> 
> I guess this will fix this design violation.
> Is this ok?

I think yes.
If needed, we could do some cleanup after RC1.
I'm just waiting for you fixing this, to avoid introducing
a layering violation.
Would you able to do it today?

Thanks

> >> --- a/lib/librte_eal/linuxapp/eal/Makefile
> >> +++ b/lib/librte_eal/linuxapp/eal/Makefile
> >> @@ -45,6 +45,7 @@ CFLAGS += -I$(RTE_SDK)/lib/librte_eal/common/include
> >>  CFLAGS += -I$(RTE_SDK)/lib/librte_ring
> >>  CFLAGS += -I$(RTE_SDK)/lib/librte_mempool
> >>  CFLAGS += -I$(RTE_SDK)/lib/librte_malloc
> >> +CFLAGS += -I$(RTE_SDK)/lib/librte_mbuf
> >
> > By removing ethdev dependency, you can remove this ugly mbuf dependency.
> >
> > Thanks Tetsuya
> >
  
Tetsuya Mukawa Feb. 25, 2015, 2:56 p.m. UTC | #4
On 2015/02/25 23:00, Thomas Monjalon wrote:
> 2015-02-25 21:32, Tetsuya Mukawa:
>> 2015-02-25 20:21 GMT+09:00 Thomas Monjalon <thomas.monjalon@6wind.com>:
>>> 2015-02-25 13:04, Tetsuya Mukawa:
>>>> --- a/lib/librte_eal/common/eal_common_dev.c
>>>> +++ b/lib/librte_eal/common/eal_common_dev.c
>>>> @@ -32,10 +32,13 @@
>>>>   *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
>>>>   */
>>>>
>>>> +#include <stdio.h>
>>>> +#include <limits.h>
>>>>  #include <string.h>
>>>>  #include <inttypes.h>
>>>>  #include <sys/queue.h>
>>>>
>>>> +#include <rte_ethdev.h>
>>>>  #include <rte_dev.h>
>>>>  #include <rte_devargs.h>
>>> No, you must not include ethdev in EAL.
>>> The ethdev layer is by design on top of EAL.
>>> Maxime already asked why you did it. He was implicitly asking to remove it.
>>> You said that you are calling ethdev_is_detachable() but you should
>>> call a function eal_is_detachable() or something like that.
>>> The detachable state must be only device-related, i.e. in EAL.
>>> The ethdev API is only a wrapper (with port id) in such case.
>>>
>> Hi Thomas,
>>
>> If ethdev library is on top of EAL, hotplug functions like
>> rte_eal_dev_attach/detach should be implemented in ethdev library.
>> Is it right?
> Yes you're right.
>
>> If so, I will move rte_eal_dev_attach/detach to ethdev library.
>> And I will change names like rte_eth_dev_attach/detach.
> It seems to be the right thing to do.
>
>> Also, I will add "rte_dev.h" and "rte_pci.h" in rte_ethdev.h, and call
>> below EAL functions from ethdev library.
>>
>> - For virtual device initialization and finalization
>> -- rte_eth_vdev_init
>> -- rte_eth_vdev_uninit()
>> - For physical NIC initialization and finalization
>> -- rte_eal_pci_probe_one()
>> -- rte_eal_pci_close_one()
>>
>> I guess this will fix this design violation.
>> Is this ok?
> I think yes.
> If needed, we could do some cleanup after RC1.
> I'm just waiting for you fixing this, to avoid introducing
> a layering violation.
> Would you able to do it today?

Hi Thomas,

I appreciate for your reply.
I start trying it.

Thanks,
Tetsuya

> Thanks
>
>>>> --- a/lib/librte_eal/linuxapp/eal/Makefile
>>>> +++ b/lib/librte_eal/linuxapp/eal/Makefile
>>>> @@ -45,6 +45,7 @@ CFLAGS += -I$(RTE_SDK)/lib/librte_eal/common/include
>>>>  CFLAGS += -I$(RTE_SDK)/lib/librte_ring
>>>>  CFLAGS += -I$(RTE_SDK)/lib/librte_mempool
>>>>  CFLAGS += -I$(RTE_SDK)/lib/librte_malloc
>>>> +CFLAGS += -I$(RTE_SDK)/lib/librte_mbuf
>>> By removing ethdev dependency, you can remove this ugly mbuf dependency.
>>>
>>> Thanks Tetsuya
>>>
>
  
Tetsuya Mukawa Feb. 25, 2015, 7:32 p.m. UTC | #5
This patch series adds a dynamic port hotplug framework to DPDK.
With the patches, DPDK apps can attach or detach ports at runtime.

The basic concept of the port hotplug is like followings.
- DPDK apps must have responsibility to manage ports.
  DPDK apps only know which ports are attached or detached at the moment.
  The port hotplug framework is implemented to allow DPDK apps to manage ports.
  For example, when DPDK apps call port attach function, attached port number
  will be returned. Also, DPDK apps can detach port by port number.
- Kernel support is needed for attaching or detaching physical device ports.
  To attach a new physical device port, the device will be recognized by
  userspace directly I/O framework in kernel at first. Then DPDK apps can
  call the port hotplug functions to attach ports.
  For detaching, steps are vice versa.
- Before detach ports, ports must be stopped and closed.
  DPDK application must call rte_eth_dev_stop() and rte_eth_dev_close() before
  detaching ports. These function will call finalization codes of PMDs.
  But so far, no PMD frees all resources allocated by initialization.
  It means PMDs are needed to be fixed to support the port hotplug.
  'RTE_PCI_DRV_DETACHABLE' is a new flag indicating a PMD supports detaching.
  Without this flag, detaching will be failed.
- Mustn't affect legacy DPDK apps.
  No DPDK EAL behavior is changed, if the port hotplug functions are't called.
  So all legacy DPDK apps can still work without modifications.

And a few limitations.
- The port hotplug functions are not thread safe.
  DPDK apps should handle it.
- Only support Linux and igb_uio so far.
  BSD and VFIO is not supported. I will send VFIO patches at least, but I don't
  have a plan to submit BSD patch so far.


Here is port hotplug APIs.
-------------------------------------------------------------------------------
/**
 * Attach a new device.
 *
 * @param devargs
 *   A pointer to a strings array describing the new device
 *   to be attached. The strings should be a pci address like
 *   '0000:01:00.0' or virtual device name like 'eth_pcap0'.
 * @param port_id
 *  A pointer to a port identifier actually attached.
 * @return
 *  0 on success and port_id is filled, negative on error
 */
int rte_eth_dev_attach(const char *devargs, uint8_t *port_id);

/**
 * Detach a device.
 *
 * @param port_id
 *   The port identifier of the device to detach.
 * @param addr
 *  A pointer to a device name actually detached.
 * @return
 *  0 on success and devname is filled, negative on error
 */
int rte_eth_dev_detach(uint8_t port_id, char *devname);
-------------------------------------------------------------------------------

This patch series are for DPDK EAL. To use port hotplug function by DPDK apps,
each PMD should be fixed to support 'RTE_PCI_DRV_DETACHABLE' flag. Please check
a patch for pcap PMD.

Also, please check testpmd patch. It will show you how to fix your legacy
applications to support port hotplug feature.

PATCH v15 changes
 - Fix issue that eal calls ethedv library APIs.
  - Remove rte_eal_dev_attach(), and add rte_eth_dev_attach().
  - Remove rte_eal_dev_detach(), and add rte_eth_dev_detach().
  - Call rte_eal_vdev_init/uninit from ethdev library.
   (Thanks to Thomas Monjalon)
 - Fix version.map
 - Reorder and squash patches to compile correctly.
 - Remove needless symbols in version.map
 - Fix version.map.

PATCH v14 changes
 - Remove needless if statement.
   (Thanks to Maxime Leroy)

PATCH v13 changes
 - Change log level when error occurs in rte_eal_vdev_init() and
   rte_eal_dev_init().
 - Return value of driver init and uninit functions.
 - Replace rte_panic by RTE_LOG in rte_eal_dev_init()
 - Fix return value of rte_eal_vdev_uninit().
 - Fix rte_eal_dev_attach_vdev to set port_id correctly.
   (Thanks to Maxime Leroy)

PATCH v12 changes
 - Add missing symbol in version map.
   (Thanks to Iremonger, Bernard)

PATCH v11 changes
 - Remove needless devargs handling codes.
 - Replace get_vdev_name() by rte_eal_parse_devargs_str().
 - Replace rte_eal_vdev_find_and_init by rte_eal_vdev_init()
 - Replace rte_eal_vdev_find_and_uninit by rte_eal_vdev_uninit()
 - Fix rte_eal_dev_init() to use rte_eal_vdev_init().
 - Remove needless patch.
   (Thanks to Maxime Leroy)

PATCH v10 changes
 - Add comments.
 - Chagne order of version.map.
 - Fix comment of "rte_ethdev.h".
   (Thanks to Thomas Monjalon)
 - Add size parameter to rte_eth_dev_create_unique_device_name().
   (Thanks to Iremonger, Bernard)

PATCH v9 changes
 - Fix commit title.
 - Fix commit log.
 - Fix comments.
 - Define CONFIG_RTE_LIBRTE_EAL_HOTPLUG at the top of this patch series.
 - DEV_INVALID/VALID are removed.
 - DEV_DISCONNECTED is replaced by DEV_DETACHED.
 - DEV_CONNECTED is replaced by DEV_ATTACHED.
 - rte_eth_dev_allocate_new_port() is renamed to
   rte_eth_dev_find_free_port().
 - rte_eth_dev_validate_port() is renamed to rte_eth_dev_is_valid_port().
 - rte_eth_dev_is_valid_port() is changed not to handle log toggle.
 - eal_compare_pci_addr() is replaced by rte_eal_compare_pci_addr().
 - rte_eth_dev_free() is replaced by rte_eth_dev_release_port().
 - Add a function to create a unique device name.
 - Change parameter of pci_devuninit_t and rte_eth_dev_uninit.
 - Remove code that initiaize callback of ethdev from
   rte_eth_dev_uninit().
 - Remove pci_unmap_device(). It will be implemented in later patch.
 - rte_eth_dev_check_detachable() is replaced by
   rte_eth_dev_is_detachable().
 - strncpy() is replaced by strcpy().
 - Implement pci_unmap_device() in this patch.
 - Remove "rte_dev_hotplug.h".
 - Remove needless "#ifdef".
 - Remove RTE_EAL_INVOKE_TYPE_PROBE/CLOSE.
 - RTE_ETH_DEV_PHYSICAL is replaced by RTE_ETH_DEV_PCI.
 - Use strcmp() instead of strncmp().
 - Remove RTE_EAL_INVOKE_TYPE_PROBE/CLOSE.
   (Thanks to Thomas Monjalon)
 - Change definition of rte_dev_uninit_t.
   (Thanks to Thomas Monjalon and Maxime Leroy)
 - Add missing symbol in version map.
   (Thanks to Nail Horman)

PATCH v8 changes
 - Fix Makefile and add version map file.
 - Add missing symbol in version map.
 - Fix pci_scan_one() to update sysfs values.
   (Thanks to Qiu, Michael and Iremonger, Bernard)
 - NONE_TRACE is replaced by NO_TRACE.
 - Fix typo.
 - Add size parameter to rte_eth_dev_save().
   (Thanks to Iremonger, Bernard)

PATCH v7 changes
 - Add a new section to programmer's guide.
   (Thanks to Iremonger, Bernard)
 - Fix port checking implementation of star_port().
 - Fix typo of warning messages.
 - Add pt_driver checking to rte_eth_dev_check_detachable().
   (Thanks to Qiu, Michael)

PATCH v6 changes
 - Fix rte_eth_dev_uninit() to handle a return value of uninit
   function of PMD. To do this, below changes also be applied.
   - Fix a parameter of rte_eth_dev_free().
   - Use rte_eth_dev structure as the paramter of rte_eth_dev_free().

PATCH v5 changes
 - Add runtime check passthrough driver type, like vfio-pci, igb_uio
   and uio_pci_generic.
   This was done by Qiu, Michael. Thanks a lot.
 - Change function names like below.
   - rte_eal_dev_find_and_invoke() to rte_eal_vdev_find_and_invoke().
   - rte_eal_dev_invoke() to rte_eal_vdev_invoke().
 - Add code to handle a return value of rte_eal_devargs_remove().
 - Fix pci address format in rte_eal_dev_detach().
 - Remove RTE_EAL_INVOKE_TYPE_UNKNOWN, because it's unused.
 - Change function definition of rte_eal_devargs_remove().
 - Fix pci_unmap_device() to check pt_driver.
 - Fix return value of below functions.
   - rte_eth_dev_get_changed_port().
   - rte_eth_dev_get_port_by_addr().
 - Change paramters of rte_eth_dev_validate_port() to cleanup code.
 - Fix pci_scan_one to handle pt_driver correctly.
   (Thanks to Qiu, Michael for above suggestions)

PATCH v4 changes
 - Merge patches to review easier.
 - Fix indent of 'if' statement.
 - Fix calculation method of eal_compare_pci_addr().
 - Fix header file declaration.
 - Add header file to determine if hotplug can be enabled.
   (Thanks to Qiu, Michael)
 - Use braces with 'for' loop.
 - Add parameter checking.
 - Fix sanity check code
 - Fix comments of rte_eth_dev_type.
 - Change function names.
   (Thanks to Iremonger, Bernard)

PATCH v3 changes:
 - Fix enum definition used in rte_ethdev.c.
   (Thanks to Zhang, Helin)

PATCH v2 changes:
 - Replace rte_eal_dev_attach_pdev(), rte_eal_dev_detach_pdev,
   rte_eal_dev_attach_vdev() and rte_eal_dev_detach_vdev() to
   rte_eal_dev_attach() and rte_eal_dev_detach().
 - Add parameter values checking.
 - Refashion a few functions.
   (Thanks to Iremonger, Bernard)

PATCH v1 Changes:
 - Fix error checking code of librte_eth APIs.
 - Fix issue that port from pcap PMD cannot be detached correctly.
 - Fix issue that testpmd could hang after forwarding, if attaching and detaching
   is repeatedly.
 - Fix if-condition of rte_eth_dev_get_port_by_addr().
   (Thanks to Mark Enright)

RFC PATCH v2 Changes:
- remove 'rte_eth_dev_validate_port()', and cleanup codes.


Michael Qiu (2):
  eal_pci: Add flag to hold kernel driver type
  eal_pci: pci memory map work with driver type

Tetsuya Mukawa (11):
  eal: Enable port Hotplug framework in Linux
  eal/pci,ethdev: Remove assumption that port will not be detached
  eal/pci: Consolidate pci address comparison APIs
  ethdev: Add rte_eth_dev_release_port to release specified port
  eal,ethdev: Add a function and function pointers to close ether device
  eal/linux/pci: Add functions for unmapping igb_uio resources
  eal/pci: Add probe and close functions of pci driver
  ethdev: Add one dev_type parameter to rte_eth_dev_allocate
  eal/pci: Add vdev driver initialization and uninitialization functions
  ethdev: Add rte_eth_dev_attach/detach() functions
  doc: Add port hotplug framework section to programmers guide

 app/test/virtual_pmd.c                           |   2 +-
 config/common_bsdapp                             |   6 +
 config/common_linuxapp                           |   5 +
 doc/guides/prog_guide/index.rst                  |   1 +
 doc/guides/prog_guide/port_hotplug_framework.rst | 110 ++++
 lib/librte_eal/bsdapp/eal/eal_pci.c              |  29 +-
 lib/librte_eal/common/eal_common_dev.c           |  77 ++-
 lib/librte_eal/common/eal_common_devargs.c       |  54 +-
 lib/librte_eal/common/eal_common_pci.c           | 100 +++-
 lib/librte_eal/common/eal_private.h              |  15 +
 lib/librte_eal/common/include/rte_dev.h          |  28 +
 lib/librte_eal/common/include/rte_devargs.h      |  28 +
 lib/librte_eal/common/include/rte_pci.h          |  91 ++++
 lib/librte_eal/linuxapp/eal/eal_pci.c            | 230 ++++++--
 lib/librte_eal/linuxapp/eal/eal_pci_init.h       |   7 +
 lib/librte_eal/linuxapp/eal/eal_pci_uio.c        |  67 ++-
 lib/librte_eal/linuxapp/eal/rte_eal_version.map  |   6 +
 lib/librte_ether/rte_ethdev.c                    | 635 +++++++++++++++++++----
 lib/librte_ether/rte_ethdev.h                    |  95 +++-
 lib/librte_ether/rte_ether_version.map           |   3 +
 lib/librte_pmd_af_packet/rte_eth_af_packet.c     |   2 +-
 lib/librte_pmd_bond/rte_eth_bond_api.c           |   2 +-
 lib/librte_pmd_pcap/rte_eth_pcap.c               |   2 +-
 lib/librte_pmd_ring/rte_eth_ring.c               |   2 +-
 lib/librte_pmd_xenvirt/rte_eth_xenvirt.c         |   2 +-
 25 files changed, 1405 insertions(+), 194 deletions(-)
 create mode 100644 doc/guides/prog_guide/port_hotplug_framework.rst
  
Thomas Monjalon Feb. 25, 2015, 11:26 p.m. UTC | #6
2015-02-26 04:32, Tetsuya Mukawa:
> This patch series adds a dynamic port hotplug framework to DPDK.
> With the patches, DPDK apps can attach or detach ports at runtime.
> 
> The basic concept of the port hotplug is like followings.
> - DPDK apps must have responsibility to manage ports.
>   DPDK apps only know which ports are attached or detached at the moment.
>   The port hotplug framework is implemented to allow DPDK apps to manage ports.
>   For example, when DPDK apps call port attach function, attached port number
>   will be returned. Also, DPDK apps can detach port by port number.
> - Kernel support is needed for attaching or detaching physical device ports.
>   To attach a new physical device port, the device will be recognized by
>   userspace directly I/O framework in kernel at first. Then DPDK apps can
>   call the port hotplug functions to attach ports.
>   For detaching, steps are vice versa.
> - Before detach ports, ports must be stopped and closed.
>   DPDK application must call rte_eth_dev_stop() and rte_eth_dev_close() before
>   detaching ports. These function will call finalization codes of PMDs.
>   But so far, no PMD frees all resources allocated by initialization.
>   It means PMDs are needed to be fixed to support the port hotplug.
>   'RTE_PCI_DRV_DETACHABLE' is a new flag indicating a PMD supports detaching.
>   Without this flag, detaching will be failed.
> - Mustn't affect legacy DPDK apps.
>   No DPDK EAL behavior is changed, if the port hotplug functions are't called.
>   So all legacy DPDK apps can still work without modifications.
> 
> And a few limitations.
> - The port hotplug functions are not thread safe.
>   DPDK apps should handle it.
> - Only support Linux and igb_uio so far.
>   BSD and VFIO is not supported. I will send VFIO patches at least, but I don't
>   have a plan to submit BSD patch so far.
> 
> 
> Here is port hotplug APIs.
> -------------------------------------------------------------------------------
> /**
>  * Attach a new device.
>  *
>  * @param devargs
>  *   A pointer to a strings array describing the new device
>  *   to be attached. The strings should be a pci address like
>  *   '0000:01:00.0' or virtual device name like 'eth_pcap0'.
>  * @param port_id
>  *  A pointer to a port identifier actually attached.
>  * @return
>  *  0 on success and port_id is filled, negative on error
>  */
> int rte_eth_dev_attach(const char *devargs, uint8_t *port_id);
> 
> /**
>  * Detach a device.
>  *
>  * @param port_id
>  *   The port identifier of the device to detach.
>  * @param addr
>  *  A pointer to a device name actually detached.
>  * @return
>  *  0 on success and devname is filled, negative on error
>  */
> int rte_eth_dev_detach(uint8_t port_id, char *devname);
> -------------------------------------------------------------------------------
> 
> This patch series are for DPDK EAL. To use port hotplug function by DPDK apps,
> each PMD should be fixed to support 'RTE_PCI_DRV_DETACHABLE' flag. Please check
> a patch for pcap PMD.
> 
> Also, please check testpmd patch. It will show you how to fix your legacy
> applications to support port hotplug feature.
> 
> PATCH v15 changes
>  - Fix issue that eal calls ethedv library APIs.
>   - Remove rte_eal_dev_attach(), and add rte_eth_dev_attach().
>   - Remove rte_eal_dev_detach(), and add rte_eth_dev_detach().
>   - Call rte_eal_vdev_init/uninit from ethdev library.
>    (Thanks to Thomas Monjalon)
>  - Fix version.map
>  - Reorder and squash patches to compile correctly.
>  - Remove needless symbols in version.map
>  - Fix version.map.
> 
> PATCH v14 changes
>  - Remove needless if statement.
>    (Thanks to Maxime Leroy)
> 
> PATCH v13 changes
>  - Change log level when error occurs in rte_eal_vdev_init() and
>    rte_eal_dev_init().
>  - Return value of driver init and uninit functions.
>  - Replace rte_panic by RTE_LOG in rte_eal_dev_init()
>  - Fix return value of rte_eal_vdev_uninit().
>  - Fix rte_eal_dev_attach_vdev to set port_id correctly.
>    (Thanks to Maxime Leroy)
> 
> PATCH v12 changes
>  - Add missing symbol in version map.
>    (Thanks to Iremonger, Bernard)
> 
> PATCH v11 changes
>  - Remove needless devargs handling codes.
>  - Replace get_vdev_name() by rte_eal_parse_devargs_str().
>  - Replace rte_eal_vdev_find_and_init by rte_eal_vdev_init()
>  - Replace rte_eal_vdev_find_and_uninit by rte_eal_vdev_uninit()
>  - Fix rte_eal_dev_init() to use rte_eal_vdev_init().
>  - Remove needless patch.
>    (Thanks to Maxime Leroy)
> 
> PATCH v10 changes
>  - Add comments.
>  - Chagne order of version.map.
>  - Fix comment of "rte_ethdev.h".
>    (Thanks to Thomas Monjalon)
>  - Add size parameter to rte_eth_dev_create_unique_device_name().
>    (Thanks to Iremonger, Bernard)
> 
> PATCH v9 changes
>  - Fix commit title.
>  - Fix commit log.
>  - Fix comments.
>  - Define CONFIG_RTE_LIBRTE_EAL_HOTPLUG at the top of this patch series.
>  - DEV_INVALID/VALID are removed.
>  - DEV_DISCONNECTED is replaced by DEV_DETACHED.
>  - DEV_CONNECTED is replaced by DEV_ATTACHED.
>  - rte_eth_dev_allocate_new_port() is renamed to
>    rte_eth_dev_find_free_port().
>  - rte_eth_dev_validate_port() is renamed to rte_eth_dev_is_valid_port().
>  - rte_eth_dev_is_valid_port() is changed not to handle log toggle.
>  - eal_compare_pci_addr() is replaced by rte_eal_compare_pci_addr().
>  - rte_eth_dev_free() is replaced by rte_eth_dev_release_port().
>  - Add a function to create a unique device name.
>  - Change parameter of pci_devuninit_t and rte_eth_dev_uninit.
>  - Remove code that initiaize callback of ethdev from
>    rte_eth_dev_uninit().
>  - Remove pci_unmap_device(). It will be implemented in later patch.
>  - rte_eth_dev_check_detachable() is replaced by
>    rte_eth_dev_is_detachable().
>  - strncpy() is replaced by strcpy().
>  - Implement pci_unmap_device() in this patch.
>  - Remove "rte_dev_hotplug.h".
>  - Remove needless "#ifdef".
>  - Remove RTE_EAL_INVOKE_TYPE_PROBE/CLOSE.
>  - RTE_ETH_DEV_PHYSICAL is replaced by RTE_ETH_DEV_PCI.
>  - Use strcmp() instead of strncmp().
>  - Remove RTE_EAL_INVOKE_TYPE_PROBE/CLOSE.
>    (Thanks to Thomas Monjalon)
>  - Change definition of rte_dev_uninit_t.
>    (Thanks to Thomas Monjalon and Maxime Leroy)
>  - Add missing symbol in version map.
>    (Thanks to Nail Horman)
> 
> PATCH v8 changes
>  - Fix Makefile and add version map file.
>  - Add missing symbol in version map.
>  - Fix pci_scan_one() to update sysfs values.
>    (Thanks to Qiu, Michael and Iremonger, Bernard)
>  - NONE_TRACE is replaced by NO_TRACE.
>  - Fix typo.
>  - Add size parameter to rte_eth_dev_save().
>    (Thanks to Iremonger, Bernard)
> 
> PATCH v7 changes
>  - Add a new section to programmer's guide.
>    (Thanks to Iremonger, Bernard)
>  - Fix port checking implementation of star_port().
>  - Fix typo of warning messages.
>  - Add pt_driver checking to rte_eth_dev_check_detachable().
>    (Thanks to Qiu, Michael)
> 
> PATCH v6 changes
>  - Fix rte_eth_dev_uninit() to handle a return value of uninit
>    function of PMD. To do this, below changes also be applied.
>    - Fix a parameter of rte_eth_dev_free().
>    - Use rte_eth_dev structure as the paramter of rte_eth_dev_free().
> 
> PATCH v5 changes
>  - Add runtime check passthrough driver type, like vfio-pci, igb_uio
>    and uio_pci_generic.
>    This was done by Qiu, Michael. Thanks a lot.
>  - Change function names like below.
>    - rte_eal_dev_find_and_invoke() to rte_eal_vdev_find_and_invoke().
>    - rte_eal_dev_invoke() to rte_eal_vdev_invoke().
>  - Add code to handle a return value of rte_eal_devargs_remove().
>  - Fix pci address format in rte_eal_dev_detach().
>  - Remove RTE_EAL_INVOKE_TYPE_UNKNOWN, because it's unused.
>  - Change function definition of rte_eal_devargs_remove().
>  - Fix pci_unmap_device() to check pt_driver.
>  - Fix return value of below functions.
>    - rte_eth_dev_get_changed_port().
>    - rte_eth_dev_get_port_by_addr().
>  - Change paramters of rte_eth_dev_validate_port() to cleanup code.
>  - Fix pci_scan_one to handle pt_driver correctly.
>    (Thanks to Qiu, Michael for above suggestions)
> 
> PATCH v4 changes
>  - Merge patches to review easier.
>  - Fix indent of 'if' statement.
>  - Fix calculation method of eal_compare_pci_addr().
>  - Fix header file declaration.
>  - Add header file to determine if hotplug can be enabled.
>    (Thanks to Qiu, Michael)
>  - Use braces with 'for' loop.
>  - Add parameter checking.
>  - Fix sanity check code
>  - Fix comments of rte_eth_dev_type.
>  - Change function names.
>    (Thanks to Iremonger, Bernard)
> 
> PATCH v3 changes:
>  - Fix enum definition used in rte_ethdev.c.
>    (Thanks to Zhang, Helin)
> 
> PATCH v2 changes:
>  - Replace rte_eal_dev_attach_pdev(), rte_eal_dev_detach_pdev,
>    rte_eal_dev_attach_vdev() and rte_eal_dev_detach_vdev() to
>    rte_eal_dev_attach() and rte_eal_dev_detach().
>  - Add parameter values checking.
>  - Refashion a few functions.
>    (Thanks to Iremonger, Bernard)
> 
> PATCH v1 Changes:
>  - Fix error checking code of librte_eth APIs.
>  - Fix issue that port from pcap PMD cannot be detached correctly.
>  - Fix issue that testpmd could hang after forwarding, if attaching and detaching
>    is repeatedly.
>  - Fix if-condition of rte_eth_dev_get_port_by_addr().
>    (Thanks to Mark Enright)
> 
> RFC PATCH v2 Changes:
> - remove 'rte_eth_dev_validate_port()', and cleanup codes.
> 
> 
> Michael Qiu (2):
>   eal_pci: Add flag to hold kernel driver type
>   eal_pci: pci memory map work with driver type
> 
> Tetsuya Mukawa (11):
>   eal: Enable port Hotplug framework in Linux
>   eal/pci,ethdev: Remove assumption that port will not be detached
>   eal/pci: Consolidate pci address comparison APIs
>   ethdev: Add rte_eth_dev_release_port to release specified port
>   eal,ethdev: Add a function and function pointers to close ether device
>   eal/linux/pci: Add functions for unmapping igb_uio resources
>   eal/pci: Add probe and close functions of pci driver
>   ethdev: Add one dev_type parameter to rte_eth_dev_allocate
>   eal/pci: Add vdev driver initialization and uninitialization functions
>   ethdev: Add rte_eth_dev_attach/detach() functions
>   doc: Add port hotplug framework section to programmers guide

Applied, thanks Tetsuya for this long work
  

Patch

diff --git a/lib/librte_eal/common/eal_common_dev.c b/lib/librte_eal/common/eal_common_dev.c
index eae5656..6d805aa 100644
--- a/lib/librte_eal/common/eal_common_dev.c
+++ b/lib/librte_eal/common/eal_common_dev.c
@@ -32,10 +32,13 @@ 
  *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
+#include <stdio.h>
+#include <limits.h>
 #include <string.h>
 #include <inttypes.h>
 #include <sys/queue.h>
 
+#include <rte_ethdev.h>
 #include <rte_dev.h>
 #include <rte_devargs.h>
 #include <rte_debug.h>
@@ -61,6 +64,32 @@  rte_eal_driver_unregister(struct rte_driver *driver)
 	TAILQ_REMOVE(&dev_driver_list, driver, next);
 }
 
+static int
+rte_eal_vdev_init(const char *name, const char *args)
+{
+	struct rte_driver *driver;
+
+	if (name == NULL)
+		return -EINVAL;
+
+	TAILQ_FOREACH(driver, &dev_driver_list, next) {
+		if (driver->type != PMD_VDEV)
+			continue;
+
+		/*
+		 * search a driver prefix in virtual device name.
+		 * For example, if the driver is pcap PMD, driver->name
+		 * will be "eth_pcap", but "name" will be "eth_pcapN".
+		 * So use strncmp to compare.
+		 */
+		if (!strncmp(driver->name, name, strlen(driver->name)))
+			return driver->init(name, args);
+	}
+
+	RTE_LOG(ERR, EAL, "no driver found for %s\n", name);
+	return -EINVAL;
+}
+
 int
 rte_eal_dev_init(void)
 {
@@ -79,22 +108,11 @@  rte_eal_dev_init(void)
 		if (devargs->type != RTE_DEVTYPE_VIRTUAL)
 			continue;
 
-		TAILQ_FOREACH(driver, &dev_driver_list, next) {
-			if (driver->type != PMD_VDEV)
-				continue;
-
-			/* search a driver prefix in virtual device name */
-			if (!strncmp(driver->name, devargs->virtual.drv_name,
-					strlen(driver->name))) {
-				driver->init(devargs->virtual.drv_name,
-					devargs->args);
-				break;
-			}
-		}
-
-		if (driver == NULL) {
-			rte_panic("no driver found for %s\n",
-				  devargs->virtual.drv_name);
+		if (rte_eal_vdev_init(devargs->virtual.drv_name,
+					devargs->args)) {
+			RTE_LOG(ERR, EAL, "failed to initialize %s device\n",
+					devargs->virtual.drv_name);
+			return -1;
 		}
 	}
 
@@ -107,3 +125,231 @@  rte_eal_dev_init(void)
 	}
 	return 0;
 }
+
+/* So far, DPDK hotplug function only supports linux */
+#ifdef RTE_LIBRTE_EAL_HOTPLUG
+static int
+rte_eal_vdev_uninit(const char *name)
+{
+	struct rte_driver *driver;
+
+	if (name == NULL)
+		return -EINVAL;
+
+	TAILQ_FOREACH(driver, &dev_driver_list, next) {
+		if (driver->type != PMD_VDEV)
+			continue;
+
+		/*
+		 * search a driver prefix in virtual device name.
+		 * For example, if the driver is pcap PMD, driver->name
+		 * will be "eth_pcap", but "name" will be "eth_pcapN".
+		 * So use strncmp to compare.
+		 */
+		if (!strncmp(driver->name, name, strlen(driver->name)))
+			return driver->uninit(name);
+	}
+
+	RTE_LOG(ERR, EAL, "no driver found for %s\n", name);
+	return -EINVAL;
+}
+
+/* attach the new physical device, then store port_id of the device */
+static int
+rte_eal_dev_attach_pdev(struct rte_pci_addr *addr, uint8_t *port_id)
+{
+	uint8_t new_port_id;
+	struct rte_eth_dev devs[RTE_MAX_ETHPORTS];
+
+	if ((addr == NULL) || (port_id == NULL))
+		goto err;
+
+	/* save current port status */
+	if (rte_eth_dev_save(devs, sizeof(devs)))
+		goto err;
+	/* re-construct pci_device_list */
+	if (rte_eal_pci_scan())
+		goto err;
+	/* invoke probe func of the driver can handle the new device.
+	 * TODO:
+	 * rte_eal_pci_probe_one() should return port_id.
+	 * And rte_eth_dev_save() and rte_eth_dev_get_changed_port()
+	 * should be removed. */
+	if (rte_eal_pci_probe_one(addr))
+		goto err;
+	/* get port_id enabled by above procedures */
+	if (rte_eth_dev_get_changed_port(devs, &new_port_id))
+		goto err;
+
+	*port_id = new_port_id;
+	return 0;
+err:
+	RTE_LOG(ERR, EAL, "Driver, cannot attach the device\n");
+	return -1;
+}
+
+/* detach the new physical device, then store pci_addr of the device */
+static int
+rte_eal_dev_detach_pdev(uint8_t port_id, struct rte_pci_addr *addr)
+{
+	struct rte_pci_addr freed_addr;
+	struct rte_pci_addr vp;
+
+	if (addr == NULL)
+		goto err;
+
+	/* check whether the driver supports detach feature, or not */
+	if (rte_eth_dev_is_detachable(port_id))
+		goto err;
+
+	/* get pci address by port id */
+	if (rte_eth_dev_get_addr_by_port(port_id, &freed_addr))
+		goto err;
+
+	/* Zerod pci addr means the port comes from virtual device */
+	vp.domain = vp.bus = vp.devid = vp.function = 0;
+	if (rte_eal_compare_pci_addr(&vp, &freed_addr) == 0)
+		goto err;
+
+	/* invoke close func of the driver,
+	 * also remove the device from pci_device_list */
+	if (rte_eal_pci_close_one(&freed_addr))
+		goto err;
+
+	*addr = freed_addr;
+	return 0;
+err:
+	RTE_LOG(ERR, EAL, "Driver, cannot detach the device\n");
+	return -1;
+}
+
+/* attach the new virtual device, then store port_id of the device */
+static int
+rte_eal_dev_attach_vdev(const char *vdevargs, uint8_t *port_id)
+{
+	char *name = NULL, *args = NULL;
+	uint8_t new_port_id;
+	struct rte_eth_dev devs[RTE_MAX_ETHPORTS];
+	int ret = -1;
+
+	if ((vdevargs == NULL) || (port_id == NULL))
+		goto end;
+
+	/* parse vdevargs, then retrieve device name and args */
+	if (rte_eal_parse_devargs_str(vdevargs, &name, &args))
+		goto end;
+
+	/* save current port status */
+	if (rte_eth_dev_save(devs, sizeof(devs)))
+		goto end;
+	/* walk around dev_driver_list to find the driver of the device,
+	 * then invoke probe function o the driver.
+	 * TODO:
+	 * rte_eal_vdev_init() should return port_id,
+	 * And rte_eth_dev_save() and rte_eth_dev_get_changed_port()
+	 * should be removed. */
+	if (rte_eal_vdev_init(name, args))
+		goto end;
+	/* get port_id enabled by above procedures */
+	if (rte_eth_dev_get_changed_port(devs, &new_port_id))
+		goto end;
+	ret = 0;
+	*port_id = new_port_id;
+end:
+	if (name)
+		free(name);
+	if (args)
+		free(args);
+
+	if (ret < 0)
+		RTE_LOG(ERR, EAL, "Driver, cannot attach the device\n");
+	return ret;
+}
+
+/* detach the new virtual device, then store the name of the device */
+static int
+rte_eal_dev_detach_vdev(uint8_t port_id, char *vdevname)
+{
+	char name[RTE_ETH_NAME_MAX_LEN];
+
+	if (vdevname == NULL)
+		goto err;
+
+	/* check whether the driver supports detach feature, or not */
+	if (rte_eth_dev_is_detachable(port_id))
+		goto err;
+
+	/* get device name by port id */
+	if (rte_eth_dev_get_name_by_port(port_id, name))
+		goto err;
+	/* walk around dev_driver_list to find the driver of the device,
+	 * then invoke close function o the driver */
+	if (rte_eal_vdev_uninit(name))
+		goto err;
+
+	strncpy(vdevname, name, sizeof(name));
+	return 0;
+err:
+	RTE_LOG(ERR, EAL, "Driver, cannot detach the device\n");
+	return -1;
+}
+
+/* attach the new device, then store port_id of the device */
+int
+rte_eal_dev_attach(const char *devargs, uint8_t *port_id)
+{
+	struct rte_pci_addr addr;
+
+	if ((devargs == NULL) || (port_id == NULL))
+		return -EINVAL;
+
+	if (eal_parse_pci_DomBDF(devargs, &addr) == 0)
+		return rte_eal_dev_attach_pdev(&addr, port_id);
+	else
+		return rte_eal_dev_attach_vdev(devargs, port_id);
+}
+
+/* detach the device, then store the name of the device */
+int
+rte_eal_dev_detach(uint8_t port_id, char *name)
+{
+	struct rte_pci_addr addr;
+	int ret;
+
+	if (name == NULL)
+		return -EINVAL;
+
+	if (rte_eth_dev_get_device_type(port_id) == RTE_ETH_DEV_PCI) {
+		ret = rte_eth_dev_get_addr_by_port(port_id, &addr);
+		if (ret < 0)
+			return ret;
+
+		ret = rte_eal_dev_detach_pdev(port_id, &addr);
+		if (ret == 0)
+			snprintf(name, RTE_ETH_NAME_MAX_LEN,
+				"%04x:%02x:%02x.%d",
+				addr.domain, addr.bus,
+				addr.devid, addr.function);
+
+		return ret;
+	} else
+		return rte_eal_dev_detach_vdev(port_id, name);
+}
+#else /* RTE_LIBRTE_EAL_HOTPLUG */
+int
+rte_eal_dev_attach(const char *devargs __rte_unused,
+			uint8_t *port_id __rte_unused)
+{
+	RTE_LOG(ERR, EAL, "Hotplug support isn't enabled\n");
+	return -1;
+}
+
+/* detach the device, then store the name of the device */
+int
+rte_eal_dev_detach(uint8_t port_id __rte_unused,
+			char *name __rte_unused)
+{
+	RTE_LOG(ERR, EAL, "Hotplug support isn't enabled\n");
+	return -1;
+}
+#endif /* RTE_LIBRTE_EAL_HOTPLUG */
diff --git a/lib/librte_eal/common/eal_common_devargs.c b/lib/librte_eal/common/eal_common_devargs.c
index eadd719..9b110f7 100644
--- a/lib/librte_eal/common/eal_common_devargs.c
+++ b/lib/librte_eal/common/eal_common_devargs.c
@@ -44,13 +44,46 @@ 
 struct rte_devargs_list devargs_list =
 	TAILQ_HEAD_INITIALIZER(devargs_list);
 
+int
+rte_eal_parse_devargs_str(const char *devargs_str,
+			char **drvname, char **drvargs)
+{
+	char *sep;
+
+	if ((devargs_str) == NULL || (drvname) == NULL || (drvargs == NULL))
+		return -1;
+
+	*drvname = strdup(devargs_str);
+	if (drvname == NULL) {
+		RTE_LOG(ERR, EAL,
+			"cannot allocate temp memory for driver name\n");
+		return -1;
+	}
+
+	/* set the first ',' to '\0' to split name and arguments */
+	sep = strchr(*drvname, ',');
+	if (sep != NULL) {
+		sep[0] = '\0';
+		*drvargs = strdup(sep + 1);
+	} else {
+		*drvargs = strdup("");
+	}
+
+	if (*drvargs == NULL) {
+		RTE_LOG(ERR, EAL,
+			"cannot allocate temp memory for driver arguments\n");
+		free(*drvname);
+		return -1;
+	}
+	return 0;
+}
+
 /* store a whitelist parameter for later parsing */
 int
 rte_eal_devargs_add(enum rte_devtype devtype, const char *devargs_str)
 {
 	struct rte_devargs *devargs = NULL;
 	char *buf = NULL;
-	char *sep;
 	int ret;
 
 	/* use malloc instead of rte_malloc as it's called early at init */
@@ -62,25 +95,8 @@  rte_eal_devargs_add(enum rte_devtype devtype, const char *devargs_str)
 	memset(devargs, 0, sizeof(*devargs));
 	devargs->type = devtype;
 
-	buf = strdup(devargs_str);
-	if (buf == NULL) {
-		RTE_LOG(ERR, EAL, "cannot allocate temp memory for devargs\n");
-		goto fail;
-	}
-
-	/* set the first ',' to '\0' to split name and arguments */
-	sep = strchr(buf, ',');
-	if (sep != NULL) {
-		sep[0] = '\0';
-		devargs->args = strdup(sep + 1);
-	} else {
-		devargs->args = strdup("");
-	}
-
-	if (devargs->args == NULL) {
-		RTE_LOG(ERR, EAL, "cannot allocate for devargs args\n");
+	if (rte_eal_parse_devargs_str(devargs_str, &buf, &devargs->args))
 		goto fail;
-	}
 
 	switch (devargs->type) {
 	case RTE_DEVTYPE_WHITELISTED_PCI:
diff --git a/lib/librte_eal/common/eal_private.h b/lib/librte_eal/common/eal_private.h
index 4acf5a0..98b286a 100644
--- a/lib/librte_eal/common/eal_private.h
+++ b/lib/librte_eal/common/eal_private.h
@@ -154,6 +154,17 @@  struct rte_pci_driver;
 struct rte_pci_device;
 
 /**
+ * Scan the content of the PCI bus, and the devices in the devices
+ * list
+ *
+ * This function is private to EAL.
+ *
+ * @return
+ *  0 on success, negative on error
+ */
+int rte_eal_pci_scan(void);
+
+/**
  * Mmap memory for single PCI device
  *
  * This function is private to EAL.
diff --git a/lib/librte_eal/common/include/rte_dev.h b/lib/librte_eal/common/include/rte_dev.h
index f7e3a10..a5ac770 100644
--- a/lib/librte_eal/common/include/rte_dev.h
+++ b/lib/librte_eal/common/include/rte_dev.h
@@ -47,6 +47,7 @@  extern "C" {
 #endif
 
 #include <sys/queue.h>
+#include <rte_pci.h>
 
 /** Double linked list of device drivers. */
 TAILQ_HEAD(rte_driver_list, rte_driver);
@@ -57,6 +58,11 @@  TAILQ_HEAD(rte_driver_list, rte_driver);
 typedef int (rte_dev_init_t)(const char *name, const char *args);
 
 /**
+ * Uninitilization function called for each device driver once.
+ */
+typedef int (rte_dev_uninit_t)(const char *name);
+
+/**
  * Driver type enumeration
  */
 enum pmd_type {
@@ -72,6 +78,7 @@  struct rte_driver {
 	enum pmd_type type;		   /**< PMD Driver type */
 	const char *name;                   /**< Driver name. */
 	rte_dev_init_t *init;              /**< Device init. function. */
+	rte_dev_uninit_t *uninit;          /**< Device uninit. function. */
 };
 
 /**
@@ -93,6 +100,32 @@  void rte_eal_driver_register(struct rte_driver *driver);
 void rte_eal_driver_unregister(struct rte_driver *driver);
 
 /**
+ * Attach a new device.
+ *
+ * @param devargs
+ *   A pointer to a strings array describing the new device
+ *   to be attached. The strings should be a pci address like
+ *   '0000:01:00.0' or virtual device name like 'eth_pcap0'.
+ * @param port_id
+ *  A pointer to a port identifier actually attached.
+ * @return
+ *  0 on success and port_id is filled, negative on error
+ */
+int rte_eal_dev_attach(const char *devargs, uint8_t *port_id);
+
+/**
+ * Detach a device.
+ *
+ * @param port_id
+ *   The port identifier of the device to detach.
+ * @param addr
+ *  A pointer to a device name actually detached.
+ * @return
+ *  0 on success and devname is filled, negative on error
+ */
+int rte_eal_dev_detach(uint8_t port_id, char *devname);
+
+/**
  * Initalize all the registered drivers in this process
  */
 int rte_eal_dev_init(void);
diff --git a/lib/librte_eal/common/include/rte_devargs.h b/lib/librte_eal/common/include/rte_devargs.h
index 6834333..039f728 100644
--- a/lib/librte_eal/common/include/rte_devargs.h
+++ b/lib/librte_eal/common/include/rte_devargs.h
@@ -99,6 +99,34 @@  TAILQ_HEAD(rte_devargs_list, rte_devargs);
 extern struct rte_devargs_list devargs_list;
 
 /**
+ * Parse a devargs string.
+ *
+ * For PCI devices, the format of arguments string is "PCI_ADDR" or
+ * "PCI_ADDR,key=val,key2=val2,...". Examples: "08:00.1", "0000:5:00.0",
+ * "04:00.0,arg=val".
+ *
+ * For virtual devices, the format of arguments string is "DRIVER_NAME*"
+ * or "DRIVER_NAME*,key=val,key2=val2,...". Examples: "eth_ring",
+ * "eth_ring0", "eth_pmdAnything,arg=0:arg2=1".
+ *
+ * The function parses the arguments string to get driver name and driver
+ * arguments.
+ *
+ * @param devargs_str
+ *   The arguments as given by the user.
+ * @param drvname
+ *   The pointer to the string to store parsed driver name.
+ * @param drvargs
+ *   The pointer to the string to store parsed driver arguments.
+ *
+ * @return
+ *   - 0 on success
+ *   - A negative value on error
+ */
+int rte_eal_parse_devargs_str(const char *devargs_str,
+				char **drvname, char **drvargs);
+
+/**
  * Add a device to the user device list
  *
  * For PCI devices, the format of arguments string is "PCI_ADDR" or
diff --git a/lib/librte_eal/linuxapp/eal/Makefile b/lib/librte_eal/linuxapp/eal/Makefile
index 23c2d48..a30bbf7 100644
--- a/lib/librte_eal/linuxapp/eal/Makefile
+++ b/lib/librte_eal/linuxapp/eal/Makefile
@@ -45,6 +45,7 @@  CFLAGS += -I$(RTE_SDK)/lib/librte_eal/common/include
 CFLAGS += -I$(RTE_SDK)/lib/librte_ring
 CFLAGS += -I$(RTE_SDK)/lib/librte_mempool
 CFLAGS += -I$(RTE_SDK)/lib/librte_malloc
+CFLAGS += -I$(RTE_SDK)/lib/librte_mbuf
 CFLAGS += -I$(RTE_SDK)/lib/librte_ether
 CFLAGS += -I$(RTE_SDK)/lib/librte_ivshmem
 CFLAGS += -I$(RTE_SDK)/lib/librte_pmd_ring
diff --git a/lib/librte_eal/linuxapp/eal/eal_pci.c b/lib/librte_eal/linuxapp/eal/eal_pci.c
index f880f90..6d4932d 100644
--- a/lib/librte_eal/linuxapp/eal/eal_pci.c
+++ b/lib/librte_eal/linuxapp/eal/eal_pci.c
@@ -440,8 +440,8 @@  error:
  * Scan the content of the PCI bus, and the devices in the devices
  * list
  */
-static int
-pci_scan(void)
+int
+rte_eal_pci_scan(void)
 {
 	struct dirent *e;
 	DIR *dir;
@@ -773,7 +773,7 @@  rte_eal_pci_init(void)
 	if (internal_config.no_pci)
 		return 0;
 
-	if (pci_scan() < 0) {
+	if (rte_eal_pci_scan() < 0) {
 		RTE_LOG(ERR, EAL, "%s(): Cannot scan PCI bus\n", __func__);
 		return -1;
 	}
diff --git a/lib/librte_eal/linuxapp/eal/rte_eal_version.map b/lib/librte_eal/linuxapp/eal/rte_eal_version.map
index c207cee..d2f7ded 100644
--- a/lib/librte_eal/linuxapp/eal/rte_eal_version.map
+++ b/lib/librte_eal/linuxapp/eal/rte_eal_version.map
@@ -20,6 +20,8 @@  DPDK_2.0 {
 	rte_dump_tailq;
 	rte_eal_alarm_cancel;
 	rte_eal_alarm_set;
+	rte_eal_dev_attach;
+	rte_eal_dev_detach;
 	rte_eal_dev_init;
 	rte_eal_devargs_add;
 	rte_eal_devargs_dump;