linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: Vadym Kochan <vadym.kochan@plvision.eu>
To: Jiri Pirko <jiri@resnulli.us>
Cc: "David S. Miller" <davem@davemloft.net>,
	Jakub Kicinski <kuba@kernel.org>, Jiri Pirko <jiri@mellanox.com>,
	Ido Schimmel <idosch@mellanox.com>, Andrew Lunn <andrew@lunn.ch>,
	Oleksandr Mazur <oleksandr.mazur@plvision.eu>,
	Serhiy Boiko <serhiy.boiko@plvision.eu>,
	Serhiy Pshyk <serhiy.pshyk@plvision.eu>,
	Volodymyr Mytnyk <volodymyr.mytnyk@plvision.eu>,
	Taras Chornyi <taras.chornyi@plvision.eu>,
	Andrii Savka <andrii.savka@plvision.eu>,
	netdev@vger.kernel.org, linux-kernel@vger.kernel.org,
	Mickey Rachamim <mickeyr@marvell.com>
Subject: Re: [net-next 1/6] net: marvell: prestera: Add driver for Prestera family ASIC devices
Date: Wed, 10 Jun 2020 16:41:32 +0300	[thread overview]
Message-ID: <20200610134132.GB20123@plvision.eu> (raw)
In-Reply-To: <20200603141632.GA2274@nanopsycho.orion>

On Wed, Jun 03, 2020 at 04:16:32PM +0200, Jiri Pirko wrote:
> Thu, May 28, 2020 at 05:12:40PM CEST, vadym.kochan@plvision.eu wrote:
> 
> [...]
> 
> >+}
> >+
> >+int prestera_hw_port_info_get(const struct prestera_port *port,
> >+			      u16 *fp_id, u32 *hw_id, u32 *dev_id)
> 
> Please unify the ordering of "hw_id" and "dev_id" with the rest of the
> functions having the same args.
> 
OK, will do.

> 
> 
> >+{
> >+	struct prestera_msg_port_info_resp resp;
> >+	struct prestera_msg_port_info_req req = {
> >+		.port = port->id
> >+	};
> >+	int err;
> >+
> >+	err = prestera_cmd_ret(port->sw, PRESTERA_CMD_TYPE_PORT_INFO_GET,
> >+			       &req.cmd, sizeof(req), &resp.ret, sizeof(resp));
> >+	if (err)
> >+		return err;
> >+
> >+	*hw_id = resp.hw_id;
> >+	*dev_id = resp.dev_id;
> >+	*fp_id = resp.fp_id;
> >+
> >+	return 0;
> >+}
> >+
> >+int prestera_hw_switch_mac_set(struct prestera_switch *sw, char *mac)
> >+{
> >+	struct prestera_msg_switch_attr_req req = {
> >+		.attr = PRESTERA_CMD_SWITCH_ATTR_MAC,
> >+	};
> >+
> >+	memcpy(req.param.mac, mac, sizeof(req.param.mac));
> >+
> >+	return prestera_cmd(sw, PRESTERA_CMD_TYPE_SWITCH_ATTR_SET,
> >+			    &req.cmd, sizeof(req));
> >+}
> >+
> >+int prestera_hw_switch_init(struct prestera_switch *sw)
> >+{
> >+	struct prestera_msg_switch_init_resp resp;
> >+	struct prestera_msg_common_req req;
> >+	int err;
> >+
> >+	INIT_LIST_HEAD(&sw->event_handlers);
> >+
> >+	err = prestera_cmd_ret_wait(sw, PRESTERA_CMD_TYPE_SWITCH_INIT,
> >+				    &req.cmd, sizeof(req),
> >+				    &resp.ret, sizeof(resp),
> >+				    PRESTERA_SWITCH_INIT_TIMEOUT);
> >+	if (err)
> >+		return err;
> >+
> >+	sw->id = resp.switch_id;
> >+	sw->port_count = resp.port_count;
> >+	sw->mtu_min = PRESTERA_MIN_MTU;
> >+	sw->mtu_max = resp.mtu_max;
> >+	sw->dev->recv_msg = prestera_evt_recv;
> >+	sw->dev->recv_pkt = prestera_pkt_recv;
> >+
> >+	return 0;
> >+}
> >+
> >+int prestera_hw_port_state_set(const struct prestera_port *port,
> >+			       bool admin_state)
> >+{
> >+	struct prestera_msg_port_attr_req req = {
> >+		.attr = PRESTERA_CMD_PORT_ATTR_ADMIN_STATE,
> >+		.port = port->hw_id,
> >+		.dev = port->dev_id,
> >+		.param = {.admin_state = admin_state}
> >+	};
> >+
> >+	return prestera_cmd(port->sw, PRESTERA_CMD_TYPE_PORT_ATTR_SET,
> >+			    &req.cmd, sizeof(req));
> >+}
> >+
> >+int prestera_hw_port_mtu_set(const struct prestera_port *port, u32 mtu)
> >+{
> >+	struct prestera_msg_port_attr_req req = {
> >+		.attr = PRESTERA_CMD_PORT_ATTR_MTU,
> >+		.port = port->hw_id,
> >+		.dev = port->dev_id,
> >+		.param = {.mtu = mtu}
> >+	};
> >+
> >+	return prestera_cmd(port->sw, PRESTERA_CMD_TYPE_PORT_ATTR_SET,
> >+			    &req.cmd, sizeof(req));
> >+}
> >+
> >+int prestera_hw_port_mac_set(const struct prestera_port *port, char *mac)
> >+{
> >+	struct prestera_msg_port_attr_req req = {
> >+		.attr = PRESTERA_CMD_PORT_ATTR_MAC,
> >+		.port = port->hw_id,
> >+		.dev = port->dev_id
> >+	};
> >+	memcpy(&req.param.mac, mac, sizeof(req.param.mac));
> >+
> >+	return prestera_cmd(port->sw, PRESTERA_CMD_TYPE_PORT_ATTR_SET,
> >+			    &req.cmd, sizeof(req));
> >+}
> >+
> >+int prestera_hw_port_cap_get(const struct prestera_port *port,
> >+			     struct prestera_port_caps *caps)
> >+{
> >+	struct prestera_msg_port_attr_resp resp;
> >+	struct prestera_msg_port_attr_req req = {
> >+		.attr = PRESTERA_CMD_PORT_ATTR_CAPABILITY,
> >+		.port = port->hw_id,
> >+		.dev = port->dev_id
> >+	};
> >+	int err;
> >+
> >+	err = prestera_cmd_ret(port->sw, PRESTERA_CMD_TYPE_PORT_ATTR_GET,
> >+			       &req.cmd, sizeof(req), &resp.ret, sizeof(resp));
> >+	if (err)
> >+		return err;
> >+
> >+	caps->supp_link_modes = resp.param.cap.link_mode;
> >+	caps->supp_fec = resp.param.cap.fec;
> >+	caps->type = resp.param.cap.type;
> >+	caps->transceiver = resp.param.cap.transceiver;
> >+
> >+	return err;
> >+}
> >+
> >+int prestera_hw_port_autoneg_set(const struct prestera_port *port,
> >+				 bool autoneg, u64 link_modes, u8 fec)
> >+{
> >+	struct prestera_msg_port_attr_req req = {
> >+		.attr = PRESTERA_CMD_PORT_ATTR_AUTONEG,
> >+		.port = port->hw_id,
> >+		.dev = port->dev_id,
> >+		.param = {.autoneg = {.link_mode = link_modes,
> >+				      .enable = autoneg,
> >+				      .fec = fec}
> >+		}
> >+	};
> >+
> >+	return prestera_cmd(port->sw, PRESTERA_CMD_TYPE_PORT_ATTR_SET,
> >+			    &req.cmd, sizeof(req));
> >+}
> >+
> >+int prestera_hw_port_stats_get(const struct prestera_port *port,
> >+			       struct prestera_port_stats *st)
> >+{
> >+	struct prestera_msg_port_stats_resp resp;
> >+	struct prestera_msg_port_attr_req req = {
> >+		.attr = PRESTERA_CMD_PORT_ATTR_STATS,
> >+		.port = port->hw_id,
> >+		.dev = port->dev_id
> >+	};
> >+	u64 *hw = resp.stats;
> >+	int err;
> >+
> >+	err = prestera_cmd_ret(port->sw, PRESTERA_CMD_TYPE_PORT_ATTR_GET,
> >+			       &req.cmd, sizeof(req), &resp.ret, sizeof(resp));
> >+	if (err)
> >+		return err;
> >+
> >+	st->good_octets_received = hw[PRESTERA_PORT_GOOD_OCTETS_RCV_CNT];
> >+	st->bad_octets_received = hw[PRESTERA_PORT_BAD_OCTETS_RCV_CNT];
> >+	st->mac_trans_error = hw[PRESTERA_PORT_MAC_TRANSMIT_ERR_CNT];
> >+	st->broadcast_frames_received = hw[PRESTERA_PORT_BRDC_PKTS_RCV_CNT];
> >+	st->multicast_frames_received = hw[PRESTERA_PORT_MC_PKTS_RCV_CNT];
> >+	st->frames_64_octets = hw[PRESTERA_PORT_PKTS_64L_CNT];
> >+	st->frames_65_to_127_octets = hw[PRESTERA_PORT_PKTS_65TO127L_CNT];
> >+	st->frames_128_to_255_octets = hw[PRESTERA_PORT_PKTS_128TO255L_CNT];
> >+	st->frames_256_to_511_octets = hw[PRESTERA_PORT_PKTS_256TO511L_CNT];
> >+	st->frames_512_to_1023_octets = hw[PRESTERA_PORT_PKTS_512TO1023L_CNT];
> >+	st->frames_1024_to_max_octets = hw[PRESTERA_PORT_PKTS_1024TOMAXL_CNT];
> >+	st->excessive_collision = hw[PRESTERA_PORT_EXCESSIVE_COLLISIONS_CNT];
> >+	st->multicast_frames_sent = hw[PRESTERA_PORT_MC_PKTS_SENT_CNT];
> >+	st->broadcast_frames_sent = hw[PRESTERA_PORT_BRDC_PKTS_SENT_CNT];
> >+	st->fc_sent = hw[PRESTERA_PORT_FC_SENT_CNT];
> >+	st->fc_received = hw[PRESTERA_PORT_GOOD_FC_RCV_CNT];
> >+	st->buffer_overrun = hw[PRESTERA_PORT_DROP_EVENTS_CNT];
> >+	st->undersize = hw[PRESTERA_PORT_UNDERSIZE_PKTS_CNT];
> >+	st->fragments = hw[PRESTERA_PORT_FRAGMENTS_PKTS_CNT];
> >+	st->oversize = hw[PRESTERA_PORT_OVERSIZE_PKTS_CNT];
> >+	st->jabber = hw[PRESTERA_PORT_JABBER_PKTS_CNT];
> >+	st->rx_error_frame_received = hw[PRESTERA_PORT_MAC_RCV_ERROR_CNT];
> >+	st->bad_crc = hw[PRESTERA_PORT_BAD_CRC_CNT];
> >+	st->collisions = hw[PRESTERA_PORT_COLLISIONS_CNT];
> >+	st->late_collision = hw[PRESTERA_PORT_LATE_COLLISIONS_CNT];
> >+	st->unicast_frames_received = hw[PRESTERA_PORT_GOOD_UC_PKTS_RCV_CNT];
> >+	st->unicast_frames_sent = hw[PRESTERA_PORT_GOOD_UC_PKTS_SENT_CNT];
> >+	st->sent_multiple = hw[PRESTERA_PORT_MULTIPLE_PKTS_SENT_CNT];
> >+	st->sent_deferred = hw[PRESTERA_PORT_DEFERRED_PKTS_SENT_CNT];
> >+	st->good_octets_sent = hw[PRESTERA_PORT_GOOD_OCTETS_SENT_CNT];
> >+
> >+	return 0;
> >+}
> >+
> >+int prestera_hw_rxtx_init(struct prestera_switch *sw,
> >+			  struct prestera_rxtx_params *params)
> >+{
> >+	struct prestera_msg_rxtx_resp resp;
> >+	struct prestera_msg_rxtx_req req;
> >+	int err;
> >+
> >+	req.use_sdma = params->use_sdma;
> >+
> >+	err = prestera_cmd_ret(sw, PRESTERA_CMD_TYPE_RXTX_INIT,
> >+			       &req.cmd, sizeof(req), &resp.ret, sizeof(resp));
> >+	if (err)
> >+		return err;
> >+
> >+	params->map_addr = resp.map_addr;
> >+	return 0;
> >+}
> >+
> >+int prestera_hw_rxtx_port_init(struct prestera_port *port)
> >+{
> >+	struct prestera_msg_rxtx_port_req req = {
> >+		.port = port->hw_id,
> >+		.dev = port->dev_id,
> >+	};
> >+
> >+	return prestera_cmd(port->sw, PRESTERA_CMD_TYPE_RXTX_PORT_INIT,
> >+			    &req.cmd, sizeof(req));
> >+}
> >+
> >+int prestera_hw_event_handler_register(struct prestera_switch *sw,
> >+				       enum prestera_event_type type,
> >+				       prestera_event_cb_t fn,
> >+				       void *arg)
> >+{
> >+	struct prestera_fw_event_handler *eh;
> >+
> >+	eh = __find_event_handler(sw, type);
> >+	if (eh)
> >+		return -EEXIST;
> >+	eh = kmalloc(sizeof(*eh), GFP_KERNEL);
> >+	if (!eh)
> >+		return -ENOMEM;
> >+
> >+	eh->type = type;
> >+	eh->func = fn;
> >+	eh->arg = arg;
> >+
> >+	INIT_LIST_HEAD(&eh->list);
> >+
> >+	list_add_rcu(&eh->list, &sw->event_handlers);
> >+
> >+	return 0;
> >+}
> >+
> >+void prestera_hw_event_handler_unregister(struct prestera_switch *sw,
> >+					  enum prestera_event_type type,
> >+					  prestera_event_cb_t fn)
> >+{
> >+	struct prestera_fw_event_handler *eh;
> >+
> >+	eh = __find_event_handler(sw, type);
> >+	if (!eh)
> >+		return;
> >+
> >+	list_del_rcu(&eh->list);
> >+	synchronize_rcu();
> >+	kfree(eh);
> 
> Try to avoid use of synchronice rcu. You can rather do:
> kfree_rcu(eh, rcu);
Thanks for this.

> 
> 
> >+}
> >diff --git a/drivers/net/ethernet/marvell/prestera/prestera_hw.h b/drivers/net/ethernet/marvell/prestera/prestera_hw.h
> >new file mode 100644
> >index 000000000000..acb0e31d6684
> >--- /dev/null
> >+++ b/drivers/net/ethernet/marvell/prestera/prestera_hw.h
> >@@ -0,0 +1,71 @@
> >+/* SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
> >+ *
> >+ * Copyright (c) 2019-2020 Marvell International Ltd. All rights reserved.
> >+ *
> >+ */
> >+
> >+#ifndef _PRESTERA_HW_H_
> >+#define _PRESTERA_HW_H_
> >+
> >+#include <linux/types.h>
> >+
> >+enum {
> >+	PRESTERA_PORT_TYPE_NONE,
> >+	PRESTERA_PORT_TYPE_TP,
> >+
> >+	PRESTERA_PORT_TYPE_MAX,
> >+};
> >+
> >+enum {
> >+	PRESTERA_PORT_FEC_OFF,
> >+
> >+	PRESTERA_PORT_FEC_MAX,
> >+};
> >+
> >+struct prestera_switch;
> >+struct prestera_port;
> >+struct prestera_port_stats;
> >+struct prestera_port_caps;
> >+enum prestera_event_type;
> >+struct prestera_event;
> >+
> >+typedef void (*prestera_event_cb_t)
> >+	(struct prestera_switch *sw, struct prestera_event *evt, void *arg);
> >+
> >+struct prestera_rxtx_params;
> >+
> >+/* Switch API */
> >+int prestera_hw_switch_init(struct prestera_switch *sw);
> >+int prestera_hw_switch_mac_set(struct prestera_switch *sw, char *mac);
> >+
> >+/* Port API */
> >+int prestera_hw_port_info_get(const struct prestera_port *port,
> >+			      u16 *fp_id, u32 *hw_id, u32 *dev_id);
> >+int prestera_hw_port_state_set(const struct prestera_port *port,
> >+			       bool admin_state);
> >+int prestera_hw_port_mtu_set(const struct prestera_port *port, u32 mtu);
> >+int prestera_hw_port_mtu_get(const struct prestera_port *port, u32 *mtu);
> >+int prestera_hw_port_mac_set(const struct prestera_port *port, char *mac);
> >+int prestera_hw_port_mac_get(const struct prestera_port *port, char *mac);
> >+int prestera_hw_port_cap_get(const struct prestera_port *port,
> >+			     struct prestera_port_caps *caps);
> >+int prestera_hw_port_autoneg_set(const struct prestera_port *port,
> >+				 bool autoneg, u64 link_modes, u8 fec);
> >+int prestera_hw_port_stats_get(const struct prestera_port *port,
> >+			       struct prestera_port_stats *stats);
> >+
> >+/* Event handlers */
> >+int prestera_hw_event_handler_register(struct prestera_switch *sw,
> >+				       enum prestera_event_type type,
> >+				       prestera_event_cb_t fn,
> >+				       void *arg);
> >+void prestera_hw_event_handler_unregister(struct prestera_switch *sw,
> >+					  enum prestera_event_type type,
> >+					  prestera_event_cb_t fn);
> >+
> >+/* RX/TX */
> >+int prestera_hw_rxtx_init(struct prestera_switch *sw,
> >+			  struct prestera_rxtx_params *params);
> >+int prestera_hw_rxtx_port_init(struct prestera_port *port);
> >+
> >+#endif /* _PRESTERA_HW_H_ */
> >diff --git a/drivers/net/ethernet/marvell/prestera/prestera_main.c b/drivers/net/ethernet/marvell/prestera/prestera_main.c
> >new file mode 100644
> >index 000000000000..b5241e9b784a
> >--- /dev/null
> >+++ b/drivers/net/ethernet/marvell/prestera/prestera_main.c
> >@@ -0,0 +1,506 @@
> >+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
> >+/* Copyright (c) 2019-2020 Marvell International Ltd. All rights reserved */
> >+
> >+#include <linux/kernel.h>
> >+#include <linux/module.h>
> >+#include <linux/list.h>
> >+#include <linux/netdevice.h>
> >+#include <linux/netdev_features.h>
> >+#include <linux/etherdevice.h>
> >+#include <linux/jiffies.h>
> >+#include <linux/of.h>
> >+#include <linux/of_net.h>
> >+
> >+#include "prestera.h"
> >+#include "prestera_hw.h"
> >+#include "prestera_rxtx.h"
> >+
> >+#define PRESTERA_MTU_DEFAULT 1536
> >+
> >+#define PRESTERA_STATS_DELAY_MS	msecs_to_jiffies(1000)
> >+
> >+static struct workqueue_struct *prestera_wq;
> >+
> >+struct prestera_port *prestera_port_find_by_hwid(struct prestera_switch *sw,
> >+						 u32 dev_id, u32 hw_id)
> 
> This is confusing. The called is calling this like:
> 	port = prestera_port_find_by_hwid(sdma->sw, hw_id, hw_port);
> 
> You are mixing hw_id and dev_id.
Yes, this is confusing, will fix it.

> 
> 
> 
> >+{
> >+	struct prestera_port *port;
> >+
> >+	rcu_read_lock();
> >+
> >+	list_for_each_entry_rcu(port, &sw->port_list, list) {
> >+		if (port->dev_id == dev_id && port->hw_id == hw_id) {
> 
> Note this is the fast path. I'm not sure what the values of dev_id or
> hw_id are, but didn't you consider having the port pointers in 2 dim
> array? Or, if the values are totally arbitrary, at least a hash table
> would be nice here.
The hash table may help.

> 
> 
> >+			rcu_read_unlock();
> >+			return port;
> 
> As Ido already pointed out, this is invalid use of rcu read.
> If you really need to do rcu read lock, the caller should hold it while
> calling this function and until it finisher work with port struct.
> 
> 
> >+		}
> >+	}
> >+
> >+	rcu_read_unlock();
> >+
> >+	return NULL;
> >+}
> >+
> >+static struct prestera_port *prestera_find_port(struct prestera_switch *sw,
> >+						u32 port_id)
> >+{
> >+	struct prestera_port *port;
> >+
> >+	rcu_read_lock();
> >+
> >+	list_for_each_entry_rcu(port, &sw->port_list, list) {
> >+		if (port->id == port_id)
> >+			break;
> >+	}
> >+
> >+	rcu_read_unlock();
> >+
> >+	return port;
> >+}
> >+
> >+static int prestera_port_state_set(struct net_device *dev, bool is_up)
> >+{
> >+	struct prestera_port *port = netdev_priv(dev);
> >+	int err;
> >+
> >+	if (!is_up)
> >+		netif_stop_queue(dev);
> >+
> >+	err = prestera_hw_port_state_set(port, is_up);
> >+
> >+	if (is_up && !err)
> >+		netif_start_queue(dev);
> >+
> >+	return err;
> >+}
> >+
> >+static int prestera_port_open(struct net_device *dev)
> >+{
> >+	return prestera_port_state_set(dev, true);
> >+}
> >+
> >+static int prestera_port_close(struct net_device *dev)
> >+{
> >+	return prestera_port_state_set(dev, false);
> >+}
> >+
> >+static netdev_tx_t prestera_port_xmit(struct sk_buff *skb,
> >+				      struct net_device *dev)
> >+{
> >+	return prestera_rxtx_xmit(netdev_priv(dev), skb);
> >+}
> >+
> >+static int prestera_is_valid_mac_addr(struct prestera_port *port, u8 *addr)
> >+{
> >+	if (!is_valid_ether_addr(addr))
> >+		return -EADDRNOTAVAIL;
> >+
> >+	if (memcmp(port->sw->base_mac, addr, ETH_ALEN - 1))
> >+		return -EINVAL;
> >+
> >+	return 0;
> >+}
> >+
> >+static int prestera_port_set_mac_address(struct net_device *dev, void *p)
> >+{
> >+	struct prestera_port *port = netdev_priv(dev);
> >+	struct sockaddr *addr = p;
> >+	int err;
> >+
> >+	err = prestera_is_valid_mac_addr(port, addr->sa_data);
> >+	if (err)
> >+		return err;
> >+
> >+	err = prestera_hw_port_mac_set(port, addr->sa_data);
> >+	if (err)
> >+		return err;
> >+
> >+	memcpy(dev->dev_addr, addr->sa_data, dev->addr_len);
> >+	return 0;
> >+}
> >+
> >+static int prestera_port_change_mtu(struct net_device *dev, int mtu)
> >+{
> >+	struct prestera_port *port = netdev_priv(dev);
> >+	int err;
> >+
> >+	err = prestera_hw_port_mtu_set(port, mtu);
> >+	if (err)
> >+		return err;
> >+
> >+	dev->mtu = mtu;
> >+	return 0;
> >+}
> >+
> >+static void prestera_port_get_stats64(struct net_device *dev,
> >+				      struct rtnl_link_stats64 *stats)
> >+{
> >+	struct prestera_port *port = netdev_priv(dev);
> >+	struct prestera_port_stats *port_stats = &port->cached_hw_stats.stats;
> >+
> >+	stats->rx_packets = port_stats->broadcast_frames_received +
> >+				port_stats->multicast_frames_received +
> >+				port_stats->unicast_frames_received;
> >+
> >+	stats->tx_packets = port_stats->broadcast_frames_sent +
> >+				port_stats->multicast_frames_sent +
> >+				port_stats->unicast_frames_sent;
> >+
> >+	stats->rx_bytes = port_stats->good_octets_received;
> >+
> >+	stats->tx_bytes = port_stats->good_octets_sent;
> >+
> >+	stats->rx_errors = port_stats->rx_error_frame_received;
> >+	stats->tx_errors = port_stats->mac_trans_error;
> >+
> >+	stats->rx_dropped = port_stats->buffer_overrun;
> >+	stats->tx_dropped = 0;
> >+
> >+	stats->multicast = port_stats->multicast_frames_received;
> >+	stats->collisions = port_stats->excessive_collision;
> >+
> >+	stats->rx_crc_errors = port_stats->bad_crc;
> >+}
> >+
> >+static void prestera_port_get_hw_stats(struct prestera_port *port)
> >+{
> >+	prestera_hw_port_stats_get(port, &port->cached_hw_stats.stats);
> >+}
> >+
> >+static void prestera_port_stats_update(struct work_struct *work)
> >+{
> >+	struct prestera_port *port =
> >+		container_of(work, struct prestera_port,
> >+			     cached_hw_stats.caching_dw.work);
> >+
> >+	prestera_port_get_hw_stats(port);
> >+
> >+	queue_delayed_work(prestera_wq, &port->cached_hw_stats.caching_dw,
> >+			   PRESTERA_STATS_DELAY_MS);
> >+}
> >+
> >+static const struct net_device_ops netdev_ops = {
> >+	.ndo_open = prestera_port_open,
> >+	.ndo_stop = prestera_port_close,
> >+	.ndo_start_xmit = prestera_port_xmit,
> >+	.ndo_change_mtu = prestera_port_change_mtu,
> >+	.ndo_get_stats64 = prestera_port_get_stats64,
> >+	.ndo_set_mac_address = prestera_port_set_mac_address,
> >+};
> >+
> >+static int prestera_port_autoneg_set(struct prestera_port *port, bool enable,
> >+				     u64 link_modes, u8 fec)
> >+{
> >+	bool refresh = false;
> >+	int err = 0;
> >+
> >+	if (port->caps.type != PRESTERA_PORT_TYPE_TP)
> >+		return enable ? -EINVAL : 0;
> >+
> >+	if (port->adver_link_modes != link_modes || port->adver_fec != fec) {
> >+		port->adver_fec = fec ?: BIT(PRESTERA_PORT_FEC_OFF);
> >+		port->adver_link_modes = link_modes;
> >+		refresh = true;
> >+	}
> >+
> >+	if (port->autoneg == enable && !(port->autoneg && refresh))
> >+		return 0;
> >+
> >+	err = prestera_hw_port_autoneg_set(port, enable, port->adver_link_modes,
> >+					   port->adver_fec);
> >+	if (err)
> >+		return -EINVAL;
> >+
> >+	port->autoneg = enable;
> >+	return 0;
> >+}
> >+
> >+static int prestera_port_create(struct prestera_switch *sw, u32 id)
> >+{
> >+	struct prestera_port *port;
> >+	struct net_device *dev;
> >+	int err;
> >+
> >+	dev = alloc_etherdev(sizeof(*port));
> >+	if (!dev)
> >+		return -ENOMEM;
> >+
> >+	port = netdev_priv(dev);
> >+
> >+	port->dev = dev;
> >+	port->id = id;
> >+	port->sw = sw;
> >+
> >+	err = prestera_hw_port_info_get(port, &port->fp_id,
> >+					&port->hw_id, &port->dev_id);
> >+	if (err) {
> >+		dev_err(prestera_dev(sw), "Failed to get port(%u) info\n", id);
> >+		goto err_port_init;
> >+	}
> >+
> >+	dev->features |= NETIF_F_NETNS_LOCAL;
> >+	dev->netdev_ops = &netdev_ops;
> >+
> >+	netif_carrier_off(dev);
> >+
> >+	dev->mtu = min_t(unsigned int, sw->mtu_max, PRESTERA_MTU_DEFAULT);
> >+	dev->min_mtu = sw->mtu_min;
> >+	dev->max_mtu = sw->mtu_max;
> >+
> >+	err = prestera_hw_port_mtu_set(port, dev->mtu);
> >+	if (err) {
> >+		dev_err(prestera_dev(sw), "Failed to set port(%u) mtu(%d)\n",
> >+			id, dev->mtu);
> >+		goto err_port_init;
> >+	}
> >+
> >+	/* Only 0xFF mac addrs are supported */
> >+	if (port->fp_id >= 0xFF)
> >+		goto err_port_init;
> >+
> >+	memcpy(dev->dev_addr, sw->base_mac, dev->addr_len - 1);
> >+	dev->dev_addr[dev->addr_len - 1] = (char)port->fp_id;
> >+
> >+	err = prestera_hw_port_mac_set(port, dev->dev_addr);
> >+	if (err) {
> >+		dev_err(prestera_dev(sw), "Failed to set port(%u) mac addr\n", id);
> >+		goto err_port_init;
> >+	}
> >+
> >+	err = prestera_hw_port_cap_get(port, &port->caps);
> >+	if (err) {
> >+		dev_err(prestera_dev(sw), "Failed to get port(%u) caps\n", id);
> >+		goto err_port_init;
> >+	}
> >+
> >+	port->adver_fec = BIT(PRESTERA_PORT_FEC_OFF);
> >+	prestera_port_autoneg_set(port, true, port->caps.supp_link_modes,
> >+				  port->caps.supp_fec);
> >+
> >+	err = prestera_hw_port_state_set(port, false);
> >+	if (err) {
> >+		dev_err(prestera_dev(sw), "Failed to set port(%u) down\n", id);
> >+		goto err_port_init;
> >+	}
> >+
> >+	err = prestera_rxtx_port_init(port);
> >+	if (err)
> >+		goto err_port_init;
> >+
> >+	INIT_DELAYED_WORK(&port->cached_hw_stats.caching_dw,
> >+			  &prestera_port_stats_update);
> >+
> >+	list_add_rcu(&port->list, &sw->port_list);
> 
> I still am not sure I fully follow. We discussed this before. Can one
> of the following cases happen?
> 
> 1) a packet is RXed while adding ports
> 2) a packet is RXed while removing ports
> 
> If yes, the rcu makes sense here. If no, you are okay with a simple
> list.
Well, I was afraid about the _fini part when ports are destroying and
the packets are flying at the same time. And referring to the previous
comment regarding rcu_read_lock()/..._unlock() - it needs only to
protect the ports list (while destroying/adding the ports) and using
just "rcu add/del list" looks enough because there is only 1 writer (or
use simply spinlock).

> 
> 
> >+
> >+	err = register_netdev(dev);
> >+	if (err)
> >+		goto err_register_netdev;
> >+
> >+	return 0;
> >+
> >+err_register_netdev:
> >+	list_del_rcu(&port->list);
> >+err_port_init:
> >+	free_netdev(dev);
> >+	return err;
> >+}
> >+
> >+static void prestera_port_destroy(struct prestera_port *port)
> >+{
> >+	struct net_device *dev = port->dev;
> >+
> >+	cancel_delayed_work_sync(&port->cached_hw_stats.caching_dw);
> >+	unregister_netdev(dev);
> >+
> >+	list_del_rcu(&port->list);
> >+
> >+	free_netdev(dev);
> >+}
> >+
> >+static void prestera_destroy_ports(struct prestera_switch *sw)
> >+{
> >+	struct prestera_port *port, *tmp;
> >+	struct list_head remove_list;
> >+
> >+	INIT_LIST_HEAD(&remove_list);
> >+
> >+	list_splice_init(&sw->port_list, &remove_list);
> 
> Why do you need a separate remove list? Why don't you iterate sw->port_list
> directly?
> 
> 
> >+
> >+	list_for_each_entry_safe(port, tmp, &remove_list, list)
> >+		prestera_port_destroy(port);
> >+}
> >+
> 
> [...]
> 
> 
> >+static int prestera_rxtx_process_skb(struct prestera_sdma *sdma,
> >+				     struct sk_buff *skb)
> >+{
> >+	const struct prestera_port *port;
> >+	struct prestera_dsa dsa;
> 
> What "DSA" stands for? Anything to do with net/dsa/ ?
Well, it uses special Marvell DSA tag which is used to extract
additional meta information like port number, etc (it differs from the
one which is supported in net/dsa, bacuse the latter are part of the DSA
infra).

> 
> 
> >+	u32 hw_port, hw_id;
> >+	int err;
> >+
> >+	skb_pull(skb, ETH_HLEN);
> >+
> >+	/* ethertype field is part of the dsa header */
> >+	err = prestera_dsa_parse(&dsa, skb->data - ETH_TLEN);
> >+	if (err)
> >+		return err;
> >+
> >+	hw_port = dsa.port_num;
> >+	hw_id = dsa.hw_dev_num;
> >+
> >+	port = prestera_port_find_by_hwid(sdma->sw, hw_id, hw_port);
> >+	if (unlikely(!port)) {
> >+		pr_warn_ratelimited("prestera: received pkt for non-existent port(%u, %u)\n",
> 
> Drop the "prestera: " prefix.
Okay.

> 
> 
> >+				    hw_id, hw_port);
> >+		return -EEXIST;
> >+	}
> >+
> 
> [...]

  reply	other threads:[~2020-06-10 13:41 UTC|newest]

Thread overview: 27+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2020-05-28 15:12 [net-next 0/6] net: marvell: prestera: Add Switchdev driver for Prestera family ASIC device 98DX326x (AC3x) Vadym Kochan
2020-05-28 15:12 ` [net-next 1/6] net: marvell: prestera: Add driver for Prestera family ASIC devices Vadym Kochan
2020-05-30  0:18   ` David Miller
2020-05-30  0:46     ` Vadym Kochan
2020-05-30  2:20       ` Andrew Lunn
2020-05-30 15:48   ` Ido Schimmel
2020-06-01 10:50     ` Vadym Kochan
2020-06-03  9:23       ` Ido Schimmel
2020-06-20 12:56         ` Vadym Kochan
2020-06-21  8:57           ` Ido Schimmel
2020-06-03 14:16   ` Jiri Pirko
2020-06-10 13:41     ` Vadym Kochan [this message]
2020-06-03 14:29   ` Jiri Pirko
2020-06-03 14:36     ` Jiri Pirko
2020-05-28 15:12 ` [net-next 2/6] net: marvell: prestera: Add PCI interface support Vadym Kochan
2020-06-03  9:59   ` Ido Schimmel
2020-05-28 15:12 ` [net-next 3/6] net: marvell: prestera: Add basic devlink support Vadym Kochan
2020-06-03 10:07   ` Ido Schimmel
2020-06-03 14:36   ` Jiri Pirko
2020-05-28 15:12 ` [net-next 4/6] net: marvell: prestera: Add ethtool interface support Vadym Kochan
2020-05-28 15:12 ` [net-next 5/6] net: marvell: prestera: Add Switchdev driver implementation Vadym Kochan
2020-05-28 15:12 ` [net-next 6/6] dt-bindings: marvell,prestera: Add description for device-tree bindings Vadym Kochan
2020-05-30 14:29 ` [net-next 0/6] net: marvell: prestera: Add Switchdev driver for Prestera family ASIC device 98DX326x (AC3x) Ido Schimmel
2020-05-30 14:52   ` Vadym Kochan
2020-05-30 15:54     ` Ido Schimmel
2020-06-01  6:24       ` Jiri Pirko
2020-06-20 13:25         ` Vadym Kochan

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20200610134132.GB20123@plvision.eu \
    --to=vadym.kochan@plvision.eu \
    --cc=andrew@lunn.ch \
    --cc=andrii.savka@plvision.eu \
    --cc=davem@davemloft.net \
    --cc=idosch@mellanox.com \
    --cc=jiri@mellanox.com \
    --cc=jiri@resnulli.us \
    --cc=kuba@kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=mickeyr@marvell.com \
    --cc=netdev@vger.kernel.org \
    --cc=oleksandr.mazur@plvision.eu \
    --cc=serhiy.boiko@plvision.eu \
    --cc=serhiy.pshyk@plvision.eu \
    --cc=taras.chornyi@plvision.eu \
    --cc=volodymyr.mytnyk@plvision.eu \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).