Thread (15 messages) flat view 15 messages, 3 authors, 2022-09-11

Re: [PATCH net-next v7 4/6] net: dsa: mv88e6xxxx: Add RMU functionality.

From: Mattias Forsblad <hidden>
Date: 2022-09-09 06:37:25

On 2022-09-09 03:36, Andrew Lunn wrote:
quoted
+	kfree_skb(chip->rmu.resp);
+	chip->rmu.resp = NULL;
+
+	mutex_unlock(&chip->rmu.mutex);
+
+	return ret > 0 ? 0 : ret;
+}
+
+static int mv88e6xxx_rmu_get_id(struct mv88e6xxx_chip *chip, int port)
+{
+	const u16 req[4] = { MV88E6XXX_RMU_REQ_FORMAT_GET_ID,
+			     MV88E6XXX_RMU_REQ_PAD,
+			     MV88E6XXX_RMU_REQ_CODE_GET_ID,
+			     MV88E6XXX_RMU_REQ_DATA};
+	struct rmu_header resp;
+	int ret = -1;
+	u16 format;
+	u16 code;
+
+	ret = mv88e6xxx_rmu_send_wait(chip, port, (const char *)req, sizeof(req),
+				      (char *)&resp, sizeof(resp));
+	if (ret) {
+		dev_dbg(chip->dev, "RMU: error for command GET_ID %pe\n", ERR_PTR(ret));
+		return ret;
+	}
+
+	/* Got response */
+	format = get_unaligned_be16(&resp.format);
+	code = get_unaligned_be16(&resp.code);
You don't need get_unaligned_be16() etc here, because resp is a stack
variable, it is guaranteed to be aligned. You only need to use these
helpers when you have no idea about alignment, like data in an skb.
So this I don't understand. We map a packed struct from incoming skb data
(which could be unaligned). Couldn't the struct members then be unaligned
also?
 
quoted
+
+	if (format != MV88E6XXX_RMU_RESP_FORMAT_1 &&
+	    format != MV88E6XXX_RMU_RESP_FORMAT_2 &&
+	    code != MV88E6XXX_RMU_RESP_CODE_GOT_ID) {
+		net_dbg_ratelimited("RMU: received unknown format 0x%04x code 0x%04x",
+				    format, code);
+		return -EIO;
+	}
+
+	chip->rmu.prodnr = get_unaligned_be16(&resp.prodnr);
+
+	return 0;
+}
+
+static void mv88e6xxx_rmu_stats_get(struct mv88e6xxx_chip *chip, int port, uint64_t *data)
I would split this into a separate patch. Add the core RMU handling in
one patch, and then add users of it one patch at a time. There is too
much going on in this patch, and it is not obviously correct.
Yes, this should be in the patch 5
quoted
+{
+	u16 req[4] = { MV88E6XXX_RMU_REQ_FORMAT_SOHO,
+		       MV88E6XXX_RMU_REQ_PAD,
+		       MV88E6XXX_RMU_REQ_CODE_DUMP_MIB,
+		       MV88E6XXX_RMU_REQ_DATA};
+	struct dump_mib_resp resp;
+	struct mv88e6xxx_port *p;
+	u8 resp_port;
+	u16 format;
+	u16 code;
+	int ret;
+	int i;
+
+	/* Populate port number in request */
+	req[3] = FIELD_PREP(MV88E6XXX_RMU_REQ_DUMP_MIB_PORT_MASK, port);
+
+	ret = mv88e6xxx_rmu_send_wait(chip, port, (const char *)req, sizeof(req),
+				      (char *)&resp, sizeof(resp));
+	if (ret) {
+		dev_dbg(chip->dev, "RMU: error for command DUMP_MIB %pe port %d\n",
+			ERR_PTR(ret), port);
+		return;
+	}
I'm surprised this is a void function, since mv88e6xxx_rmu_send_wait()
etc can return an error.
This is because the caller prototype is declared thus:
 	void	(*get_ethtool_stats)(struct dsa_switch *ds,
				     int port, uint64_t *data);

quoted
+	for (i = 0; i < MV88E6XXX_RMU_MAX_RMON; i++)
+		p->rmu_raw_stats[i] = get_unaligned_be32(&resp.mib[i]);
+
+	/* Update MIB for port */
+	if (chip->info->ops->stats_get_stats)
+		chip->info->ops->stats_get_stats(chip, port, data);
+}
+
+void mv88e6xxx_master_change(struct dsa_switch *ds, const struct net_device *master,
+			     bool operational)
+{
+	struct mv88e6xxx_chip *chip = ds->priv;
+	struct dsa_port *cpu_dp;
+	int port;
+	int ret;
+
+	cpu_dp = master->dsa_ptr;
+	port = dsa_towards_port(ds, cpu_dp->ds->index, cpu_dp->index);
+
+	mv88e6xxx_reg_lock(chip);
+
+	if (operational && chip->info->ops->rmu_enable) {
+		if (!chip->info->ops->rmu_enable(chip, port)) {
You should probably differentiate on the error here. -EOPNOTSUPP you
want to handle different to other errors, which are fatal.
Will fix.
quoted
+			dev_dbg(chip->dev, "RMU: Enabled on port %d", port);
+			chip->rmu.master_netdev = (struct net_device *)master;
+
+			/* Check if chip is alive */
+			ret = mv88e6xxx_rmu_get_id(chip, port);
+			if (!ret)
+				goto out;
+
+			chip->smi_ops = chip->rmu.rmu_ops;
+
+		} else {
+			dev_err(chip->dev, "RMU: Unable to enable on port %d", port);
Don't you need a goto out; here?
Will fix.
quoted
+		}
+	}
+
+	chip->smi_ops = chip->rmu.smi_ops;
+	chip->rmu.master_netdev = NULL;
+	if (chip->info->ops->rmu_disable)
+		chip->info->ops->rmu_disable(chip);
I would probably pull this function apart, break it up into helpers,
to make the flow simpler.
Ok, will fix.
quoted
+
+out:
+	mv88e6xxx_reg_unlock(chip);
+}
+
+static int mv88e6xxx_validate_mac(struct dsa_switch *ds, struct sk_buff *skb)
+{
+	struct mv88e6xxx_chip *chip = ds->priv;
+	unsigned char *ethhdr;
+
+	/* Check matching MAC */
+	ethhdr = skb_mac_header(skb);
+	if (!ether_addr_equal(chip->rmu.master_netdev->dev_addr, ethhdr)) {
+		dev_dbg_ratelimited(ds->dev, "RMU: mismatching MAC address for request. Rx %pM expecting %pM\n",
+				    ethhdr, chip->rmu.master_netdev->dev_addr);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+void mv88e6xxx_decode_frame2reg_handler(struct net_device *dev, struct sk_buff *skb)
+{
+	struct dsa_port *dp = dev->dsa_ptr;
+	struct dsa_switch *ds = dp->ds;
+	struct mv88e6xxx_chip *chip;
+	int source_device;
+	u8 *dsa_header;
+	u8 seqno;
+
+	/* Decode Frame2Reg DSA portion */
+	dsa_header = skb->data - 2;
+
+	source_device = FIELD_GET(MV88E6XXX_SOURCE_DEV, dsa_header[0]);
+	ds = dsa_switch_find(ds->dst->index, source_device);
+	if (!ds) {
+		net_err_ratelimited("RMU: Didn't find switch with index %d", source_device);
+		return;
+	}
+
+	if (mv88e6xxx_validate_mac(ds, skb))
+		return;
+
+	chip = ds->priv;
+	seqno = dsa_header[3];
+	if (seqno != chip->rmu.seqno) {
+		net_err_ratelimited("RMU: wrong seqno received. Was %d, expected %d",
+				    seqno, chip->rmu.seqno);
+		return;
+	}
+
+	/* Pull DSA L2 data */
+	skb_pull(skb, MV88E6XXX_DSA_HLEN);
+
+	/* Make an copy for further processing in initiator */
+	chip->rmu.resp = skb_copy(skb, GFP_ATOMIC);
+	if (!chip->rmu.resp)
+		return;
I think this should be in the other order. First see if there is
anybody interested in the skb, then make a copy of it.
That's not what it's doing. It's a check that the skb_copy
finished correctly, not that anyone is interested in a reply.
Or did I misinterpret your comment?
quoted
+
+	dsa_switch_inband_complete(ds, NULL);
+}
+
+int mv88e6xxx_rmu_setup(struct mv88e6xxx_chip *chip)
+{
+	mutex_init(&chip->rmu.mutex);
+
+	/* Remember original ops for restore */
+	chip->rmu.smi_ops = chip->smi_ops;
+
+	/* Change rmu ops with our own pointer */
+	chip->rmu.rmu_ops = (struct mv88e6xxx_bus_ops *)chip->rmu.smi_ops;
Why do you need a cast? In general, casts are wrong, it suggests your
types are wrong.
quoted
+	chip->rmu.rmu_ops->get_rmon = mv88e6xxx_rmu_stats_get;
+
+	if (chip->info->ops->rmu_disable)
+		return chip->info->ops->rmu_disable(chip);
Why is a setup function calling disable?
So Vladimir Oltean commented before:
"I think it's very important for the RMU to still start as disabled.
You enable it dynamically when the master goes up."
    Andrew
  
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help