diff --git a/drivers/net/ethernet/pensando/pds_core/Makefile b/drivers/net/ethernet/pensando/pds_core/Makefile
index 06bd3da8c38b..ee794cc08fda 100644
--- a/drivers/net/ethernet/pensando/pds_core/Makefile
+++ b/drivers/net/ethernet/pensando/pds_core/Makefile
@@ -8,6 +8,7 @@ pds_core-y := main.o \
 	      dev.o \
 	      adminq.o \
 	      core.o \
+	      netdev.o \
 	      fw.o
 
 pds_core-$(CONFIG_DEBUG_FS) += debugfs.o
diff --git a/drivers/net/ethernet/pensando/pds_core/core.c b/drivers/net/ethernet/pensando/pds_core/core.c
index 203a27a0fc5c..202f1a6b4605 100644
--- a/drivers/net/ethernet/pensando/pds_core/core.c
+++ b/drivers/net/ethernet/pensando/pds_core/core.c
@@ -449,6 +449,12 @@ int pdsc_setup(struct pdsc *pdsc, bool init)
 	if (init)
 		pdsc_debugfs_add_viftype(pdsc);
 
+	if (init) {
+		err = pdsc_init_netdev(pdsc);
+		if (err)
+			goto err_out_teardown;
+	}
+
 	clear_bit(PDSC_S_FW_DEAD, &pdsc->state);
 	return 0;
 
@@ -461,6 +467,12 @@ void pdsc_teardown(struct pdsc *pdsc, bool removing)
 {
 	int i;
 
+	if (removing && pdsc->netdev) {
+		unregister_netdev(pdsc->netdev);
+		free_netdev(pdsc->netdev);
+		pdsc->netdev = NULL;
+	}
+
 	pdsc_devcmd_reset(pdsc);
 	pdsc_qcq_free(pdsc, &pdsc->notifyqcq);
 	pdsc_qcq_free(pdsc, &pdsc->adminqcq);
@@ -528,6 +540,7 @@ static void pdsc_fw_down(struct pdsc *pdsc)
 		return;
 	}
 
+	netif_device_detach(pdsc->netdev);
 	pdsc_mask_interrupts(pdsc);
 	pdsc_teardown(pdsc, PDSC_TEARDOWN_RECOVERY);
 
@@ -554,8 +567,12 @@ static void pdsc_fw_up(struct pdsc *pdsc)
 	if (err)
 		goto err_out;
 
+	netif_device_attach(pdsc->netdev);
+
 	mutex_unlock(&pdsc->config_lock);
 
+	pdsc_vf_attr_replay(pdsc);
+
 	return;
 
 err_out:
diff --git a/drivers/net/ethernet/pensando/pds_core/core.h b/drivers/net/ethernet/pensando/pds_core/core.h
index 46d10afb0bde..07499a8aae21 100644
--- a/drivers/net/ethernet/pensando/pds_core/core.h
+++ b/drivers/net/ethernet/pensando/pds_core/core.h
@@ -30,6 +30,22 @@ struct pdsc_dev_bar {
 	int res_index;
 };
 
+struct pdsc_vf {
+	u16     index;
+	u8      macaddr[6];
+	__le32  maxrate;
+	__le16  vlanid;
+	u8      spoofchk;
+	u8      trusted;
+	u8      linkstate;
+	__le16  vif_types[PDS_DEV_TYPE_MAX];
+
+	struct pds_core_vf_stats stats;
+	dma_addr_t               stats_pa;
+
+	struct pds_auxiliary_dev *padev;
+};
+
 struct pdsc_devinfo {
 	u8 asic_type;
 	u8 asic_rev;
@@ -153,6 +169,7 @@ struct pdsc {
 	struct dentry *dentry;
 	struct device *dev;
 	struct pdsc_dev_bar bars[PDS_CORE_BARS_MAX];
+	struct pdsc_vf *vfs;
 	int num_vfs;
 	int hw_index;
 	int id;
@@ -172,6 +189,8 @@ struct pdsc {
 	struct pdsc_intr_info *intr_info;	/* array of nintrs elements */
 
 	struct workqueue_struct *wq;
+	struct net_device *netdev;
+	struct rw_semaphore vf_op_lock;	/* lock for VF operations */
 
 	unsigned int devcmd_timeout;
 	struct mutex devcmd_lock;	/* lock for dev_cmd operations */
@@ -309,4 +328,9 @@ irqreturn_t pdsc_adminq_isr(int irq, void *data);
 int pdsc_firmware_update(struct pdsc *pdsc, const struct firmware *fw,
 			 struct netlink_ext_ack *extack);
 
+int pdsc_init_netdev(struct pdsc *pdsc);
+int pdsc_set_vf_config(struct pdsc *pdsc, int vf,
+		       struct pds_core_vf_setattr_cmd *vfc);
+void pdsc_vf_attr_replay(struct pdsc *pdsc);
+
 #endif /* _PDSC_H_ */
diff --git a/drivers/net/ethernet/pensando/pds_core/main.c b/drivers/net/ethernet/pensando/pds_core/main.c
index 856704f8827a..36e330c49360 100644
--- a/drivers/net/ethernet/pensando/pds_core/main.c
+++ b/drivers/net/ethernet/pensando/pds_core/main.c
@@ -165,6 +165,67 @@ void __iomem *pdsc_map_dbpage(struct pdsc *pdsc, int page_num)
 			       (u64)page_num << PAGE_SHIFT, PAGE_SIZE);
 }
 
+static int pdsc_sriov_configure(struct pci_dev *pdev, int num_vfs)
+{
+	struct pds_core_vf_setattr_cmd vfc = { .attr = PDS_CORE_VF_ATTR_STATSADDR };
+	struct pdsc *pdsc = pci_get_drvdata(pdev);
+	struct device *dev = pdsc->dev;
+	struct pdsc_vf *v;
+	int ret = 0;
+	int i;
+
+	if (num_vfs > 0) {
+		pdsc->vfs = kcalloc(num_vfs, sizeof(struct pdsc_vf), GFP_KERNEL);
+		if (!pdsc->vfs)
+			return -ENOMEM;
+		pdsc->num_vfs = num_vfs;
+
+		for (i = 0; i < num_vfs; i++) {
+			v = &pdsc->vfs[i];
+			v->stats_pa = dma_map_single(pdsc->dev, &v->stats,
+						     sizeof(v->stats), DMA_FROM_DEVICE);
+			if (dma_mapping_error(pdsc->dev, v->stats_pa)) {
+				dev_err(pdsc->dev, "DMA mapping failed for vf[%d] stats\n", i);
+				v->stats_pa = 0;
+			} else {
+				vfc.stats.len = cpu_to_le32(sizeof(v->stats));
+				vfc.stats.pa = cpu_to_le64(v->stats_pa);
+				pdsc_set_vf_config(pdsc, i, &vfc);
+			}
+		}
+
+		ret = pci_enable_sriov(pdev, num_vfs);
+		if (ret) {
+			dev_err(dev, "Cannot enable SRIOV: %pe\n", ERR_PTR(ret));
+			goto no_vfs;
+		}
+
+		return num_vfs;
+	}
+
+no_vfs:
+	pci_disable_sriov(pdev);
+
+	for (i = pdsc->num_vfs - 1; i >= 0; i--) {
+		v = &pdsc->vfs[i];
+
+		if (v->stats_pa) {
+			vfc.stats.len = 0;
+			vfc.stats.pa = 0;
+			pdsc_set_vf_config(pdsc, i, &vfc);
+			dma_unmap_single(pdsc->dev, v->stats_pa,
+					 sizeof(v->stats), DMA_FROM_DEVICE);
+			v->stats_pa = 0;
+		}
+	}
+
+	kfree(pdsc->vfs);
+	pdsc->vfs = NULL;
+	pdsc->num_vfs = 0;
+
+	return ret;
+}
+
 static DEFINE_IDA(pdsc_pf_ida);
 
 #define PDSC_WQ_NAME_LEN 24
@@ -237,6 +298,7 @@ static int pdsc_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
 	spin_lock_init(&pdsc->adminq_lock);
 
 	mutex_lock(&pdsc->config_lock);
+	init_rwsem(&pdsc->vf_op_lock);
 	err = pdsc_setup(pdsc, PDSC_SETUP_INIT);
 	if (err)
 		goto err_out_unmap_bars;
@@ -300,6 +362,8 @@ static void pdsc_remove(struct pci_dev *pdev)
 	 */
 	pdsc_dl_unregister(pdsc);
 
+	pdsc_sriov_configure(pdev, 0);
+
 	/* Now we can lock it up and tear it down */
 	mutex_lock(&pdsc->config_lock);
 	set_bit(PDSC_S_STOPPING_DRIVER, &pdsc->state);
@@ -337,6 +401,7 @@ static struct pci_driver pdsc_driver = {
 	.id_table = pdsc_id_table,
 	.probe = pdsc_probe,
 	.remove = pdsc_remove,
+	.sriov_configure = pdsc_sriov_configure,
 };
 
 static int __init pdsc_init_module(void)
diff --git a/drivers/net/ethernet/pensando/pds_core/netdev.c b/drivers/net/ethernet/pensando/pds_core/netdev.c
new file mode 100644
index 000000000000..0d7f9c2c7df8
--- /dev/null
+++ b/drivers/net/ethernet/pensando/pds_core/netdev.c
@@ -0,0 +1,504 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright(c) 2022 Pensando Systems, Inc */
+
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/pci.h>
+#include <linux/delay.h>
+#include <net/devlink.h>
+#include <linux/etherdevice.h>
+#include <linux/ethtool.h>
+
+#include "core.h"
+
+static const char *pdsc_vf_attr_to_str(enum pds_core_vf_attr attr)
+{
+	switch (attr) {
+	case PDS_CORE_VF_ATTR_SPOOFCHK:
+		return "PDS_CORE_VF_ATTR_SPOOFCHK";
+	case PDS_CORE_VF_ATTR_TRUST:
+		return "PDS_CORE_VF_ATTR_TRUST";
+	case PDS_CORE_VF_ATTR_LINKSTATE:
+		return "PDS_CORE_VF_ATTR_LINKSTATE";
+	case PDS_CORE_VF_ATTR_MAC:
+		return "PDS_CORE_VF_ATTR_MAC";
+	case PDS_CORE_VF_ATTR_VLAN:
+		return "PDS_CORE_VF_ATTR_VLAN";
+	case PDS_CORE_VF_ATTR_RATE:
+		return "PDS_CORE_VF_ATTR_RATE";
+	case PDS_CORE_VF_ATTR_STATSADDR:
+		return "PDS_CORE_VF_ATTR_STATSADDR";
+	default:
+		return "PDS_CORE_VF_ATTR_UNKNOWN";
+	}
+}
+
+static int pdsc_get_vf_stats(struct net_device *netdev, int vf,
+			     struct ifla_vf_stats *vf_stats)
+{
+	struct pdsc *pdsc = *(struct pdsc **)netdev_priv(netdev);
+	struct pds_core_vf_stats *vs;
+	int err = 0;
+
+	if (!netif_device_present(netdev))
+		return -EBUSY;
+
+	down_read(&pdsc->vf_op_lock);
+
+	if (vf >= pci_num_vf(pdsc->pdev) || !pdsc->vfs) {
+		err = -EINVAL;
+	} else {
+		memset(vf_stats, 0, sizeof(*vf_stats));
+		vs = &pdsc->vfs[vf].stats;
+
+		vf_stats->rx_packets = le64_to_cpu(vs->rx_ucast_packets);
+		vf_stats->tx_packets = le64_to_cpu(vs->tx_ucast_packets);
+		vf_stats->rx_bytes   = le64_to_cpu(vs->rx_ucast_bytes);
+		vf_stats->tx_bytes   = le64_to_cpu(vs->tx_ucast_bytes);
+		vf_stats->broadcast  = le64_to_cpu(vs->rx_bcast_packets);
+		vf_stats->multicast  = le64_to_cpu(vs->rx_mcast_packets);
+		vf_stats->rx_dropped = le64_to_cpu(vs->rx_ucast_drop_packets) +
+				       le64_to_cpu(vs->rx_mcast_drop_packets) +
+				       le64_to_cpu(vs->rx_bcast_drop_packets);
+		vf_stats->tx_dropped = le64_to_cpu(vs->tx_ucast_drop_packets) +
+				       le64_to_cpu(vs->tx_mcast_drop_packets) +
+				       le64_to_cpu(vs->tx_bcast_drop_packets);
+	}
+
+	up_read(&pdsc->vf_op_lock);
+	return err;
+}
+
+static int pdsc_get_fw_vf_config(struct pdsc *pdsc, int vf, struct pdsc_vf *vfdata)
+{
+	struct pds_core_vf_getattr_comp comp = { 0 };
+	int err;
+	u8 attr;
+
+	attr = PDS_CORE_VF_ATTR_VLAN;
+	err = pdsc_dev_cmd_vf_getattr(pdsc, vf, attr, &comp);
+	if (err && comp.status != PDS_RC_ENOSUPP)
+		goto err_out;
+	if (!err)
+		vfdata->vlanid = comp.vlanid;
+
+	attr = PDS_CORE_VF_ATTR_SPOOFCHK;
+	err = pdsc_dev_cmd_vf_getattr(pdsc, vf, attr, &comp);
+	if (err && comp.status != PDS_RC_ENOSUPP)
+		goto err_out;
+	if (!err)
+		vfdata->spoofchk = comp.spoofchk;
+
+	attr = PDS_CORE_VF_ATTR_LINKSTATE;
+	err = pdsc_dev_cmd_vf_getattr(pdsc, vf, attr, &comp);
+	if (err && comp.status != PDS_RC_ENOSUPP)
+		goto err_out;
+	if (!err) {
+		switch (comp.linkstate) {
+		case PDS_CORE_VF_LINK_STATUS_UP:
+			vfdata->linkstate = IFLA_VF_LINK_STATE_ENABLE;
+			break;
+		case PDS_CORE_VF_LINK_STATUS_DOWN:
+			vfdata->linkstate = IFLA_VF_LINK_STATE_DISABLE;
+			break;
+		case PDS_CORE_VF_LINK_STATUS_AUTO:
+			vfdata->linkstate = IFLA_VF_LINK_STATE_AUTO;
+			break;
+		default:
+			dev_warn(pdsc->dev, "Unexpected link state %u\n", comp.linkstate);
+			break;
+		}
+	}
+
+	attr = PDS_CORE_VF_ATTR_RATE;
+	err = pdsc_dev_cmd_vf_getattr(pdsc, vf, attr, &comp);
+	if (err && comp.status != PDS_RC_ENOSUPP)
+		goto err_out;
+	if (!err)
+		vfdata->maxrate = comp.maxrate;
+
+	attr = PDS_CORE_VF_ATTR_TRUST;
+	err = pdsc_dev_cmd_vf_getattr(pdsc, vf, attr, &comp);
+	if (err && comp.status != PDS_RC_ENOSUPP)
+		goto err_out;
+	if (!err)
+		vfdata->trusted = comp.trust;
+
+	attr = PDS_CORE_VF_ATTR_MAC;
+	err = pdsc_dev_cmd_vf_getattr(pdsc, vf, attr, &comp);
+	if (err && comp.status != PDS_RC_ENOSUPP)
+		goto err_out;
+	if (!err)
+		ether_addr_copy(vfdata->macaddr, comp.macaddr);
+
+err_out:
+	if (err)
+		dev_err(pdsc->dev, "Failed to get %s for VF %d\n",
+			pdsc_vf_attr_to_str(attr), vf);
+
+	return err;
+}
+
+static int pdsc_get_vf_config(struct net_device *netdev,
+			      int vf, struct ifla_vf_info *ivf)
+{
+	struct pdsc *pdsc = *(struct pdsc **)netdev_priv(netdev);
+	struct pdsc_vf vfdata = { 0 };
+	int err = 0;
+
+	if (!netif_device_present(netdev))
+		return -EBUSY;
+
+	down_read(&pdsc->vf_op_lock);
+
+	if (vf >= pci_num_vf(pdsc->pdev) || !pdsc->vfs) {
+		err = -EINVAL;
+	} else {
+		ivf->vf = vf;
+		ivf->qos = 0;
+
+		err = pdsc_get_fw_vf_config(pdsc, vf, &vfdata);
+		if (!err) {
+			ivf->vlan         = le16_to_cpu(vfdata.vlanid);
+			ivf->spoofchk     = vfdata.spoofchk;
+			ivf->linkstate    = vfdata.linkstate;
+			ivf->max_tx_rate  = le32_to_cpu(vfdata.maxrate);
+			ivf->trusted      = vfdata.trusted;
+			ether_addr_copy(ivf->mac, vfdata.macaddr);
+		}
+	}
+
+	up_read(&pdsc->vf_op_lock);
+	return err;
+}
+
+int pdsc_set_vf_config(struct pdsc *pdsc, int vf,
+		       struct pds_core_vf_setattr_cmd *vfc)
+{
+	union pds_core_dev_comp comp = { 0 };
+	union pds_core_dev_cmd cmd = {
+		.vf_setattr.opcode = PDS_CORE_CMD_VF_SETATTR,
+		.vf_setattr.attr = vfc->attr,
+		.vf_setattr.vf_index = cpu_to_le16(vf),
+	};
+	int err;
+
+	if (vf >= pdsc->num_vfs)
+		return -EINVAL;
+
+	memcpy(cmd.vf_setattr.pad, vfc->pad, sizeof(vfc->pad));
+
+	err = pdsc_devcmd(pdsc, &cmd, &comp, pdsc->devcmd_timeout);
+
+	return err;
+}
+
+static int pdsc_set_vf_mac(struct net_device *netdev, int vf, u8 *mac)
+{
+	struct pds_core_vf_setattr_cmd vfc = { .attr = PDS_CORE_VF_ATTR_MAC };
+	struct pdsc *pdsc = *(struct pdsc **)netdev_priv(netdev);
+	int err;
+
+	if (!(is_zero_ether_addr(mac) || is_valid_ether_addr(mac)))
+		return -EINVAL;
+
+	if (!netif_device_present(netdev))
+		return -EBUSY;
+
+	down_write(&pdsc->vf_op_lock);
+
+	if (vf >= pci_num_vf(pdsc->pdev) || !pdsc->vfs) {
+		err = -EINVAL;
+	} else {
+		ether_addr_copy(vfc.macaddr, mac);
+		dev_dbg(pdsc->dev, "%s: vf %d macaddr %pM\n",
+			__func__, vf, vfc.macaddr);
+
+		err = pdsc_set_vf_config(pdsc, vf, &vfc);
+		if (!err)
+			ether_addr_copy(pdsc->vfs[vf].macaddr, mac);
+	}
+
+	up_write(&pdsc->vf_op_lock);
+	return err;
+}
+
+static int pdsc_set_vf_vlan(struct net_device *netdev, int vf, u16 vlan,
+			    u8 qos, __be16 proto)
+{
+	struct pds_core_vf_setattr_cmd vfc = { .attr = PDS_CORE_VF_ATTR_VLAN };
+	struct pdsc *pdsc = *(struct pdsc **)netdev_priv(netdev);
+	int err;
+
+	/* until someday when we support qos */
+	if (qos)
+		return -EINVAL;
+
+	if (vlan > 4095)
+		return -EINVAL;
+
+	if (!netif_device_present(netdev))
+		return -EBUSY;
+
+	down_write(&pdsc->vf_op_lock);
+
+	if (vf >= pci_num_vf(pdsc->pdev) || !pdsc->vfs) {
+		err = -EINVAL;
+	} else {
+		vfc.vlanid = cpu_to_le16(vlan);
+		dev_dbg(pdsc->dev, "%s: vf %d vlan %d\n",
+			__func__, vf, le16_to_cpu(vfc.vlanid));
+
+		err = pdsc_set_vf_config(pdsc, vf, &vfc);
+		if (!err)
+			pdsc->vfs[vf].vlanid = cpu_to_le16(vlan);
+	}
+
+	up_write(&pdsc->vf_op_lock);
+	return err;
+}
+
+static int pdsc_set_vf_trust(struct net_device *netdev, int vf, bool set)
+{
+	struct pds_core_vf_setattr_cmd vfc = { .attr = PDS_CORE_VF_ATTR_TRUST };
+	struct pdsc *pdsc = *(struct pdsc **)netdev_priv(netdev);
+	u8 data = set;  /* convert to u8 for config */
+	int err;
+
+	if (!netif_device_present(netdev))
+		return -EBUSY;
+
+	down_write(&pdsc->vf_op_lock);
+
+	if (vf >= pci_num_vf(pdsc->pdev) || !pdsc->vfs) {
+		err = -EINVAL;
+	} else {
+		vfc.trust = set;
+		dev_dbg(pdsc->dev, "%s: vf %d trust %d\n",
+			__func__, vf, vfc.trust);
+
+		err = pdsc_set_vf_config(pdsc, vf, &vfc);
+		if (!err)
+			pdsc->vfs[vf].trusted = data;
+	}
+
+	up_write(&pdsc->vf_op_lock);
+	return err;
+}
+
+static int pdsc_set_vf_rate(struct net_device *netdev, int vf,
+			    int tx_min, int tx_max)
+{
+	struct pds_core_vf_setattr_cmd vfc = { .attr = PDS_CORE_VF_ATTR_RATE };
+	struct pdsc *pdsc = *(struct pdsc **)netdev_priv(netdev);
+	int err;
+
+	/* setting the min just seems silly */
+	if (tx_min)
+		return -EINVAL;
+
+	if (!netif_device_present(netdev))
+		return -EBUSY;
+
+	down_write(&pdsc->vf_op_lock);
+
+	if (vf >= pci_num_vf(pdsc->pdev) || !pdsc->vfs) {
+		err = -EINVAL;
+	} else {
+		vfc.maxrate = cpu_to_le32(tx_max);
+		dev_dbg(pdsc->dev, "%s: vf %d maxrate %d\n",
+			__func__, vf, le32_to_cpu(vfc.maxrate));
+
+		err = pdsc_set_vf_config(pdsc, vf, &vfc);
+		if (!err)
+			pdsc->vfs[vf].maxrate = cpu_to_le32(tx_max);
+	}
+
+	up_write(&pdsc->vf_op_lock);
+	return err;
+}
+
+static int pdsc_set_vf_spoofchk(struct net_device *netdev, int vf, bool set)
+{
+	struct pds_core_vf_setattr_cmd vfc = { .attr = PDS_CORE_VF_ATTR_SPOOFCHK };
+	struct pdsc *pdsc = *(struct pdsc **)netdev_priv(netdev);
+	u8 data = set;  /* convert to u8 for config */
+	int err;
+
+	if (!netif_device_present(netdev))
+		return -EBUSY;
+
+	down_write(&pdsc->vf_op_lock);
+
+	if (vf >= pci_num_vf(pdsc->pdev) || !pdsc->vfs) {
+		err = -EINVAL;
+	} else {
+		vfc.spoofchk = set;
+		dev_dbg(pdsc->dev, "%s: vf %d spoof %d\n",
+			__func__, vf, vfc.spoofchk);
+
+		err = pdsc_set_vf_config(pdsc, vf, &vfc);
+		if (!err)
+			pdsc->vfs[vf].spoofchk = data;
+	}
+
+	up_write(&pdsc->vf_op_lock);
+	return err;
+}
+
+static int pdsc_set_vf_link_state(struct net_device *netdev, int vf, int set)
+{
+	struct pds_core_vf_setattr_cmd vfc = { .attr = PDS_CORE_VF_ATTR_LINKSTATE };
+	struct pdsc *pdsc = *(struct pdsc **)netdev_priv(netdev);
+	u8 data;
+	int err;
+
+	switch (set) {
+	case IFLA_VF_LINK_STATE_ENABLE:
+		data = PDS_CORE_VF_LINK_STATUS_UP;
+		break;
+	case IFLA_VF_LINK_STATE_DISABLE:
+		data = PDS_CORE_VF_LINK_STATUS_DOWN;
+		break;
+	case IFLA_VF_LINK_STATE_AUTO:
+		data = PDS_CORE_VF_LINK_STATUS_AUTO;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	if (!netif_device_present(netdev))
+		return -EBUSY;
+
+	down_write(&pdsc->vf_op_lock);
+
+	if (vf >= pci_num_vf(pdsc->pdev) || !pdsc->vfs) {
+		err = -EINVAL;
+	} else {
+		vfc.linkstate = data;
+		dev_dbg(pdsc->dev, "%s: vf %d linkstate %d\n",
+			__func__, vf, vfc.linkstate);
+
+		err = pdsc_set_vf_config(pdsc, vf, &vfc);
+		if (!err)
+			pdsc->vfs[vf].linkstate = set;
+	}
+
+	up_write(&pdsc->vf_op_lock);
+	return err;
+}
+
+static const struct net_device_ops pdsc_netdev_ops = {
+	.ndo_set_vf_vlan	= pdsc_set_vf_vlan,
+	.ndo_set_vf_mac		= pdsc_set_vf_mac,
+	.ndo_set_vf_trust	= pdsc_set_vf_trust,
+	.ndo_set_vf_rate	= pdsc_set_vf_rate,
+	.ndo_set_vf_spoofchk	= pdsc_set_vf_spoofchk,
+	.ndo_set_vf_link_state	= pdsc_set_vf_link_state,
+	.ndo_get_vf_config	= pdsc_get_vf_config,
+	.ndo_get_vf_stats       = pdsc_get_vf_stats,
+};
+
+static void pdsc_get_drvinfo(struct net_device *netdev,
+			     struct ethtool_drvinfo *drvinfo)
+{
+	struct pdsc *pdsc = *(struct pdsc **)netdev_priv(netdev);
+
+	strscpy(drvinfo->driver, PDS_CORE_DRV_NAME, sizeof(drvinfo->driver));
+	strscpy(drvinfo->fw_version, pdsc->dev_info.fw_version, sizeof(drvinfo->fw_version));
+	strscpy(drvinfo->bus_info, pci_name(pdsc->pdev), sizeof(drvinfo->bus_info));
+}
+
+static const struct ethtool_ops pdsc_ethtool_ops = {
+	.get_drvinfo = pdsc_get_drvinfo,
+};
+
+int pdsc_init_netdev(struct pdsc *pdsc)
+{
+	struct pdsc **p;
+
+	pdsc->netdev = alloc_netdev(sizeof(struct pdsc *), "pdsc%d",
+				    NET_NAME_UNKNOWN, ether_setup);
+	SET_NETDEV_DEV(pdsc->netdev, pdsc->dev);
+	pdsc->netdev->netdev_ops = &pdsc_netdev_ops;
+	pdsc->netdev->ethtool_ops = &pdsc_ethtool_ops;
+
+	p = netdev_priv(pdsc->netdev);
+	*p = pdsc;
+
+	netif_carrier_off(pdsc->netdev);
+
+	return register_netdev(pdsc->netdev);
+}
+
+void pdsc_vf_attr_replay(struct pdsc *pdsc)
+{
+	struct pds_core_vf_setattr_cmd vfc;
+	struct pdsc_vf *v;
+	int i;
+
+	if (!pdsc->vfs)
+		return;
+
+	down_read(&pdsc->vf_op_lock);
+
+	for (i = 0; i < pdsc->num_vfs; i++) {
+		v = &pdsc->vfs[i];
+
+		if (v->stats_pa) {
+			vfc.attr = PDS_CORE_VF_ATTR_STATSADDR;
+			vfc.stats.len = cpu_to_le32(sizeof(v->stats_pa));
+			vfc.stats.pa = cpu_to_le64(v->stats_pa);
+			pdsc_set_vf_config(pdsc, i, &vfc);
+			vfc.stats.pa = 0;
+			vfc.stats.len = 0;
+		}
+
+		if (!is_zero_ether_addr(v->macaddr)) {
+			vfc.attr = PDS_CORE_VF_ATTR_MAC;
+			ether_addr_copy(vfc.macaddr, v->macaddr);
+			pdsc_set_vf_config(pdsc, i, &vfc);
+			eth_zero_addr(vfc.macaddr);
+		}
+
+		if (v->vlanid) {
+			vfc.attr = PDS_CORE_VF_ATTR_VLAN;
+			vfc.vlanid = v->vlanid;
+			pdsc_set_vf_config(pdsc, i, &vfc);
+			vfc.vlanid = 0;
+		}
+
+		if (v->maxrate) {
+			vfc.attr = PDS_CORE_VF_ATTR_RATE;
+			vfc.maxrate = v->maxrate;
+			pdsc_set_vf_config(pdsc, i, &vfc);
+			vfc.maxrate = 0;
+		}
+
+		if (v->spoofchk) {
+			vfc.attr = PDS_CORE_VF_ATTR_SPOOFCHK;
+			vfc.spoofchk = v->spoofchk;
+			pdsc_set_vf_config(pdsc, i, &vfc);
+			vfc.spoofchk = 0;
+		}
+
+		if (v->trusted) {
+			vfc.attr = PDS_CORE_VF_ATTR_TRUST;
+			vfc.trust = v->trusted;
+			pdsc_set_vf_config(pdsc, i, &vfc);
+			vfc.trust = 0;
+		}
+
+		if (v->linkstate) {
+			vfc.attr = PDS_CORE_VF_ATTR_LINKSTATE;
+			vfc.linkstate = v->linkstate;
+			pdsc_set_vf_config(pdsc, i, &vfc);
+			vfc.linkstate = 0;
+		}
+	}
+
+	up_read(&pdsc->vf_op_lock);
+
+	pds_devcmd_vf_start(pdsc);
+}
