Commit 2483223e authored by Jakub Kicinski's avatar Jakub Kicinski
Browse files

Merge branch 'devlink-sanitize-per-port-region-creation-destruction'

Jiri Pirko says:

====================
devlink: sanitize per-port region creation/destruction

Currently the only user of per-port devlink regions is DSA. All drivers
that use regions register them before devlink registration. For DSA,
this was not possible as the internals of struct devlink_port needed for
region creation are initialized during port registration.

This introduced a mismatch in creation flow of devlink and devlink port
regions. As you can see, it causes the DSA driver to make the port
init/exit flow a bit cumbersome.

Fix this by introducing port_init/fini() which could be optionally
called by drivers like DSA, to prepare struct devlink_port to be used
for region creation purposes before devlink port register is called.

Tested by Vladimir on his setup.
====================

Link: https://lore.kernel.org/r/20220929072902.2986539-1-jiri@resnulli.us


Signed-off-by: default avatarJakub Kicinski <kuba@kernel.org>
parents 3406079b 61e4a516
Loading
Loading
Loading
Loading
+6 −1
Original line number Diff line number Diff line
@@ -129,7 +129,9 @@ struct devlink_port {
	void *type_dev;
	struct devlink_port_attrs attrs;
	u8 attrs_set:1,
	   switch_port:1;
	   switch_port:1,
	   registered:1,
	   initialized:1;
	struct delayed_work type_warn_dw;
	struct list_head reporter_list;
	struct mutex reporters_lock; /* Protects reporter_list */
@@ -1562,6 +1564,9 @@ void devlink_set_features(struct devlink *devlink, u64 features);
void devlink_register(struct devlink *devlink);
void devlink_unregister(struct devlink *devlink);
void devlink_free(struct devlink *devlink);
void devlink_port_init(struct devlink *devlink,
		       struct devlink_port *devlink_port);
void devlink_port_fini(struct devlink_port *devlink_port);
int devl_port_register(struct devlink *devlink,
		       struct devlink_port *devlink_port,
		       unsigned int port_index);
+0 −2
Original line number Diff line number Diff line
@@ -294,8 +294,6 @@ struct dsa_port {

	u8			lag_tx_enabled:1;

	u8			devlink_port_setup:1;

	/* Master state bits, valid only on CPU ports */
	u8			master_admin_up:1;
	u8			master_oper_up:1;
+64 −16
Original line number Diff line number Diff line
@@ -371,6 +371,13 @@ static struct devlink *devlink_get_from_attrs(struct net *net,
	return ERR_PTR(-ENODEV);
}

#define ASSERT_DEVLINK_PORT_REGISTERED(devlink_port)				\
	WARN_ON_ONCE(!(devlink_port)->registered)
#define ASSERT_DEVLINK_PORT_NOT_REGISTERED(devlink_port)			\
	WARN_ON_ONCE((devlink_port)->registered)
#define ASSERT_DEVLINK_PORT_INITIALIZED(devlink_port)				\
	WARN_ON_ONCE(!(devlink_port)->initialized)

static struct devlink_port *devlink_port_get_by_index(struct devlink *devlink,
						      unsigned int port_index)
{
@@ -9847,6 +9854,44 @@ static void devlink_port_type_warn_cancel(struct devlink_port *devlink_port)
	cancel_delayed_work_sync(&devlink_port->type_warn_dw);
}

/**
 * devlink_port_init() - Init devlink port
 *
 * @devlink: devlink
 * @devlink_port: devlink port
 *
 * Initialize essencial stuff that is needed for functions
 * that may be called before devlink port registration.
 * Call to this function is optional and not needed
 * in case the driver does not use such functions.
 */
void devlink_port_init(struct devlink *devlink,
		       struct devlink_port *devlink_port)
{
	if (devlink_port->initialized)
		return;
	devlink_port->devlink = devlink;
	INIT_LIST_HEAD(&devlink_port->region_list);
	devlink_port->initialized = true;
}
EXPORT_SYMBOL_GPL(devlink_port_init);

/**
 * devlink_port_fini() - Deinitialize devlink port
 *
 * @devlink_port: devlink port
 *
 * Deinitialize essencial stuff that is in use for functions
 * that may be called after devlink port unregistration.
 * Call to this function is optional and not needed
 * in case the driver does not use such functions.
 */
void devlink_port_fini(struct devlink_port *devlink_port)
{
	WARN_ON(!list_empty(&devlink_port->region_list));
}
EXPORT_SYMBOL_GPL(devlink_port_fini);

/**
 * devl_port_register() - Register devlink port
 *
@@ -9869,14 +9914,15 @@ int devl_port_register(struct devlink *devlink,
	if (devlink_port_index_exists(devlink, port_index))
		return -EEXIST;

	WARN_ON(devlink_port->devlink);
	devlink_port->devlink = devlink;
	ASSERT_DEVLINK_PORT_NOT_REGISTERED(devlink_port);

	devlink_port_init(devlink, devlink_port);
	devlink_port->registered = true;
	devlink_port->index = port_index;
	spin_lock_init(&devlink_port->type_lock);
	INIT_LIST_HEAD(&devlink_port->reporter_list);
	mutex_init(&devlink_port->reporters_lock);
	list_add_tail(&devlink_port->list, &devlink->port_list);
	INIT_LIST_HEAD(&devlink_port->region_list);

	INIT_DELAYED_WORK(&devlink_port->type_warn_dw, &devlink_port_type_warn);
	devlink_port_type_warn_schedule(devlink_port);
@@ -9926,8 +9972,8 @@ void devl_port_unregister(struct devlink_port *devlink_port)
	devlink_port_notify(devlink_port, DEVLINK_CMD_PORT_DEL);
	list_del(&devlink_port->list);
	WARN_ON(!list_empty(&devlink_port->reporter_list));
	WARN_ON(!list_empty(&devlink_port->region_list));
	mutex_destroy(&devlink_port->reporters_lock);
	devlink_port->registered = false;
}
EXPORT_SYMBOL_GPL(devl_port_unregister);

@@ -9952,8 +9998,8 @@ static void __devlink_port_type_set(struct devlink_port *devlink_port,
				    enum devlink_port_type type,
				    void *type_dev)
{
	if (WARN_ON(!devlink_port->devlink))
		return;
	ASSERT_DEVLINK_PORT_REGISTERED(devlink_port);

	devlink_port_type_warn_cancel(devlink_port);
	spin_lock_bh(&devlink_port->type_lock);
	devlink_port->type = type;
@@ -10072,8 +10118,8 @@ void devlink_port_attrs_set(struct devlink_port *devlink_port,
{
	int ret;

	if (WARN_ON(devlink_port->devlink))
		return;
	ASSERT_DEVLINK_PORT_NOT_REGISTERED(devlink_port);

	devlink_port->attrs = *attrs;
	ret = __devlink_port_attrs_set(devlink_port, attrs->flavour);
	if (ret)
@@ -10096,8 +10142,8 @@ void devlink_port_attrs_pci_pf_set(struct devlink_port *devlink_port, u32 contro
	struct devlink_port_attrs *attrs = &devlink_port->attrs;
	int ret;

	if (WARN_ON(devlink_port->devlink))
		return;
	ASSERT_DEVLINK_PORT_NOT_REGISTERED(devlink_port);

	ret = __devlink_port_attrs_set(devlink_port,
				       DEVLINK_PORT_FLAVOUR_PCI_PF);
	if (ret)
@@ -10123,8 +10169,8 @@ void devlink_port_attrs_pci_vf_set(struct devlink_port *devlink_port, u32 contro
	struct devlink_port_attrs *attrs = &devlink_port->attrs;
	int ret;

	if (WARN_ON(devlink_port->devlink))
		return;
	ASSERT_DEVLINK_PORT_NOT_REGISTERED(devlink_port);

	ret = __devlink_port_attrs_set(devlink_port,
				       DEVLINK_PORT_FLAVOUR_PCI_VF);
	if (ret)
@@ -10151,8 +10197,8 @@ void devlink_port_attrs_pci_sf_set(struct devlink_port *devlink_port, u32 contro
	struct devlink_port_attrs *attrs = &devlink_port->attrs;
	int ret;

	if (WARN_ON(devlink_port->devlink))
		return;
	ASSERT_DEVLINK_PORT_NOT_REGISTERED(devlink_port);

	ret = __devlink_port_attrs_set(devlink_port,
				       DEVLINK_PORT_FLAVOUR_PCI_SF);
	if (ret)
@@ -10267,8 +10313,8 @@ EXPORT_SYMBOL_GPL(devl_rate_nodes_destroy);
void devlink_port_linecard_set(struct devlink_port *devlink_port,
			       struct devlink_linecard *linecard)
{
	if (WARN_ON(devlink_port->devlink))
		return;
	ASSERT_DEVLINK_PORT_NOT_REGISTERED(devlink_port);

	devlink_port->linecard = linecard;
}
EXPORT_SYMBOL_GPL(devlink_port_linecard_set);
@@ -11339,6 +11385,8 @@ devlink_port_region_create(struct devlink_port *port,
	struct devlink_region *region;
	int err = 0;

	ASSERT_DEVLINK_PORT_INITIALIZED(port);

	if (WARN_ON(!ops) || WARN_ON(!ops->destructor))
		return ERR_PTR(-EINVAL);

+77 −107
Original line number Diff line number Diff line
@@ -461,6 +461,72 @@ static void dsa_tree_teardown_cpu_ports(struct dsa_switch_tree *dst)
			dp->cpu_dp = NULL;
}

static int dsa_port_devlink_setup(struct dsa_port *dp)
{
	struct devlink_port *dlp = &dp->devlink_port;
	struct dsa_switch_tree *dst = dp->ds->dst;
	struct devlink_port_attrs attrs = {};
	struct devlink *dl = dp->ds->devlink;
	struct dsa_switch *ds = dp->ds;
	const unsigned char *id;
	unsigned char len;
	int err;

	memset(dlp, 0, sizeof(*dlp));
	devlink_port_init(dl, dlp);

	if (ds->ops->port_setup) {
		err = ds->ops->port_setup(ds, dp->index);
		if (err)
			return err;
	}

	id = (const unsigned char *)&dst->index;
	len = sizeof(dst->index);

	attrs.phys.port_number = dp->index;
	memcpy(attrs.switch_id.id, id, len);
	attrs.switch_id.id_len = len;

	switch (dp->type) {
	case DSA_PORT_TYPE_UNUSED:
		attrs.flavour = DEVLINK_PORT_FLAVOUR_UNUSED;
		break;
	case DSA_PORT_TYPE_CPU:
		attrs.flavour = DEVLINK_PORT_FLAVOUR_CPU;
		break;
	case DSA_PORT_TYPE_DSA:
		attrs.flavour = DEVLINK_PORT_FLAVOUR_DSA;
		break;
	case DSA_PORT_TYPE_USER:
		attrs.flavour = DEVLINK_PORT_FLAVOUR_PHYSICAL;
		break;
	}

	devlink_port_attrs_set(dlp, &attrs);
	err = devlink_port_register(dl, dlp, dp->index);
	if (err) {
		if (ds->ops->port_teardown)
			ds->ops->port_teardown(ds, dp->index);
		return err;
	}

	return 0;
}

static void dsa_port_devlink_teardown(struct dsa_port *dp)
{
	struct devlink_port *dlp = &dp->devlink_port;
	struct dsa_switch *ds = dp->ds;

	devlink_port_unregister(dlp);

	if (ds->ops->port_teardown)
		ds->ops->port_teardown(ds, dp->index);

	devlink_port_fini(dlp);
}

static int dsa_port_setup(struct dsa_port *dp)
{
	struct devlink_port *dlp = &dp->devlink_port;
@@ -472,11 +538,9 @@ static int dsa_port_setup(struct dsa_port *dp)
	if (dp->setup)
		return 0;

	if (ds->ops->port_setup) {
		err = ds->ops->port_setup(ds, dp->index);
	err = dsa_port_devlink_setup(dp);
	if (err)
		return err;
	}

	switch (dp->type) {
	case DSA_PORT_TYPE_UNUSED:
@@ -533,8 +597,7 @@ static int dsa_port_setup(struct dsa_port *dp)
	if (err && dsa_port_link_registered)
		dsa_shared_port_link_unregister_of(dp);
	if (err) {
		if (ds->ops->port_teardown)
			ds->ops->port_teardown(ds, dp->index);
		dsa_port_devlink_teardown(dp);
		return err;
	}

@@ -543,59 +606,13 @@ static int dsa_port_setup(struct dsa_port *dp)
	return 0;
}

static int dsa_port_devlink_setup(struct dsa_port *dp)
{
	struct devlink_port *dlp = &dp->devlink_port;
	struct dsa_switch_tree *dst = dp->ds->dst;
	struct devlink_port_attrs attrs = {};
	struct devlink *dl = dp->ds->devlink;
	const unsigned char *id;
	unsigned char len;
	int err;

	id = (const unsigned char *)&dst->index;
	len = sizeof(dst->index);

	attrs.phys.port_number = dp->index;
	memcpy(attrs.switch_id.id, id, len);
	attrs.switch_id.id_len = len;
	memset(dlp, 0, sizeof(*dlp));

	switch (dp->type) {
	case DSA_PORT_TYPE_UNUSED:
		attrs.flavour = DEVLINK_PORT_FLAVOUR_UNUSED;
		break;
	case DSA_PORT_TYPE_CPU:
		attrs.flavour = DEVLINK_PORT_FLAVOUR_CPU;
		break;
	case DSA_PORT_TYPE_DSA:
		attrs.flavour = DEVLINK_PORT_FLAVOUR_DSA;
		break;
	case DSA_PORT_TYPE_USER:
		attrs.flavour = DEVLINK_PORT_FLAVOUR_PHYSICAL;
		break;
	}

	devlink_port_attrs_set(dlp, &attrs);
	err = devlink_port_register(dl, dlp, dp->index);

	if (!err)
		dp->devlink_port_setup = true;

	return err;
}

static void dsa_port_teardown(struct dsa_port *dp)
{
	struct devlink_port *dlp = &dp->devlink_port;
	struct dsa_switch *ds = dp->ds;

	if (!dp->setup)
		return;

	if (ds->ops->port_teardown)
		ds->ops->port_teardown(ds, dp->index);

	devlink_port_type_clear(dlp);

	switch (dp->type) {
@@ -619,46 +636,15 @@ static void dsa_port_teardown(struct dsa_port *dp)
		break;
	}

	dp->setup = false;
}

static void dsa_port_devlink_teardown(struct dsa_port *dp)
{
	struct devlink_port *dlp = &dp->devlink_port;
	dsa_port_devlink_teardown(dp);

	if (dp->devlink_port_setup)
		devlink_port_unregister(dlp);
	dp->devlink_port_setup = false;
	dp->setup = false;
}

/* Destroy the current devlink port, and create a new one which has the UNUSED
 * flavour. At this point, any call to ds->ops->port_setup has been already
 * balanced out by a call to ds->ops->port_teardown, so we know that any
 * devlink port regions the driver had are now unregistered. We then call its
 * ds->ops->port_setup again, in order for the driver to re-create them on the
 * new devlink port.
 */
static int dsa_port_reinit_as_unused(struct dsa_port *dp)
static int dsa_port_setup_as_unused(struct dsa_port *dp)
{
	struct dsa_switch *ds = dp->ds;
	int err;

	dsa_port_devlink_teardown(dp);
	dp->type = DSA_PORT_TYPE_UNUSED;
	err = dsa_port_devlink_setup(dp);
	if (err)
		return err;

	if (ds->ops->port_setup) {
		/* On error, leave the devlink port registered,
		 * dsa_switch_teardown will clean it up later.
		 */
		err = ds->ops->port_setup(ds, dp->index);
		if (err)
			return err;
	}

	return 0;
	return dsa_port_setup(dp);
}

static int dsa_devlink_info_get(struct devlink *dl,
@@ -882,7 +868,6 @@ static int dsa_switch_setup(struct dsa_switch *ds)
{
	struct dsa_devlink_priv *dl_priv;
	struct device_node *dn;
	struct dsa_port *dp;
	int err;

	if (ds->setup)
@@ -905,18 +890,9 @@ static int dsa_switch_setup(struct dsa_switch *ds)
	dl_priv = devlink_priv(ds->devlink);
	dl_priv->ds = ds;

	/* Setup devlink port instances now, so that the switch
	 * setup() can register regions etc, against the ports
	 */
	dsa_switch_for_each_port(dp, ds) {
		err = dsa_port_devlink_setup(dp);
		if (err)
			goto unregister_devlink_ports;
	}

	err = dsa_switch_register_notifier(ds);
	if (err)
		goto unregister_devlink_ports;
		goto devlink_free;

	ds->configure_vlan_while_not_filtering = true;

@@ -957,9 +933,7 @@ static int dsa_switch_setup(struct dsa_switch *ds)
		ds->ops->teardown(ds);
unregister_notifier:
	dsa_switch_unregister_notifier(ds);
unregister_devlink_ports:
	dsa_switch_for_each_port(dp, ds)
		dsa_port_devlink_teardown(dp);
devlink_free:
	devlink_free(ds->devlink);
	ds->devlink = NULL;
	return err;
@@ -967,8 +941,6 @@ static int dsa_switch_setup(struct dsa_switch *ds)

static void dsa_switch_teardown(struct dsa_switch *ds)
{
	struct dsa_port *dp;

	if (!ds->setup)
		return;

@@ -987,8 +959,6 @@ static void dsa_switch_teardown(struct dsa_switch *ds)
	dsa_switch_unregister_notifier(ds);

	if (ds->devlink) {
		dsa_switch_for_each_port(dp, ds)
			dsa_port_devlink_teardown(dp);
		devlink_free(ds->devlink);
		ds->devlink = NULL;
	}
@@ -1041,7 +1011,7 @@ static int dsa_tree_setup_ports(struct dsa_switch_tree *dst)
		if (dsa_port_is_user(dp) || dsa_port_is_unused(dp)) {
			err = dsa_port_setup(dp);
			if (err) {
				err = dsa_port_reinit_as_unused(dp);
				err = dsa_port_setup_as_unused(dp);
				if (err)
					goto teardown;
			}
+1 −0
Original line number Diff line number Diff line
@@ -294,6 +294,7 @@ int dsa_port_mrp_add_ring_role(const struct dsa_port *dp,
int dsa_port_mrp_del_ring_role(const struct dsa_port *dp,
			       const struct switchdev_obj_ring_role_mrp *mrp);
int dsa_port_phylink_create(struct dsa_port *dp);
void dsa_port_phylink_destroy(struct dsa_port *dp);
int dsa_shared_port_link_register_of(struct dsa_port *dp);
void dsa_shared_port_link_unregister_of(struct dsa_port *dp);
int dsa_port_hsr_join(struct dsa_port *dp, struct net_device *hsr);
Loading