[dpdk-dev,v5,7/7] net/ark: Arkville PMD component integration

Message ID 3d68039422963c92ef6f05ad4bd9b0b3497eb7bd.1490309515.git.ed.czeck@atomicrules.com (mailing list archive)
State Superseded, archived
Delegated to: Ferruh Yigit
Headers

Checks

Context Check Description
ci/checkpatch success coding style OK
ci/Intel-compilation success Compilation OK

Commit Message

Ed Czeck March 23, 2017, 11:01 p.m. UTC
  * Flesh out device configuration
* Add links dev_ops
* allow dynamic extension loading

Signed-off-by: Shepard Siegel <shepard.siegel@atomicrules.com>
Signed-off-by: John Miller <john.miller@atomicrules.com>
Signed-off-by: Ed Czeck <ed.czeck@atomicrules.com>

Conflicts:
	drivers/net/ark/ark_ethdev.c
---
 doc/guides/nics/features/ark.ini |  15 +
 drivers/net/ark/ark_ethdev.c     | 707 ++++++++++++++++++++++++++++++++++++++-
 drivers/net/ark/ark_ext.h        |  79 +++++
 drivers/net/ark/ark_global.h     |  23 ++
 4 files changed, 818 insertions(+), 6 deletions(-)
 create mode 100644 doc/guides/nics/features/ark.ini
 create mode 100644 drivers/net/ark/ark_ext.h
  

Comments

Ferruh Yigit March 28, 2017, 2:38 p.m. UTC | #1
On 3/23/2017 11:01 PM, Ed Czeck wrote:
> * Flesh out device configuration
> * Add links dev_ops
> * allow dynamic extension loading
> 
> Signed-off-by: Shepard Siegel <shepard.siegel@atomicrules.com>
> Signed-off-by: John Miller <john.miller@atomicrules.com>
> Signed-off-by: Ed Czeck <ed.czeck@atomicrules.com>

<...>

> +[Features]
> +Queue start/stop     = Y
> +Jumbo frame          = Y
> +Scattered Rx         = Y
> +Basic stats          = Y
> +Stats per queue      = Y

> +FW version           = Y

FW version is not required, as far as I can see, it requires
fw_version_get eth_dev_ops implemented.

<...>

> +	/* We are a single function multi-port device. */
> +	const unsigned int numa_node = rte_socket_id();
> +	struct ether_addr adr;
> +
> +	ret = ark_config_device(dev);
>  	dev->dev_ops = &ark_eth_dev_ops;
>  
> +	dev->data->mac_addrs = rte_zmalloc("ark", ETHER_ADDR_LEN, 0);
> +	if (!dev->data->mac_addrs) {
> +		PMD_DRV_LOG(ERR,
> +			    "Failed to allocated memory for storing mac address"
> +			    );
> +	}
> +	ether_addr_copy((struct ether_addr *)&adr, &dev->data->mac_addrs[0]);

"adr" has random value at this point, right? Why to copy it?


> +	/*
> +	 * We will create additional devices based on the number of requested
> +	 * ports
> +	 */
> +	int pc = 1;
> +	int p;

I am aware some people prefer the declaring variables close to context,
which is good idea, but if I remember correct, there was a patchset,
from Adrien, to make DPDK C99 compatible, will this break it?

> +
> +	if (ark->user_ext.dev_get_port_count) {
> +		pc = ark->user_ext.dev_get_port_count(dev, ark->user_data);
> +		ark->num_ports = pc;
> +	} else {
> +		ark->num_ports = 1;

Because pc has default value of "1", this if statement can be simplified.

> +	}
> +	for (p = 0; p < pc; p++) {
> +		struct ark_port *port;
> +
> +		port = &ark->port[p];
> +		struct rte_eth_dev_data *data = NULL;
> +
> +		port->id = p;
> +
> +		char name[RTE_ETH_NAME_MAX_LEN];
> +
> +		snprintf(name, sizeof(name), "arketh%d",
> +			 dev->data->port_id + p);
> +
> +		if (p == 0) {
> +			/* First port is already allocated by DPDK */
> +			port->eth_dev = ark->eth_dev;
> +			continue;
> +		}
> +
> +		/* reserve an ethdev entry */
> +		port->eth_dev = rte_eth_dev_allocate(name);
> +		if (!port->eth_dev) {
> +			PMD_DRV_LOG(ERR,
> +				    "Could not allocate eth_dev for port %d\n",
> +				    p);
> +			goto error;
> +		}
> +
> +		data = rte_zmalloc_socket(name, sizeof(*data), 0, numa_node);
> +		if (!data) {
> +			PMD_DRV_LOG(ERR,
> +				    "Could not allocate eth_dev for port %d\n",
> +				    p);
> +			goto error;
> +		}
> +		data->port_id = ark->eth_dev->data->port_id + p;

"ark->eth_dev->data->port_id" is port_id of the first physical ARK
device, and it looks like each device may have multiple ports and "p" is
the port_id within same device.

From DPDK point of view, port_id is a global value incremented one by
each eth port, so port_id is a unique value, why adding these two values?

> +		port->eth_dev->data = data;

Why overwriting existing data value?

> +		port->eth_dev->device = &pci_dev->device;
> +		port->eth_dev->data->dev_private = ark;
> +		port->eth_dev->driver = ark->eth_dev->driver;
> +		port->eth_dev->dev_ops = ark->eth_dev->dev_ops;
> +		port->eth_dev->tx_pkt_burst = ark->eth_dev->tx_pkt_burst;
> +		port->eth_dev->rx_pkt_burst = ark->eth_dev->rx_pkt_burst;
> +
> +		rte_eth_copy_pci_info(port->eth_dev, pci_dev);
> +
> +		port->eth_dev->data->mac_addrs =
> +			rte_zmalloc(name, ETHER_ADDR_LEN, 0);
> +		if (!port->eth_dev->data->mac_addrs) {
> +			PMD_DRV_LOG(ERR,
> +				    "Memory allocation for MAC failed!"
> +				    " Exiting.\n");
> +			goto error;
> +		}
> +		ether_addr_copy(&adr,
> +				&port->eth_dev->data->mac_addrs[0]);
> +
> +		if (ark->user_ext.dev_init)
> +			ark->user_data =
> +				ark->user_ext.dev_init(dev, ark->a_bar, p);
> +	}

<...>

>  static int
>  eth_ark_dev_uninit(struct rte_eth_dev *dev)
>  {
> +	struct ark_adapter *ark =
> +		(struct ark_adapter *)dev->data->dev_private;
> +
>  	if (rte_eal_process_type() != RTE_PROC_PRIMARY)
>  		return 0;
>  
> +	if (ark->user_ext.dev_uninit)
> +		ark->user_ext.dev_uninit(dev, ark->user_data);
> +
> +	ark_pktgen_uninit(ark->pg);
> +	ark_pktchkr_uninit(ark->pc);
> +
>  	dev->dev_ops = NULL;
>  	dev->rx_pkt_burst = NULL;
>  	dev->tx_pkt_burst = NULL;
> +	if (dev->data->mac_addrs)
> +		rte_free(dev->data->mac_addrs);
> +	if (dev->data)
> +		rte_free(dev->data);
> +

Shouldn't uninit go thorough all ports ("for (p = 0; p < pc; p++) ") and
uninit them all?

>  	return 0;
>  }
>  

<...>

> +/*
> + * The following functions are optional and are directly mapped
> + * from the DPDK PMD ops structure.
> + * Each function if implemented is called after the ARK PMD
> + * implementation executes.
> + */
> +int dev_configure(struct rte_eth_dev *dev, void *user_data);
> +int dev_start(struct rte_eth_dev *dev, void *user_data);
> +void dev_stop(struct rte_eth_dev *dev, void *user_data);
> +void dev_close(struct rte_eth_dev *dev, void *user_data);
> +int link_update(struct rte_eth_dev *dev, int wait_to_complete,
> +	void *user_data);
> +int dev_set_link_up(struct rte_eth_dev *dev, void *user_data);
> +int dev_set_link_down(struct rte_eth_dev *dev, void *user_data);
> +void stats_get(struct rte_eth_dev *dev, struct rte_eth_stats *stats,
> +	void *user_data);
> +void stats_reset(struct rte_eth_dev *dev, void *user_data);
> +void mac_addr_add(struct rte_eth_dev *dev,
> +	struct ether_addr *macadr,
> +				  uint32_t index,
> +				  uint32_t pool,
> +				  void *user_data);
> +void mac_addr_remove(struct rte_eth_dev *dev, uint32_t index, void *user_data);
> +void mac_addr_set(struct rte_eth_dev *dev, struct ether_addr *mac_addr,
> +	void *user_data);

Where these functions are implemented? Do we need these declerations?
  
Adrien Mazarguil March 28, 2017, 3 p.m. UTC | #2
On Tue, Mar 28, 2017 at 03:38:13PM +0100, Ferruh Yigit wrote:
> On 3/23/2017 11:01 PM, Ed Czeck wrote:
> > * Flesh out device configuration
> > * Add links dev_ops
> > * allow dynamic extension loading
> > 
> > Signed-off-by: Shepard Siegel <shepard.siegel@atomicrules.com>
> > Signed-off-by: John Miller <john.miller@atomicrules.com>
> > Signed-off-by: Ed Czeck <ed.czeck@atomicrules.com>
[...]
> > +	/*
> > +	 * We will create additional devices based on the number of requested
> > +	 * ports
> > +	 */
> > +	int pc = 1;
> > +	int p;
> 
> I am aware some people prefer the declaring variables close to context,
> which is good idea, but if I remember correct, there was a patchset,
> from Adrien, to make DPDK C99 compatible, will this break it?
[...]

Only replying to this part, declaring variables anywhere is fine since C99,
so no problem. This is not the preferred style in DPDK as abusing this
sometimes makes the code less readable (can lead to super large functions
without consistent code blocks).
  
Ed Czeck March 28, 2017, 10:42 p.m. UTC | #3
On Tue, Mar 28, 2017 at 10:38 AM, Ferruh Yigit <ferruh.yigit@intel.com>
wrote:

> On 3/23/2017 11:01 PM, Ed Czeck wrote:
> > * Flesh out device configuration
> > * Add links dev_ops
> > * allow dynamic extension loading
> >
> > Signed-off-by: Shepard Siegel <shepard.siegel@atomicrules.com>
> > Signed-off-by: John Miller <john.miller@atomicrules.com>
> > Signed-off-by: Ed Czeck <ed.czeck@atomicrules.com>
>
> <...>
>
> > +FW version           = Y
>
> FW version is not required, as far as I can see, it requires
> fw_version_get eth_dev_ops implemented.
>
Entry removed.


>
> <...>
>
> > +     /* We are a single function multi-port device. */
> > +     const unsigned int numa_node = rte_socket_id();
> > +     struct ether_addr adr;
> > +
> > +     ret = ark_config_device(dev);
> >       dev->dev_ops = &ark_eth_dev_ops;
> >
> > +     dev->data->mac_addrs = rte_zmalloc("ark", ETHER_ADDR_LEN, 0);
> > +     if (!dev->data->mac_addrs) {
> > +             PMD_DRV_LOG(ERR,
> > +                         "Failed to allocated memory for storing mac
> address"
> > +                         );
> > +     }
> > +     ether_addr_copy((struct ether_addr *)&adr,
> &dev->data->mac_addrs[0]);
>
> "adr" has random value at this point, right? Why to copy it?
>
Code removed.   The mac address is left as 0, since arkville does not
include a mac.


> > +     int pc = 1;
> > +     int p;
>
> I am aware some people prefer the declaring variables close to context,
> which is good idea, but if I remember correct, there was a patchset,
> from Adrien, to make DPDK C99 compatible, will this break it?
>
I moved the declarations to the top of the function to match the dpdk style.



> > +
> > +     if (ark->user_ext.dev_get_port_count) {
> > +             pc = ark->user_ext.dev_get_port_count(dev,
> ark->user_data);
> > +             ark->num_ports = pc;
> > +     } else {
> > +             ark->num_ports = 1;
>
> Because pc has default value of "1", this if statement can be simplified.
>
Code has been simplified to 3 lines.


>
> > +     }
> > +     for (p = 0; p < pc; p++) {
> > +             struct ark_port *port;
> > +
> > +             port = &ark->port[p];
> > +             struct rte_eth_dev_data *data = NULL;
> > +
> > +             port->id = p;
> > +
> > +             char name[RTE_ETH_NAME_MAX_LEN];
> > +
> > +             snprintf(name, sizeof(name), "arketh%d",
> > +                      dev->data->port_id + p);
> > +
> > +             if (p == 0) {
> > +                     /* First port is already allocated by DPDK */
> > +                     port->eth_dev = ark->eth_dev;
> > +                     continue;
> > +             }
> > +
> > +             /* reserve an ethdev entry */
> > +             port->eth_dev = rte_eth_dev_allocate(name);
> > +             if (!port->eth_dev) {
> > +                     PMD_DRV_LOG(ERR,
> > +                                 "Could not allocate eth_dev for port
> %d\n",
> > +                                 p);
> > +                     goto error;
> > +             }
> > +
> > +             data = rte_zmalloc_socket(name, sizeof(*data), 0,
> numa_node);
> > +             if (!data) {
> > +                     PMD_DRV_LOG(ERR,
> > +                                 "Could not allocate eth_dev for port
> %d\n",
> > +                                 p);
> > +                     goto error;
> > +             }
> > +             data->port_id = ark->eth_dev->data->port_id + p;
>
> "ark->eth_dev->data->port_id" is port_id of the first physical ARK
> device, and it looks like each device may have multiple ports and "p" is
> the port_id within same device.
>
Arkville is a single PCIE function with 1 or more ports, lets call this p.
After initialization each there will be p additional devices, and each
device will have exactly 1 port.
We now understand the features of the rte_eth_dev_allocate(), and have
simplified this code avoid the unnecessary copies.


>
> From DPDK point of view, port_id is a global value incremented one by
> each eth port, so port_id is a unique value, why adding these two values?
>
> > +             port->eth_dev->data = data;
>
> Why overwriting existing data value?
>
> > +             port->eth_dev->device = &pci_dev->device;
> > +
>
> > +                             ark->user_ext.dev_init(dev, ark->a_bar, p);
> > +     }
>
Unnecessary coping has been removed.




>
> <...>
>
> >  static int
> >  eth_ark_dev_uninit(struct rte_eth_dev *dev)
> >  {
>
>
> Shouldn't uninit go thorough all ports ("for (p = 0; p < pc; p++) ") and
> uninit them all?
>
 Arkville has one PCIE function, during initialization we  determine the
number of ports, and from that one device for each port will be created.
At the end, there will be p devices, each with one port.   So during device
uninit, there is only 1 port.


>
>
> <...>
>
> > +/*
> > + * The following functions are optional and are directly mapped
> > + * from the DPDK PMD ops structure.
> > + * Each function if implemented is called after the ARK PMD
> > + * implementation executes.
> > + */
> > +int dev_configure(struct rte_eth_dev *dev, void *user_data);
> > +int dev_start(struct rte_eth_dev *dev, void *user_data);
> > +void dev_stop(struct rte_eth_dev *dev, void *user_data);
> > +void dev_close(struct rte_eth_dev *dev, void *user_data);
> > +int link_update(struct rte_eth_dev *dev, int wait_to_complete,
> > +     void *user_data);
> > +int dev_set_link_up(struct rte_eth_dev *dev, void *user_data);
> > +int dev_set_link_down(struct rte_eth_dev *dev, void *user_data);
> > +void stats_get(struct rte_eth_dev *dev, struct rte_eth_stats *stats,
> > +     void *user_data);
> > +void stats_reset(struct rte_eth_dev *dev, void *user_data);
> > +void mac_addr_add(struct rte_eth_dev *dev,
> > +     struct ether_addr *macadr,
> > +                               uint32_t index,
> > +                               uint32_t pool,
> > +                               void *user_data);
> > +void mac_addr_remove(struct rte_eth_dev *dev, uint32_t index, void
> *user_data);
> > +void mac_addr_set(struct rte_eth_dev *dev, struct ether_addr *mac_addr,
> > +     void *user_data);
>
> Where these functions are implemented? Do we need these declarations?
>
The following comment has been added to this file, which addresses your
question.
/*
 * This is the template file for users who which to define a dynamic
 * extension to the Arkville PMD.   User's who create an extension
 * should include this file and define the necessary and desired
 * functions.
 * Only 1 function is required for an extension, dev_init(); all other
 * functions prototyped in this file are optional.
 */

Thanks
Ed.
  

Patch

diff --git a/doc/guides/nics/features/ark.ini b/doc/guides/nics/features/ark.ini
new file mode 100644
index 0000000..dc8a0e2
--- /dev/null
+++ b/doc/guides/nics/features/ark.ini
@@ -0,0 +1,15 @@ 
+;
+; Supported features of the 'ark' poll mode driver.
+;
+; Refer to default.ini for the full list of available PMD features.
+;
+[Features]
+Queue start/stop     = Y
+Jumbo frame          = Y
+Scattered Rx         = Y
+Basic stats          = Y
+Stats per queue      = Y
+FW version           = Y
+Linux UIO            = Y
+x86-64               = Y
+Usage doc            = Y
diff --git a/drivers/net/ark/ark_ethdev.c b/drivers/net/ark/ark_ethdev.c
index 0a47543..008c0c9 100644
--- a/drivers/net/ark/ark_ethdev.c
+++ b/drivers/net/ark/ark_ethdev.c
@@ -40,14 +40,42 @@ 
 #include "ark_global.h"
 #include "ark_debug.h"
 #include "ark_ethdev.h"
+#include "ark_ethdev_tx.h"
+#include "ark_ethdev_rx.h"
+#include "ark_mpu.h"
+#include "ark_ddm.h"
+#include "ark_udm.h"
+#include "ark_rqp.h"
+#include "ark_pktdir.h"
+#include "ark_pktgen.h"
+#include "ark_pktchkr.h"
 
 /*  Internal prototypes */
 static int eth_ark_check_args(struct ark_adapter *ark, const char *params);
 static int eth_ark_dev_init(struct rte_eth_dev *dev);
+static int ark_config_device(struct rte_eth_dev *dev);
 static int eth_ark_dev_uninit(struct rte_eth_dev *eth_dev);
 static int eth_ark_dev_configure(struct rte_eth_dev *dev);
+static int eth_ark_dev_start(struct rte_eth_dev *dev);
+static void eth_ark_dev_stop(struct rte_eth_dev *dev);
+static void eth_ark_dev_close(struct rte_eth_dev *dev);
 static void eth_ark_dev_info_get(struct rte_eth_dev *dev,
 				 struct rte_eth_dev_info *dev_info);
+static int eth_ark_dev_link_update(struct rte_eth_dev *dev,
+				   int wait_to_complete);
+static int eth_ark_dev_set_link_up(struct rte_eth_dev *dev);
+static int eth_ark_dev_set_link_down(struct rte_eth_dev *dev);
+static void eth_ark_dev_stats_get(struct rte_eth_dev *dev,
+				  struct rte_eth_stats *stats);
+static void eth_ark_dev_stats_reset(struct rte_eth_dev *dev);
+static void eth_ark_set_default_mac_addr(struct rte_eth_dev *dev,
+					 struct ether_addr *mac_addr);
+static void eth_ark_macaddr_add(struct rte_eth_dev *dev,
+				struct ether_addr *mac_addr,
+				uint32_t index,
+				uint32_t pool);
+static void eth_ark_macaddr_remove(struct rte_eth_dev *dev,
+				   uint32_t index);
 
 #define ARK_DEV_TO_PCI(eth_dev)			\
 	RTE_DEV_TO_PCI((eth_dev)->device)
@@ -106,10 +134,111 @@  static void eth_ark_dev_info_get(struct rte_eth_dev *dev,
 
 static const struct eth_dev_ops ark_eth_dev_ops = {
 	.dev_configure = eth_ark_dev_configure,
+	.dev_start = eth_ark_dev_start,
+	.dev_stop = eth_ark_dev_stop,
+	.dev_close = eth_ark_dev_close,
+
 	.dev_infos_get = eth_ark_dev_info_get,
+
+	.rx_queue_setup = eth_ark_dev_rx_queue_setup,
+	.rx_queue_count = eth_ark_dev_rx_queue_count,
+	.tx_queue_setup = eth_ark_tx_queue_setup,
+
+	.link_update = eth_ark_dev_link_update,
+	.dev_set_link_up = eth_ark_dev_set_link_up,
+	.dev_set_link_down = eth_ark_dev_set_link_down,
+
+	.rx_queue_start = eth_ark_rx_start_queue,
+	.rx_queue_stop = eth_ark_rx_stop_queue,
+
+	.tx_queue_start = eth_ark_tx_queue_start,
+	.tx_queue_stop = eth_ark_tx_queue_stop,
+
+	.stats_get = eth_ark_dev_stats_get,
+	.stats_reset = eth_ark_dev_stats_reset,
+
+	.mac_addr_add = eth_ark_macaddr_add,
+	.mac_addr_remove = eth_ark_macaddr_remove,
+	.mac_addr_set = eth_ark_set_default_mac_addr,
 };
 
 static int
+check_for_ext(struct ark_adapter *ark)
+{
+	int found = 0;
+
+	/* Get the env */
+	const char *dllpath = getenv("ARK_EXT_PATH");
+
+	if (dllpath == NULL) {
+		ARK_DEBUG_TRACE("ARK EXT NO dll path specified\n");
+		return 0;
+	}
+	ARK_DEBUG_TRACE("ARK EXT found dll path at %s\n", dllpath);
+
+	/* Open and load the .so */
+	ark->d_handle = dlopen(dllpath, RTLD_LOCAL | RTLD_LAZY);
+	if (ark->d_handle == NULL)
+		PMD_DRV_LOG(ERR, "Could not load user extension %s\n", dllpath);
+	else
+		ARK_DEBUG_TRACE("SUCCESS: loaded user extension %s\n", dllpath);
+
+	/* Get the entry points */
+	ark->user_ext.dev_init =
+		(void *(*)(struct rte_eth_dev *, void *, int))
+		dlsym(ark->d_handle, "dev_init");
+	ARK_DEBUG_TRACE("device ext init pointer = %p\n",
+			ark->user_ext.dev_init);
+	ark->user_ext.dev_get_port_count =
+		(int (*)(struct rte_eth_dev *, void *))
+		dlsym(ark->d_handle, "dev_get_port_count");
+	ark->user_ext.dev_uninit =
+		(void (*)(struct rte_eth_dev *, void *))
+		dlsym(ark->d_handle, "dev_uninit");
+	ark->user_ext.dev_configure =
+		(int (*)(struct rte_eth_dev *, void *))
+		dlsym(ark->d_handle, "dev_configure");
+	ark->user_ext.dev_start =
+		(int (*)(struct rte_eth_dev *, void *))
+		dlsym(ark->d_handle, "dev_start");
+	ark->user_ext.dev_stop =
+		(void (*)(struct rte_eth_dev *, void *))
+		dlsym(ark->d_handle, "dev_stop");
+	ark->user_ext.dev_close =
+		(void (*)(struct rte_eth_dev *, void *))
+		dlsym(ark->d_handle, "dev_close");
+	ark->user_ext.link_update =
+		(int (*)(struct rte_eth_dev *, int, void *))
+		dlsym(ark->d_handle, "link_update");
+	ark->user_ext.dev_set_link_up =
+		(int (*)(struct rte_eth_dev *, void *))
+		dlsym(ark->d_handle, "dev_set_link_up");
+	ark->user_ext.dev_set_link_down =
+		(int (*)(struct rte_eth_dev *, void *))
+		dlsym(ark->d_handle, "dev_set_link_down");
+	ark->user_ext.stats_get =
+		(void (*)(struct rte_eth_dev *, struct rte_eth_stats *,
+			  void *))
+		dlsym(ark->d_handle, "stats_get");
+	ark->user_ext.stats_reset =
+		(void (*)(struct rte_eth_dev *, void *))
+		dlsym(ark->d_handle, "stats_reset");
+	ark->user_ext.mac_addr_add =
+		(void (*)(struct rte_eth_dev *, struct ether_addr *, uint32_t,
+			  uint32_t, void *))
+		dlsym(ark->d_handle, "mac_addr_add");
+	ark->user_ext.mac_addr_remove =
+		(void (*)(struct rte_eth_dev *, uint32_t, void *))
+		dlsym(ark->d_handle, "mac_addr_remove");
+	ark->user_ext.mac_addr_set =
+		(void (*)(struct rte_eth_dev *, struct ether_addr *,
+			  void *))
+		dlsym(ark->d_handle, "mac_addr_set");
+
+	return found;
+}
+
+static int
 eth_ark_dev_init(struct rte_eth_dev *dev)
 {
 	struct ark_adapter *ark =
@@ -121,55 +250,489 @@  static void eth_ark_dev_info_get(struct rte_eth_dev *dev,
 
 	ARK_DEBUG_TRACE("eth_ark_dev_init(struct rte_eth_dev *dev)\n");
 
+	/* Check to see if there is an extension that we need to load */
+	check_for_ext(ark);
 	pci_dev = ARK_DEV_TO_PCI(dev);
 	rte_eth_copy_pci_info(dev, pci_dev);
 
-	if (pci_dev->device.devargs)
-		eth_ark_check_args(ark, pci_dev->device.devargs->args);
-	else
-		PMD_DRV_LOG(INFO, "No Device args found\n");
-
+	/* Use dummy function until setup */
+	dev->rx_pkt_burst = &eth_ark_recv_pkts_noop;
+	dev->tx_pkt_burst = &eth_ark_xmit_pkts_noop;
 
 	ark->bar0 = (uint8_t *)pci_dev->mem_resource[0].addr;
 	ark->a_bar = (uint8_t *)pci_dev->mem_resource[2].addr;
 
+	ark->sysctrl.v  = (void *)&ark->bar0[ARK_SYSCTRL_BASE];
+	ark->mpurx.v  = (void *)&ark->bar0[ARK_MPU_RX_BASE];
+	ark->udm.v  = (void *)&ark->bar0[ARK_UDM_BASE];
+	ark->mputx.v  = (void *)&ark->bar0[ARK_MPU_TX_BASE];
+	ark->ddm.v  = (void *)&ark->bar0[ARK_DDM_BASE];
+	ark->cmac.v  = (void *)&ark->bar0[ARK_CMAC_BASE];
+	ark->external.v  = (void *)&ark->bar0[ARK_EXTERNAL_BASE];
+	ark->pktdir.v  = (void *)&ark->bar0[ARK_PKTDIR_BASE];
+	ark->pktgen.v  = (void *)&ark->bar0[ARK_PKTGEN_BASE];
+	ark->pktchkr.v  = (void *)&ark->bar0[ARK_PKTCHKR_BASE];
+
+	ark->rqpacing =
+		(struct ark_rqpace_t *)(ark->bar0 + ARK_RCPACING_BASE);
+	ark->started = 0;
+
+	ARK_DEBUG_TRACE
+		("Sys Ctrl Const = 0x%x  HW Commit_ID: %08x\n",
+		 ark->sysctrl.t32[4],
+		 rte_be_to_cpu_32(ark->sysctrl.t32[0x20 / 4]));
+	PMD_DRV_LOG(INFO, "ARKP PMD  HW Commit_ID: %08x\n",
+		    rte_be_to_cpu_32(ark->sysctrl.t32[0x20 / 4]));
+
+	/* If HW sanity test fails, return an error */
+	if (ark->sysctrl.t32[4] != 0xcafef00d) {
+		PMD_DRV_LOG(ERR,
+			    "HW Sanity test has failed, expected constant"
+			    " 0x%x, read 0x%x (%s)\n",
+			    0xcafef00d,
+			    ark->sysctrl.t32[4], __func__);
+		return -1;
+	}
+
+	PMD_DRV_LOG(INFO,
+		    "HW Sanity test has PASSED, expected constant"
+		    " 0x%x, read 0x%x (%s)\n",
+		    0xcafef00d, ark->sysctrl.t32[4], __func__);
+
+	/* We are a single function multi-port device. */
+	const unsigned int numa_node = rte_socket_id();
+	struct ether_addr adr;
+
+	ret = ark_config_device(dev);
 	dev->dev_ops = &ark_eth_dev_ops;
 
+	dev->data->mac_addrs = rte_zmalloc("ark", ETHER_ADDR_LEN, 0);
+	if (!dev->data->mac_addrs) {
+		PMD_DRV_LOG(ERR,
+			    "Failed to allocated memory for storing mac address"
+			    );
+	}
+	ether_addr_copy((struct ether_addr *)&adr, &dev->data->mac_addrs[0]);
+
+	if (ark->user_ext.dev_init) {
+		ark->user_data = ark->user_ext.dev_init(dev, ark->a_bar, 0);
+		if (!ark->user_data) {
+			PMD_DRV_LOG(INFO,
+				    "Failed to initialize PMD extension!"
+				    " continuing without it\n");
+			memset(&ark->user_ext, 0, sizeof(struct ark_user_ext));
+			dlclose(ark->d_handle);
+		}
+	}
+
 	/*  We process our args last as they require everything to be setup */
 	if (pci_dev->device.devargs)
 		eth_ark_check_args(ark, pci_dev->device.devargs->args);
 	else
 		PMD_DRV_LOG(INFO, "No Device args found\n");
 
+	/*
+	 * We will create additional devices based on the number of requested
+	 * ports
+	 */
+	int pc = 1;
+	int p;
+
+	if (ark->user_ext.dev_get_port_count) {
+		pc = ark->user_ext.dev_get_port_count(dev, ark->user_data);
+		ark->num_ports = pc;
+	} else {
+		ark->num_ports = 1;
+	}
+	for (p = 0; p < pc; p++) {
+		struct ark_port *port;
+
+		port = &ark->port[p];
+		struct rte_eth_dev_data *data = NULL;
+
+		port->id = p;
+
+		char name[RTE_ETH_NAME_MAX_LEN];
+
+		snprintf(name, sizeof(name), "arketh%d",
+			 dev->data->port_id + p);
+
+		if (p == 0) {
+			/* First port is already allocated by DPDK */
+			port->eth_dev = ark->eth_dev;
+			continue;
+		}
+
+		/* reserve an ethdev entry */
+		port->eth_dev = rte_eth_dev_allocate(name);
+		if (!port->eth_dev) {
+			PMD_DRV_LOG(ERR,
+				    "Could not allocate eth_dev for port %d\n",
+				    p);
+			goto error;
+		}
+
+		data = rte_zmalloc_socket(name, sizeof(*data), 0, numa_node);
+		if (!data) {
+			PMD_DRV_LOG(ERR,
+				    "Could not allocate eth_dev for port %d\n",
+				    p);
+			goto error;
+		}
+		data->port_id = ark->eth_dev->data->port_id + p;
+		port->eth_dev->data = data;
+		port->eth_dev->device = &pci_dev->device;
+		port->eth_dev->data->dev_private = ark;
+		port->eth_dev->driver = ark->eth_dev->driver;
+		port->eth_dev->dev_ops = ark->eth_dev->dev_ops;
+		port->eth_dev->tx_pkt_burst = ark->eth_dev->tx_pkt_burst;
+		port->eth_dev->rx_pkt_burst = ark->eth_dev->rx_pkt_burst;
+
+		rte_eth_copy_pci_info(port->eth_dev, pci_dev);
+
+		port->eth_dev->data->mac_addrs =
+			rte_zmalloc(name, ETHER_ADDR_LEN, 0);
+		if (!port->eth_dev->data->mac_addrs) {
+			PMD_DRV_LOG(ERR,
+				    "Memory allocation for MAC failed!"
+				    " Exiting.\n");
+			goto error;
+		}
+		ether_addr_copy(&adr,
+				&port->eth_dev->data->mac_addrs[0]);
+
+		if (ark->user_ext.dev_init)
+			ark->user_data =
+				ark->user_ext.dev_init(dev, ark->a_bar, p);
+	}
+
 	return ret;
+
+ error:
+	if (dev->data->mac_addrs)
+		rte_free(dev->data->mac_addrs);
+
+	for (p = 0; p < pc; p++) {
+		if (ark->port[p].eth_dev->data)
+			rte_free(ark->port[p].eth_dev->data);
+		if (ark->port[p].eth_dev->data->mac_addrs)
+			rte_free(ark->port[p].eth_dev->data->mac_addrs);
+	}
+
+	return -1;
+}
+
+/*
+ *Initial device configuration when device is opened
+ * setup the DDM, and UDM
+ * Called once per PCIE device
+ */
+static int
+ark_config_device(struct rte_eth_dev *dev)
+{
+	struct ark_adapter *ark =
+		(struct ark_adapter *)dev->data->dev_private;
+	uint16_t num_q, i;
+	struct ark_mpu_t *mpu;
+
+	/*
+	 * Make sure that the packet director, generator and checker are in a
+	 * known state
+	 */
+	ark->start_pg = 0;
+	ark->pg = ark_pktgen_init(ark->pktgen.v, 0, 1);
+	ark_pktgen_reset(ark->pg);
+	ark->pc = ark_pktchkr_init(ark->pktchkr.v, 0, 1);
+	ark_pktchkr_stop(ark->pc);
+	ark->pd = ark_pktdir_init(ark->pktdir.v);
+
+	/* Verify HW */
+	if (ark_udm_verify(ark->udm.v))
+		return -1;
+	if (ark_ddm_verify(ark->ddm.v))
+		return -1;
+
+	/* UDM */
+	if (ark_udm_reset(ark->udm.v)) {
+		PMD_DRV_LOG(ERR, "Unable to stop and reset UDM\n");
+		return -1;
+	}
+	/* Keep in reset until the MPU are cleared */
+
+	/* MPU reset */
+	mpu = ark->mpurx.v;
+	num_q = ark_api_num_queues(mpu);
+	ark->rx_queues = num_q;
+	for (i = 0; i < num_q; i++) {
+		ark_mpu_reset(mpu);
+		mpu = RTE_PTR_ADD(mpu, ARK_MPU_QOFFSET);
+	}
+
+	ark_udm_stop(ark->udm.v, 0);
+	ark_udm_configure(ark->udm.v,
+			  RTE_PKTMBUF_HEADROOM,
+			  RTE_MBUF_DEFAULT_DATAROOM,
+			  ARK_RX_WRITE_TIME_NS);
+	ark_udm_stats_reset(ark->udm.v);
+	ark_udm_stop(ark->udm.v, 0);
+
+	/* TX -- DDM */
+	if (ark_ddm_stop(ark->ddm.v, 1))
+		PMD_DRV_LOG(ERR, "Unable to stop DDM\n");
+
+	mpu = ark->mputx.v;
+	num_q = ark_api_num_queues(mpu);
+	ark->tx_queues = num_q;
+	for (i = 0; i < num_q; i++) {
+		ark_mpu_reset(mpu);
+		mpu = RTE_PTR_ADD(mpu, ARK_MPU_QOFFSET);
+	}
+
+	ark_ddm_reset(ark->ddm.v);
+	ark_ddm_stats_reset(ark->ddm.v);
+
+	ark_ddm_stop(ark->ddm.v, 0);
+	ark_rqp_stats_reset(ark->rqpacing);
+
+	return 0;
 }
 
 static int
 eth_ark_dev_uninit(struct rte_eth_dev *dev)
 {
+	struct ark_adapter *ark =
+		(struct ark_adapter *)dev->data->dev_private;
+
 	if (rte_eal_process_type() != RTE_PROC_PRIMARY)
 		return 0;
 
+	if (ark->user_ext.dev_uninit)
+		ark->user_ext.dev_uninit(dev, ark->user_data);
+
+	ark_pktgen_uninit(ark->pg);
+	ark_pktchkr_uninit(ark->pc);
+
 	dev->dev_ops = NULL;
 	dev->rx_pkt_burst = NULL;
 	dev->tx_pkt_burst = NULL;
+	if (dev->data->mac_addrs)
+		rte_free(dev->data->mac_addrs);
+	if (dev->data)
+		rte_free(dev->data);
+
 	return 0;
 }
 
 static int
-eth_ark_dev_configure(struct rte_eth_dev *dev __rte_unused)
+eth_ark_dev_configure(struct rte_eth_dev *dev)
 {
 	ARK_DEBUG_TRACE("ARKP: In %s\n", __func__);
+	struct ark_adapter *ark =
+		(struct ark_adapter *)dev->data->dev_private;
+
+	eth_ark_dev_set_link_up(dev);
+	if (ark->user_ext.dev_configure)
+		return ark->user_ext.dev_configure(dev, ark->user_data);
+	return 0;
+}
+
+static void *
+delay_pg_start(void *arg)
+{
+	struct ark_adapter *ark = (struct ark_adapter *)arg;
+
+	/* This function is used exclusively for regression testing, We
+	 * perform a blind sleep here to ensure that the external test
+	 * application has time to setup the test before we generate packets
+	 */
+	usleep(100000);
+	ark_pktgen_run(ark->pg);
+	return NULL;
+}
+
+static int
+eth_ark_dev_start(struct rte_eth_dev *dev)
+{
+	struct ark_adapter *ark =
+		(struct ark_adapter *)dev->data->dev_private;
+	int i;
+
+	ARK_DEBUG_TRACE("ARKP: In eth_ark_dev_start\n");
+
+	/* RX Side */
+	/* start UDM */
+	ark_udm_start(ark->udm.v);
+
+	for (i = 0; i < dev->data->nb_rx_queues; i++)
+		eth_ark_rx_start_queue(dev, i);
+
+	/* TX Side */
+	for (i = 0; i < dev->data->nb_tx_queues; i++)
+		eth_ark_tx_queue_start(dev, i);
+
+	/* start DDM */
+	ark_ddm_start(ark->ddm.v);
+
+	ark->started = 1;
+	/* set xmit and receive function */
+	dev->rx_pkt_burst = &eth_ark_recv_pkts;
+	dev->tx_pkt_burst = &eth_ark_xmit_pkts;
+
+	if (ark->start_pg)
+		ark_pktchkr_run(ark->pc);
+
+	if (ark->start_pg && (dev->data->port_id == 0)) {
+		pthread_t thread;
+
+		/* Delay packet generatpr start allow the hardware to be ready
+		 * This is only used for sanity checking with internal generator
+		 */
+		pthread_create(&thread, NULL, delay_pg_start, ark);
+	}
+
+	if (ark->user_ext.dev_start)
+		ark->user_ext.dev_start(dev, ark->user_data);
+
 	return 0;
 }
 
 static void
+eth_ark_dev_stop(struct rte_eth_dev *dev)
+{
+	uint16_t i;
+	int status;
+	struct ark_adapter *ark =
+		(struct ark_adapter *)dev->data->dev_private;
+	struct ark_mpu_t *mpu;
+
+	ARK_DEBUG_TRACE("ARKP: In eth_ark_dev_stop\n");
+
+	if (ark->started == 0)
+		return;
+	ark->started = 0;
+
+	/* Stop the extension first */
+	if (ark->user_ext.dev_stop)
+		ark->user_ext.dev_stop(dev, ark->user_data);
+
+	/* Stop the packet generator */
+	if (ark->start_pg)
+		ark_pktgen_pause(ark->pg);
+
+	dev->rx_pkt_burst = &eth_ark_recv_pkts_noop;
+	dev->tx_pkt_burst = &eth_ark_xmit_pkts_noop;
+
+	/* STOP TX Side */
+	for (i = 0; i < dev->data->nb_tx_queues; i++) {
+		status = eth_ark_tx_queue_stop(dev, i);
+		if (status != 0) {
+			uint8_t port = dev->data->port_id;
+			PMD_DRV_LOG(ERR,
+				    "ARKP tx_queue stop anomaly"
+				    " port %u, queue %u\n",
+				    port, i);
+		}
+	}
+
+	/* Stop DDM */
+	/* Wait up to 0.1 second.  each stop is upto 1000 * 10 useconds */
+	for (i = 0; i < 10; i++) {
+		status = ark_ddm_stop(ark->ddm.v, 1);
+		if (status == 0)
+			break;
+	}
+	if (status || i != 0) {
+		PMD_DRV_LOG(ERR, "DDM stop anomaly. status:"
+			    " %d iter: %u. (%s)\n",
+			    status,
+			    i,
+			    __func__);
+		ark_ddm_dump(ark->ddm.v, "Stop anomaly");
+
+		mpu = ark->mputx.v;
+		for (i = 0; i < ark->tx_queues; i++) {
+			ark_mpu_dump(mpu, "DDM failure dump", i);
+			mpu = RTE_PTR_ADD(mpu, ARK_MPU_QOFFSET);
+		}
+	}
+
+	/* STOP RX Side */
+	/* Stop UDM  multiple tries attempted */
+	for (i = 0; i < 10; i++) {
+		status = ark_udm_stop(ark->udm.v, 1);
+		if (status == 0)
+			break;
+	}
+	if (status || i != 0) {
+		PMD_DRV_LOG(ERR, "UDM stop anomaly. status %d iter: %u. (%s)\n",
+			    status, i, __func__);
+		ark_udm_dump(ark->udm.v, "Stop anomaly");
+
+		mpu = ark->mpurx.v;
+		for (i = 0; i < ark->rx_queues; i++) {
+			ark_mpu_dump(mpu, "UDM Stop anomaly", i);
+			mpu = RTE_PTR_ADD(mpu, ARK_MPU_QOFFSET);
+		}
+	}
+
+	ark_udm_dump_stats(ark->udm.v, "Post stop");
+	ark_udm_dump_perf(ark->udm.v, "Post stop");
+
+	for (i = 0; i < dev->data->nb_tx_queues; i++)
+		eth_ark_rx_dump_queue(dev, i, __func__);
+
+	/* Stop the packet checker if it is running */
+	if (ark->start_pg) {
+		ark_pktchkr_dump_stats(ark->pc);
+		ark_pktchkr_stop(ark->pc);
+	}
+}
+
+static void
+eth_ark_dev_close(struct rte_eth_dev *dev)
+{
+	struct ark_adapter *ark =
+		(struct ark_adapter *)dev->data->dev_private;
+	uint16_t i;
+
+	if (ark->user_ext.dev_close)
+		ark->user_ext.dev_close(dev, ark->user_data);
+
+	eth_ark_dev_stop(dev);
+	eth_ark_udm_force_close(dev);
+
+	/*
+	 * TODO This should only be called once for the device during shutdown
+	 */
+	ark_rqp_dump(ark->rqpacing);
+
+	for (i = 0; i < dev->data->nb_tx_queues; i++) {
+		eth_ark_tx_queue_release(dev->data->tx_queues[i]);
+		dev->data->tx_queues[i] = 0;
+	}
+
+	for (i = 0; i < dev->data->nb_rx_queues; i++) {
+		eth_ark_dev_rx_queue_release(dev->data->rx_queues[i]);
+		dev->data->rx_queues[i] = 0;
+	}
+}
+
+static void
 eth_ark_dev_info_get(struct rte_eth_dev *dev,
 		     struct rte_eth_dev_info *dev_info)
 {
+	struct ark_adapter *ark =
+		(struct ark_adapter *)dev->data->dev_private;
+	struct ark_mpu_t *tx_mpu = RTE_PTR_ADD(ark->bar0, ARK_MPU_TX_BASE);
+	struct ark_mpu_t *rx_mpu = RTE_PTR_ADD(ark->bar0, ARK_MPU_RX_BASE);
+	uint16_t ports = ark->num_ports;
+
 	dev_info->max_rx_pktlen = ARK_RX_MAX_PKT_LEN;
 	dev_info->min_rx_bufsize = ARK_RX_MIN_BUFSIZE;
 
+	dev_info->max_rx_queues = ark_api_num_queues_per_port(rx_mpu, ports);
+	dev_info->max_tx_queues = ark_api_num_queues_per_port(tx_mpu, ports);
+
 	dev_info->rx_desc_lim = (struct rte_eth_desc_lim) {
 		.nb_max = ARK_RX_MAX_QUEUE,
 		.nb_min = ARK_RX_MIN_QUEUE,
@@ -190,6 +753,121 @@  static void eth_ark_dev_info_get(struct rte_eth_dev *dev,
 	dev_info->pci_dev = ARK_DEV_TO_PCI(dev);
 }
 
+static int
+eth_ark_dev_link_update(struct rte_eth_dev *dev, int wait_to_complete)
+{
+	ARK_DEBUG_TRACE("ARKP: link status = %d\n",
+			dev->data->dev_link.link_status);
+	struct ark_adapter *ark =
+		(struct ark_adapter *)dev->data->dev_private;
+
+	if (ark->user_ext.link_update) {
+		return ark->user_ext.link_update
+			(dev, wait_to_complete,
+			 ark->user_data);
+	}
+	return 0;
+}
+
+static int
+eth_ark_dev_set_link_up(struct rte_eth_dev *dev)
+{
+	dev->data->dev_link.link_status = 1;
+	struct ark_adapter *ark =
+		(struct ark_adapter *)dev->data->dev_private;
+
+	if (ark->user_ext.dev_set_link_up)
+		return ark->user_ext.dev_set_link_up(dev, ark->user_data);
+	return 0;
+}
+
+static int
+eth_ark_dev_set_link_down(struct rte_eth_dev *dev)
+{
+	dev->data->dev_link.link_status = 0;
+	struct ark_adapter *ark =
+		(struct ark_adapter *)dev->data->dev_private;
+
+	if (ark->user_ext.dev_set_link_down)
+		return ark->user_ext.dev_set_link_down(dev, ark->user_data);
+	return 0;
+}
+
+static void
+eth_ark_dev_stats_get(struct rte_eth_dev *dev, struct rte_eth_stats *stats)
+{
+	uint16_t i;
+	struct ark_adapter *ark =
+		(struct ark_adapter *)dev->data->dev_private;
+
+	stats->ipackets = 0;
+	stats->ibytes = 0;
+	stats->opackets = 0;
+	stats->obytes = 0;
+	stats->imissed = 0;
+	stats->oerrors = 0;
+
+	for (i = 0; i < dev->data->nb_tx_queues; i++)
+		eth_tx_queue_stats_get(dev->data->tx_queues[i], stats);
+	for (i = 0; i < dev->data->nb_rx_queues; i++)
+		eth_rx_queue_stats_get(dev->data->rx_queues[i], stats);
+	if (ark->user_ext.stats_get)
+		ark->user_ext.stats_get(dev, stats, ark->user_data);
+}
+
+static void
+eth_ark_dev_stats_reset(struct rte_eth_dev *dev)
+{
+	uint16_t i;
+	struct ark_adapter *ark =
+		(struct ark_adapter *)dev->data->dev_private;
+
+	for (i = 0; i < dev->data->nb_tx_queues; i++)
+		eth_tx_queue_stats_reset(dev->data->rx_queues[i]);
+	for (i = 0; i < dev->data->nb_rx_queues; i++)
+		eth_rx_queue_stats_reset(dev->data->rx_queues[i]);
+	if (ark->user_ext.stats_reset)
+		ark->user_ext.stats_reset(dev, ark->user_data);
+}
+
+static void
+eth_ark_macaddr_add(struct rte_eth_dev *dev,
+		    struct ether_addr *mac_addr,
+		    uint32_t index,
+		    uint32_t pool)
+{
+	struct ark_adapter *ark =
+		(struct ark_adapter *)dev->data->dev_private;
+
+	if (ark->user_ext.mac_addr_add)
+		ark->user_ext.mac_addr_add(dev,
+					   mac_addr,
+					   index,
+					   pool,
+					   ark->user_data);
+}
+
+static void
+eth_ark_macaddr_remove(struct rte_eth_dev *dev, uint32_t index)
+{
+	struct ark_adapter *ark =
+		(struct ark_adapter *)dev->data->dev_private;
+
+	if (ark->user_ext.mac_addr_remove)
+		ark->user_ext.mac_addr_remove(dev, index, ark->user_data);
+}
+
+static void
+eth_ark_set_default_mac_addr(struct rte_eth_dev *dev,
+			     struct ether_addr *mac_addr)
+{
+	struct ark_adapter *ark =
+		(struct ark_adapter *)dev->data->dev_private;
+
+	if (ark->user_ext.mac_addr_set)
+		ark->user_ext.mac_addr_set(dev, mac_addr, ark->user_data);
+}
+
 static inline int
 process_pktdir_arg(const char *key, const char *value,
 		   void *extra_args)
@@ -278,8 +956,25 @@  static void eth_ark_dev_info_get(struct rte_eth_dev *dev,
 		PMD_DRV_LOG(ERR, "Unable to parse arg %s\n", ARK_PKTCHKR_ARG);
 	}
 
+	/* Setup the packet director */
+	ark_pktdir_setup(ark->pd, ark->pkt_dir_v);
 	ARK_DEBUG_TRACE("INFO: packet director set to 0x%x\n", ark->pkt_dir_v);
 
+	/* Setup the packet generator */
+	if (ark->pkt_gen_args) {
+		PMD_DRV_LOG(INFO, "Setting up the packet generator\n");
+		ark_pktgen_parse(ark->pkt_gen_args);
+		ark_pktgen_reset(ark->pg);
+		ark_pktgen_setup(ark->pg);
+		ark->start_pg = 1;
+	}
+
+	/* Setup the packet checker */
+	if (ark->pkt_chkr_args) {
+		ark_pktchkr_parse(ark->pkt_chkr_args);
+		ark_pktchkr_setup(ark->pc);
+	}
+
 	return 1;
 }
 
diff --git a/drivers/net/ark/ark_ext.h b/drivers/net/ark/ark_ext.h
new file mode 100644
index 0000000..0786d1f
--- /dev/null
+++ b/drivers/net/ark/ark_ext.h
@@ -0,0 +1,79 @@ 
+/*-
+ * BSD LICENSE
+ *
+ * Copyright (c) 2015-2017 Atomic Rules LLC
+ * 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 copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of copyright holder 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 _ARK_EXT_H_
+#define _ARK_EXT_H_
+
+/*
+ * Called post PMD init.
+ * The implementation returns its private data that gets passed into
+ * all other functions as user_data
+ * The ARK extension implementation MUST implement this function
+ */
+void *dev_init(struct rte_eth_dev *dev, void *a_bar, int port_id);
+
+/* Called during device shutdown */
+void dev_uninit(struct rte_eth_dev *dev, void *user_data);
+
+/* This call is optional and allows the
+ * extension to specify the number of supported ports.
+ */
+uint8_t dev_get_port_count(struct rte_eth_dev *dev, void *user_data);
+
+/*
+ * The following functions are optional and are directly mapped
+ * from the DPDK PMD ops structure.
+ * Each function if implemented is called after the ARK PMD
+ * implementation executes.
+ */
+int dev_configure(struct rte_eth_dev *dev, void *user_data);
+int dev_start(struct rte_eth_dev *dev, void *user_data);
+void dev_stop(struct rte_eth_dev *dev, void *user_data);
+void dev_close(struct rte_eth_dev *dev, void *user_data);
+int link_update(struct rte_eth_dev *dev, int wait_to_complete,
+	void *user_data);
+int dev_set_link_up(struct rte_eth_dev *dev, void *user_data);
+int dev_set_link_down(struct rte_eth_dev *dev, void *user_data);
+void stats_get(struct rte_eth_dev *dev, struct rte_eth_stats *stats,
+	void *user_data);
+void stats_reset(struct rte_eth_dev *dev, void *user_data);
+void mac_addr_add(struct rte_eth_dev *dev,
+	struct ether_addr *macadr,
+				  uint32_t index,
+				  uint32_t pool,
+				  void *user_data);
+void mac_addr_remove(struct rte_eth_dev *dev, uint32_t index, void *user_data);
+void mac_addr_set(struct rte_eth_dev *dev, struct ether_addr *mac_addr,
+	void *user_data);
+
+#endif
diff --git a/drivers/net/ark/ark_global.h b/drivers/net/ark/ark_global.h
index 86039c6..33652ba 100644
--- a/drivers/net/ark/ark_global.h
+++ b/drivers/net/ark/ark_global.h
@@ -91,6 +91,28 @@  struct ark_port {
 	int id;
 };
 
+struct ark_user_ext {
+	void *(*dev_init)(struct rte_eth_dev *, void *abar, int port_id);
+	void (*dev_uninit)(struct rte_eth_dev *, void *);
+	int (*dev_get_port_count)(struct rte_eth_dev *, void *);
+	int (*dev_configure)(struct rte_eth_dev *, void *);
+	int (*dev_start)(struct rte_eth_dev *, void *);
+	void (*dev_stop)(struct rte_eth_dev *, void *);
+	void (*dev_close)(struct rte_eth_dev *, void *);
+	int (*link_update)(struct rte_eth_dev *, int wait_to_complete, void *);
+	int (*dev_set_link_up)(struct rte_eth_dev *, void *);
+	int (*dev_set_link_down)(struct rte_eth_dev *, void *);
+	void (*stats_get)(struct rte_eth_dev *, struct rte_eth_stats *, void *);
+	void (*stats_reset)(struct rte_eth_dev *, void *);
+	void (*mac_addr_add)(struct rte_eth_dev *,
+						  struct ether_addr *,
+						 uint32_t,
+						 uint32_t,
+						 void *);
+	void (*mac_addr_remove)(struct rte_eth_dev *, uint32_t, void *);
+	void (*mac_addr_set)(struct rte_eth_dev *, struct ether_addr *, void *);
+};
+
 struct ark_adapter {
 	/* User extension private data */
 	void *user_data;
@@ -113,6 +135,7 @@  struct ark_adapter {
 	struct rte_eth_dev *eth_dev;
 
 	void *d_handle;
+	struct ark_user_ext user_ext;
 
 	/* Our Bar 0 */
 	uint8_t *bar0;