diff mbox series

[net-next] net: mvpp2: Add reserved port private flag configuration

Message ID 1615369329-9389-1-git-send-email-stefanc@marvell.com (mailing list archive)
State Superseded
Delegated to: Netdev Maintainers
Headers show
Series [net-next] net: mvpp2: Add reserved port private flag configuration | expand

Checks

Context Check Description
netdev/cover_letter success Link
netdev/fixes_present success Link
netdev/patch_count success Link
netdev/tree_selection success Clearly marked for net-next
netdev/subject_prefix success Link
netdev/cc_maintainers warning 5 maintainers not CCed: hawk@kernel.org daniel@iogearbox.net bpf@vger.kernel.org ast@kernel.org john.fastabend@gmail.com
netdev/source_inline success Was 0 now: 0
netdev/verify_signedoff success Link
netdev/module_param success Was 0 now: 0
netdev/build_32bit success Errors and warnings before: 5 this patch: 5
netdev/kdoc success Errors and warnings before: 0 this patch: 0
netdev/verify_fixes success Link
netdev/checkpatch warning WARNING: line length of 82 exceeds 80 columns WARNING: line length of 86 exceeds 80 columns WARNING: line length of 88 exceeds 80 columns
netdev/build_allmodconfig_warn success Errors and warnings before: 5 this patch: 5
netdev/header_inline success Link

Commit Message

Stefan Chulski March 10, 2021, 9:42 a.m. UTC
From: Stefan Chulski <stefanc@marvell.com>

According to Armada SoC architecture and design, all the PPv2 ports
which are populated on the same communication processor silicon die
(CP11x) share the same Classifier and Parser engines.

Armada is an embedded platform and therefore there is a need to reserve
some of the PPv2 ports for different use cases.

For example, a port can be reserved for a CM3 CPU running FreeRTOS for
management purposes or by user-space data plane application.

During port reservation all common configurations are preserved and
only RXQ, TXQ, and interrupt vectors are disabled.

Signed-off-by: Stefan Chulski <stefanc@marvell.com>
---
 drivers/net/ethernet/marvell/mvpp2/mvpp2.h      |   4 +
 drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c | 286 ++++++++++++++++----
 2 files changed, 242 insertions(+), 48 deletions(-)

Comments

Andrew Lunn March 10, 2021, 3:50 p.m. UTC | #1
>  static void mvpp2_ethtool_get_strings(struct net_device *netdev, u32 sset,
>  				      u8 *data)
>  {
>  	struct mvpp2_port *port = netdev_priv(netdev);
>  	int i, q;
>  
> -	if (sset != ETH_SS_STATS)
> -		return;
> +	switch (sset) {
> +	case ETH_SS_STATS:
> +		for (i = 0; i < ARRAY_SIZE(mvpp2_ethtool_mib_regs); i++) {
> +			strscpy(data, mvpp2_ethtool_mib_regs[i].string,
> +				ETH_GSTRING_LEN);
> +			data += ETH_GSTRING_LEN;
> +		}

Hi Stefan

Maybe rename the existing function to
mvpp2_ethtool_get_strings_stats() and turn it into a helper. Add a new
mvpp2_ethtool_get_strings_priv() helper. And a new
mvpp2_ethtool_get_strings() which just calls the two helpers. Overall
the patch should be smaller and much easier to review.

    Andrew
Stefan Chulski March 10, 2021, 4:37 p.m. UTC | #2
> -----Original Message-----
> From: Andrew Lunn <andrew@lunn.ch>
> Sent: Wednesday, March 10, 2021 5:51 PM
> To: Stefan Chulski <stefanc@marvell.com>
> Cc: netdev@vger.kernel.org; thomas.petazzoni@bootlin.com;
> davem@davemloft.net; Nadav Haklai <nadavh@marvell.com>; Yan
> Markman <ymarkman@marvell.com>; linux-kernel@vger.kernel.org;
> kuba@kernel.org; linux@armlinux.org.uk; mw@semihalf.com;
> rmk+kernel@armlinux.org.uk; atenart@kernel.org; rabeeh@solid-run.com
> Subject: [EXT] Re: [net-next] net: mvpp2: Add reserved port private flag
> configuration
> 
> External Email
> 
> ----------------------------------------------------------------------
> >  static void mvpp2_ethtool_get_strings(struct net_device *netdev, u32
> sset,
> >  				      u8 *data)
> >  {
> >  	struct mvpp2_port *port = netdev_priv(netdev);
> >  	int i, q;
> >
> > -	if (sset != ETH_SS_STATS)
> > -		return;
> > +	switch (sset) {
> > +	case ETH_SS_STATS:
> > +		for (i = 0; i < ARRAY_SIZE(mvpp2_ethtool_mib_regs); i++) {
> > +			strscpy(data, mvpp2_ethtool_mib_regs[i].string,
> > +				ETH_GSTRING_LEN);
> > +			data += ETH_GSTRING_LEN;
> > +		}
> 
> Hi Stefan
> 
> Maybe rename the existing function to
> mvpp2_ethtool_get_strings_stats() and turn it into a helper. Add a new
> mvpp2_ethtool_get_strings_priv() helper. And a new
> mvpp2_ethtool_get_strings() which just calls the two helpers. 

OK, I can do this.

> Overall the
> patch should be smaller and much easier to review.
> 
>     Andrew

Make it patch series? I can split it to 2/3 patches.
Thanks,
Stefan.
Andrew Lunn March 10, 2021, 6:18 p.m. UTC | #3
> Make it patch series? I can split it to 2/3 patches.

I don't think that will be needed. The helpers should be pretty
obvious.

	Andrew
Russell King (Oracle) March 10, 2021, 7 p.m. UTC | #4
On Wed, Mar 10, 2021 at 11:42:09AM +0200, stefanc@marvell.com wrote:
> From: Stefan Chulski <stefanc@marvell.com>
> 
> According to Armada SoC architecture and design, all the PPv2 ports
> which are populated on the same communication processor silicon die
> (CP11x) share the same Classifier and Parser engines.
> 
> Armada is an embedded platform and therefore there is a need to reserve
> some of the PPv2 ports for different use cases.
> 
> For example, a port can be reserved for a CM3 CPU running FreeRTOS for
> management purposes or by user-space data plane application.
> 
> During port reservation all common configurations are preserved and
> only RXQ, TXQ, and interrupt vectors are disabled.

If a port is reserved for use by the CM3, what are the implications
for Linux running on the AP? Should Linux have knowledge of the port?
What configurations of the port should be permitted?

I think describing how a port reserved for use by the CM3 CPU should
appear to Linux is particularly important for the commit commentry
to cover.
Stefan Chulski March 11, 2021, 8:12 a.m. UTC | #5
> > From: Stefan Chulski <stefanc@marvell.com>
> >
> > According to Armada SoC architecture and design, all the PPv2 ports
> > which are populated on the same communication processor silicon die
> > (CP11x) share the same Classifier and Parser engines.
> >
> > Armada is an embedded platform and therefore there is a need to
> > reserve some of the PPv2 ports for different use cases.
> >
> > For example, a port can be reserved for a CM3 CPU running FreeRTOS for
> > management purposes or by user-space data plane application.
> >
> > During port reservation all common configurations are preserved and
> > only RXQ, TXQ, and interrupt vectors are disabled.
> 
> If a port is reserved for use by the CM3, what are the implications for Linux
> running on the AP? Should Linux have knowledge of the port?
> What configurations of the port should be permitted?

In reserved mode all port TXQ's closed(Linux won't transmit any packet from this ports) and
RX interrupts disabled(Linux won't receive any packet). We still can change port MAC address, do port UP/DOWN from Linux running on the AP.
Only permitted configurations Is MTU change.
Driver .ndo_change_mtu callback has logic that switch between percpu_pools and shared pools buffer mode, we should avoid this since buffer management not done by Kernel.

> I think describing how a port reserved for use by the CM3 CPU should appear
> to Linux is particularly important for the commit commentry to cover.

Ok, I would add more info to commit message.

Thanks,
Stefan.
diff mbox series

Patch

diff --git a/drivers/net/ethernet/marvell/mvpp2/mvpp2.h b/drivers/net/ethernet/marvell/mvpp2/mvpp2.h
index 8edba5e..e2f8eec 100644
--- a/drivers/net/ethernet/marvell/mvpp2/mvpp2.h
+++ b/drivers/net/ethernet/marvell/mvpp2/mvpp2.h
@@ -865,6 +865,7 @@ 
 /* Port flags */
 #define MVPP2_F_LOOPBACK		BIT(0)
 #define MVPP2_F_DT_COMPAT		BIT(1)
+#define MVPP22_F_IF_RESERVED		BIT(2)
 
 /* Marvell tag types */
 enum mvpp2_tag_type {
@@ -1251,6 +1252,9 @@  struct mvpp2_port {
 
 	/* Firmware TX flow control */
 	bool tx_fc;
+
+	/* private storage, allocated/used by Reserved/Normal mode toggling */
+	void *res_cfg;
 };
 
 /* The mvpp2_tx_desc and mvpp2_rx_desc structures describe the
diff --git a/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c b/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c
index d415447..80ddf1c 100644
--- a/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c
+++ b/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c
@@ -55,6 +55,14 @@  enum mvpp2_bm_pool_log_num {
 	int buf_num;
 } mvpp2_pools[MVPP2_BM_POOLS_NUM];
 
+struct mvpp2_port_port_cfg {
+	unsigned int nqvecs;
+	unsigned int nrxqs;
+	unsigned int ntxqs;
+	int mtu;
+	bool rxhash_en;
+};
+
 /* The prototype is added here to be used in start_dev when using ACPI. This
  * will be removed once phylink is used for all modes (dt+ACPI).
  */
@@ -1431,6 +1439,9 @@  static void mvpp2_interrupts_unmask(void *arg)
 	if (cpu >= port->priv->nthreads)
 		return;
 
+	if (port->flags & MVPP22_F_IF_RESERVED)
+		return;
+
 	thread = mvpp2_cpu_to_thread(port->priv, cpu);
 
 	val = MVPP2_CAUSE_MISC_SUM_MASK |
@@ -1942,48 +1953,58 @@  static u32 mvpp2_read_index(struct mvpp2 *priv, u32 index, u32 reg)
 						 (ARRAY_SIZE(mvpp2_ethtool_rxq_regs) * (nrxqs)) + \
 						 ARRAY_SIZE(mvpp2_ethtool_xdp))
 
+static const char mvpp22_priv_flags_strings[][ETH_GSTRING_LEN] = {
+	"reserved",
+};
+
+#define MVPP22_F_IF_RESERVED_PRIV	BIT(0)
+
 static void mvpp2_ethtool_get_strings(struct net_device *netdev, u32 sset,
 				      u8 *data)
 {
 	struct mvpp2_port *port = netdev_priv(netdev);
 	int i, q;
 
-	if (sset != ETH_SS_STATS)
-		return;
+	switch (sset) {
+	case ETH_SS_STATS:
+		for (i = 0; i < ARRAY_SIZE(mvpp2_ethtool_mib_regs); i++) {
+			strscpy(data, mvpp2_ethtool_mib_regs[i].string,
+				ETH_GSTRING_LEN);
+			data += ETH_GSTRING_LEN;
+		}
 
-	for (i = 0; i < ARRAY_SIZE(mvpp2_ethtool_mib_regs); i++) {
-		strscpy(data, mvpp2_ethtool_mib_regs[i].string,
-			ETH_GSTRING_LEN);
-		data += ETH_GSTRING_LEN;
-	}
+		for (i = 0; i < ARRAY_SIZE(mvpp2_ethtool_port_regs); i++) {
+			strscpy(data, mvpp2_ethtool_port_regs[i].string,
+				ETH_GSTRING_LEN);
+			data += ETH_GSTRING_LEN;
+		}
 
-	for (i = 0; i < ARRAY_SIZE(mvpp2_ethtool_port_regs); i++) {
-		strscpy(data, mvpp2_ethtool_port_regs[i].string,
-			ETH_GSTRING_LEN);
-		data += ETH_GSTRING_LEN;
-	}
+		for (q = 0; q < port->ntxqs; q++) {
+			for (i = 0; i < ARRAY_SIZE(mvpp2_ethtool_txq_regs); i++) {
+				snprintf(data, ETH_GSTRING_LEN,
+					 mvpp2_ethtool_txq_regs[i].string, q);
+				data += ETH_GSTRING_LEN;
+			}
+		}
 
-	for (q = 0; q < port->ntxqs; q++) {
-		for (i = 0; i < ARRAY_SIZE(mvpp2_ethtool_txq_regs); i++) {
-			snprintf(data, ETH_GSTRING_LEN,
-				 mvpp2_ethtool_txq_regs[i].string, q);
-			data += ETH_GSTRING_LEN;
+		for (q = 0; q < port->nrxqs; q++) {
+			for (i = 0; i < ARRAY_SIZE(mvpp2_ethtool_rxq_regs); i++) {
+				snprintf(data, ETH_GSTRING_LEN,
+					 mvpp2_ethtool_rxq_regs[i].string,
+					 q);
+				data += ETH_GSTRING_LEN;
+			}
 		}
-	}
 
-	for (q = 0; q < port->nrxqs; q++) {
-		for (i = 0; i < ARRAY_SIZE(mvpp2_ethtool_rxq_regs); i++) {
-			snprintf(data, ETH_GSTRING_LEN,
-				 mvpp2_ethtool_rxq_regs[i].string,
-				 q);
+		for (i = 0; i < ARRAY_SIZE(mvpp2_ethtool_xdp); i++) {
+			strscpy(data, mvpp2_ethtool_xdp[i].string,
+				ETH_GSTRING_LEN);
 			data += ETH_GSTRING_LEN;
 		}
-	}
-
-	for (i = 0; i < ARRAY_SIZE(mvpp2_ethtool_xdp); i++) {
-		strscpy(data, mvpp2_ethtool_xdp[i].string,
-			ETH_GSTRING_LEN);
-		data += ETH_GSTRING_LEN;
+		break;
+	case ETH_SS_PRIV_FLAGS:
+		memcpy(data, mvpp22_priv_flags_strings,
+		       ARRAY_SIZE(mvpp22_priv_flags_strings) * ETH_GSTRING_LEN);
 	}
 }
 
@@ -2130,8 +2151,13 @@  static int mvpp2_ethtool_get_sset_count(struct net_device *dev, int sset)
 {
 	struct mvpp2_port *port = netdev_priv(dev);
 
-	if (sset == ETH_SS_STATS)
+	switch (sset) {
+	case ETH_SS_STATS:
 		return MVPP2_N_ETHTOOL_STATS(port->ntxqs, port->nrxqs);
+	case ETH_SS_PRIV_FLAGS:
+		return (port->priv->hw_version == MVPP21) ?
+			0 : ARRAY_SIZE(mvpp22_priv_flags_strings);
+	}
 
 	return -EOPNOTSUPP;
 }
@@ -2207,6 +2233,9 @@  static inline void mvpp2_gmac_max_rx_size_set(struct mvpp2_port *port)
 {
 	u32 val;
 
+	if (port->flags & MVPP22_F_IF_RESERVED)
+		return;
+
 	val = readl(port->base + MVPP2_GMAC_CTRL_0_REG);
 	val &= ~MVPP2_GMAC_MAX_RX_SIZE_MASK;
 	val |= (((port->pkt_size - MVPP2_MH_SIZE) / 2) <<
@@ -2219,6 +2248,9 @@  static inline void mvpp2_xlg_max_rx_size_set(struct mvpp2_port *port)
 {
 	u32 val;
 
+	if (port->flags & MVPP22_F_IF_RESERVED)
+		return;
+
 	val =  readl(port->base + MVPP22_XLG_CTRL1_REG);
 	val &= ~MVPP22_XLG_CTRL1_FRAMESIZELIMIT_MASK;
 	val |= ((port->pkt_size - MVPP2_MH_SIZE) / 2) <<
@@ -2321,6 +2353,9 @@  static void mvpp2_egress_enable(struct mvpp2_port *port)
 	int queue;
 	int tx_port_num = mvpp2_egress_port(port);
 
+	if (port->flags & MVPP22_F_IF_RESERVED)
+		return;
+
 	/* Enable all initialized TXs. */
 	qmap = 0;
 	for (queue = 0; queue < port->ntxqs; queue++) {
@@ -2343,6 +2378,9 @@  static void mvpp2_egress_disable(struct mvpp2_port *port)
 	int delay;
 	int tx_port_num = mvpp2_egress_port(port);
 
+	if (port->flags & MVPP22_F_IF_RESERVED)
+		return;
+
 	/* Issue stop command for active channels only */
 	mvpp2_write(port->priv, MVPP2_TXP_SCHED_PORT_INDEX_REG, tx_port_num);
 	reg_data = (mvpp2_read(port->priv, MVPP2_TXP_SCHED_Q_CMD_REG)) &
@@ -3411,7 +3449,8 @@  static void mvpp2_isr_handle_link(struct mvpp2_port *port, bool link)
 		mvpp2_egress_enable(port);
 		mvpp2_ingress_enable(port);
 		netif_carrier_on(dev);
-		netif_tx_wake_all_queues(dev);
+		if (!(port->flags & MVPP22_F_IF_RESERVED))
+			netif_tx_wake_all_queues(dev);
 	} else {
 		netif_tx_stop_all_queues(dev);
 		netif_carrier_off(dev);
@@ -4556,7 +4595,8 @@  static void mvpp2_start_dev(struct mvpp2_port *port)
 		mvpp2_acpi_start(port);
 	}
 
-	netif_tx_start_all_queues(port->dev);
+	if (!(port->flags & MVPP22_F_IF_RESERVED))
+		netif_tx_start_all_queues(port->dev);
 
 	clear_bit(0, &port->state);
 }
@@ -4702,7 +4742,8 @@  static void mvpp2_irqs_deinit(struct mvpp2_port *port)
 static bool mvpp22_rss_is_supported(struct mvpp2_port *port)
 {
 	return (queue_mode == MVPP2_QDIST_MULTI_MODE) &&
-		!(port->flags & MVPP2_F_LOOPBACK);
+		!(port->flags & MVPP2_F_LOOPBACK) &&
+		!(port->flags & MVPP22_F_IF_RESERVED);
 }
 
 static int mvpp2_open(struct net_device *dev)
@@ -4719,20 +4760,23 @@  static int mvpp2_open(struct net_device *dev)
 		netdev_err(dev, "mvpp2_prs_mac_da_accept BC failed\n");
 		return err;
 	}
-	err = mvpp2_prs_mac_da_accept(port, dev->dev_addr, true);
-	if (err) {
-		netdev_err(dev, "mvpp2_prs_mac_da_accept own addr failed\n");
-		return err;
-	}
-	err = mvpp2_prs_tag_mode_set(port->priv, port->id, MVPP2_TAG_TYPE_MH);
-	if (err) {
-		netdev_err(dev, "mvpp2_prs_tag_mode_set failed\n");
-		return err;
-	}
-	err = mvpp2_prs_def_flow(port);
-	if (err) {
-		netdev_err(dev, "mvpp2_prs_def_flow failed\n");
-		return err;
+
+	if (!(port->flags & MVPP22_F_IF_RESERVED)) {
+		err = mvpp2_prs_mac_da_accept(port, dev->dev_addr, true);
+		if (err) {
+			netdev_err(dev, "mvpp2_prs_mac_da_accept own addr failed\n");
+			return err;
+		}
+		err = mvpp2_prs_tag_mode_set(port->priv, port->id, MVPP2_TAG_TYPE_MH);
+		if (err) {
+			netdev_err(dev, "mvpp2_prs_tag_mode_set failed\n");
+			return err;
+		}
+		err = mvpp2_prs_def_flow(port);
+		if (err) {
+			netdev_err(dev, "mvpp2_prs_def_flow failed\n");
+			return err;
+		}
 	}
 
 	/* Allocate the Rx/Tx queues */
@@ -4979,6 +5023,11 @@  static int mvpp2_change_mtu(struct net_device *dev, int mtu)
 	struct mvpp2 *priv = port->priv;
 	int err;
 
+	if (port->flags & MVPP22_F_IF_RESERVED) {
+		netdev_err(dev, "MTU can not be modified for port in reserved mode\n");
+		return -EPERM;
+	}
+
 	if (!IS_ALIGNED(MVPP2_RX_PKT_SIZE(mtu), 8)) {
 		netdev_info(dev, "illegal MTU value %d, round to %d\n", mtu,
 			    ALIGN(MVPP2_RX_PKT_SIZE(mtu), 8));
@@ -5385,12 +5434,16 @@  static int mvpp2_ethtool_get_coalesce(struct net_device *dev,
 static void mvpp2_ethtool_get_drvinfo(struct net_device *dev,
 				      struct ethtool_drvinfo *drvinfo)
 {
+	struct mvpp2_port *port = netdev_priv(dev);
+
 	strlcpy(drvinfo->driver, MVPP2_DRIVER_NAME,
 		sizeof(drvinfo->driver));
 	strlcpy(drvinfo->version, MVPP2_DRIVER_VERSION,
 		sizeof(drvinfo->version));
 	strlcpy(drvinfo->bus_info, dev_name(&dev->dev),
 		sizeof(drvinfo->bus_info));
+	drvinfo->n_priv_flags = (port->priv->hw_version == MVPP21) ?
+			0 : ARRAY_SIZE(mvpp22_priv_flags_strings);
 }
 
 static void mvpp2_ethtool_get_ringparam(struct net_device *dev,
@@ -5662,6 +5715,139 @@  static int mvpp2_ethtool_set_rxfh_context(struct net_device *dev,
 
 	return mvpp22_port_rss_ctx_indir_set(port, *rss_context, indir);
 }
+
+static u32 mvpp22_get_priv_flags(struct net_device *dev)
+{
+	struct mvpp2_port *port = netdev_priv(dev);
+	u32 priv_flags = 0;
+
+	if (port->flags & MVPP22_F_IF_RESERVED)
+		priv_flags |= MVPP22_F_IF_RESERVED_PRIV;
+	return priv_flags;
+}
+
+static int mvpp2_port_reserved_cfg(struct net_device *dev, bool ena)
+{
+	struct mvpp2_port *port = netdev_priv(dev);
+	struct mvpp2_port_port_cfg *cfg;
+
+	if (ena) {
+		/* Disable Queues and IntVec allocations for reserved ports,
+		 * but save original values.
+		 */
+		cfg = kzalloc(sizeof(*cfg), GFP_KERNEL);
+		if (!cfg)
+			return -ENOMEM;
+		port->res_cfg = (void *)cfg;
+		cfg->nqvecs = port->nqvecs;
+		cfg->nrxqs  = port->nrxqs;
+		cfg->ntxqs = port->ntxqs;
+		cfg->mtu = dev->mtu;
+		cfg->rxhash_en = !!(dev->hw_features & NETIF_F_RXHASH);
+
+		port->nqvecs = 0;
+		port->nrxqs  = 0;
+		port->ntxqs  = 0;
+		if (cfg->rxhash_en) {
+			dev->hw_features &= ~NETIF_F_RXHASH;
+			netdev_update_features(dev);
+		}
+	} else {
+		struct mvpp2_bm_pool *pool;
+		int i;
+
+		/* Back to normal mode */
+		cfg = port->res_cfg;
+		port->nqvecs = cfg->nqvecs;
+		port->nrxqs  = cfg->nrxqs;
+		port->ntxqs  = cfg->ntxqs;
+		if (cfg->rxhash_en) {
+			dev->hw_features |= NETIF_F_RXHASH;
+			netdev_update_features(dev);
+		}
+		kfree(cfg);
+		port->res_cfg = NULL;
+
+		/* Restore RxQ/pool association */
+		for (i = 0; i < port->nrxqs; i++) {
+			if (port->priv->percpu_pools) {
+				pool = &port->priv->bm_pools[i];
+				mvpp2_rxq_short_pool_set(port, i, pool->id);
+				pool = &port->priv->bm_pools[i + port->nrxqs];
+				mvpp2_rxq_long_pool_set(port, i, pool->id);
+			} else {
+				mvpp2_rxq_short_pool_set(port, i, port->pool_short->id);
+				mvpp2_rxq_long_pool_set(port, i, port->pool_long->id);
+			}
+		}
+	}
+	return 0;
+}
+
+static int mvpp2_port_reserved_set(struct net_device *dev, bool ena)
+{
+	struct mvpp2_port *port = netdev_priv(dev);
+	bool running = netif_running(dev);
+	struct mvpp2 *priv = port->priv;
+	int err;
+
+	/* This procedure is called by ethtool change or by Module-remove.
+	 * For "remove" do anything only if we are in reserved-mode
+	 * and toggling back to Normal-mode is required.
+	 */
+	if (!ena && !port->res_cfg)
+		return 0;
+
+	if (ena) {
+		port->flags |= MVPP22_F_IF_RESERVED;
+		if (priv->percpu_pools)
+			mvpp2_bm_switch_buffers(priv, false);
+	} else {
+		bool reserved = false;
+		int i;
+
+		port->flags &= ~MVPP22_F_IF_RESERVED;
+		for (i = 0; i < priv->port_count; i++)
+			if (priv->port_list[i] != port &&
+			    priv->port_list[i]->flags & MVPP22_F_IF_RESERVED) {
+				reserved = true;
+				break;
+			}
+
+		if (!reserved) {
+			dev_info(port->dev->dev.parent,
+				 "No ports in reserved mode, switching to per-cpu buffers");
+			mvpp2_bm_switch_buffers(priv, true);
+		}
+	}
+
+	if (running)
+		mvpp2_stop(dev);
+
+	err = mvpp2_port_reserved_cfg(dev, ena);
+	if (err)
+		netdev_err(dev, "reserved set=%d: error=%d\n", ena, err);
+
+	if (running)
+		mvpp2_open(dev);
+
+	return 0;
+}
+
+static int mvpp22_set_priv_flags(struct net_device *dev, u32 priv_flags)
+{
+	struct mvpp2_port *port = netdev_priv(dev);
+	bool f_old, f_new;
+	int err = 0;
+
+	f_old = port->flags & MVPP22_F_IF_RESERVED;
+	f_new = priv_flags & MVPP22_F_IF_RESERVED_PRIV;
+	if (f_old != f_new)
+		err = mvpp2_port_reserved_set(dev, f_new);
+
+	return err;
+}
+
 /* Device ops */
 
 static const struct net_device_ops mvpp2_netdev_ops = {
@@ -5705,6 +5891,8 @@  static int mvpp2_ethtool_set_rxfh_context(struct net_device *dev,
 	.set_rxfh		= mvpp2_ethtool_set_rxfh,
 	.get_rxfh_context	= mvpp2_ethtool_get_rxfh_context,
 	.set_rxfh_context	= mvpp2_ethtool_set_rxfh_context,
+	.get_priv_flags		= mvpp22_get_priv_flags,
+	.set_priv_flags		= mvpp22_set_priv_flags,
 };
 
 /* Used for PPv2.1, or PPv2.2 with the old Device Tree binding that
@@ -6602,7 +6790,8 @@  static void mvpp2_mac_link_up(struct phylink_config *config,
 
 	mvpp2_egress_enable(port);
 	mvpp2_ingress_enable(port);
-	netif_tx_wake_all_queues(port->dev);
+	if (!(port->flags & MVPP22_F_IF_RESERVED))
+		netif_tx_wake_all_queues(port->dev);
 }
 
 static void mvpp2_mac_link_down(struct phylink_config *config,
@@ -6944,6 +7133,7 @@  static void mvpp2_port_remove(struct mvpp2_port *port)
 {
 	int i;
 
+	mvpp2_port_reserved_set(port->dev, false);
 	unregister_netdev(port->dev);
 	if (port->phylink)
 		phylink_destroy(port->phylink);