diff options
Diffstat (limited to 'net/dsa/dsa2.c')
| -rw-r--r-- | net/dsa/dsa2.c | 67 | 
1 files changed, 54 insertions, 13 deletions
diff --git a/net/dsa/dsa2.c b/net/dsa/dsa2.c index c66abbed4daf..e7c30b472034 100644 --- a/net/dsa/dsa2.c +++ b/net/dsa/dsa2.c @@ -614,6 +614,32 @@ static int dsa_port_parse_dsa(struct dsa_port *dp)  	return 0;  } +static enum dsa_tag_protocol dsa_get_tag_protocol(struct dsa_port *dp, +						  struct net_device *master) +{ +	enum dsa_tag_protocol tag_protocol = DSA_TAG_PROTO_NONE; +	struct dsa_switch *mds, *ds = dp->ds; +	unsigned int mdp_upstream; +	struct dsa_port *mdp; + +	/* It is possible to stack DSA switches onto one another when that +	 * happens the switch driver may want to know if its tagging protocol +	 * is going to work in such a configuration. +	 */ +	if (dsa_slave_dev_check(master)) { +		mdp = dsa_slave_to_port(master); +		mds = mdp->ds; +		mdp_upstream = dsa_upstream_port(mds, mdp->index); +		tag_protocol = mds->ops->get_tag_protocol(mds, mdp_upstream, +							  DSA_TAG_PROTO_NONE); +	} + +	/* If the master device is not itself a DSA slave in a disjoint DSA +	 * tree, then return immediately. +	 */ +	return ds->ops->get_tag_protocol(ds, dp->index, tag_protocol); +} +  static int dsa_port_parse_cpu(struct dsa_port *dp, struct net_device *master)  {  	struct dsa_switch *ds = dp->ds; @@ -621,20 +647,21 @@ static int dsa_port_parse_cpu(struct dsa_port *dp, struct net_device *master)  	const struct dsa_device_ops *tag_ops;  	enum dsa_tag_protocol tag_protocol; -	tag_protocol = ds->ops->get_tag_protocol(ds, dp->index); +	tag_protocol = dsa_get_tag_protocol(dp, master);  	tag_ops = dsa_tag_driver_get(tag_protocol);  	if (IS_ERR(tag_ops)) {  		if (PTR_ERR(tag_ops) == -ENOPROTOOPT)  			return -EPROBE_DEFER;  		dev_warn(ds->dev, "No tagger for this switch\n"); +		dp->master = NULL;  		return PTR_ERR(tag_ops);  	} +	dp->master = master;  	dp->type = DSA_PORT_TYPE_CPU;  	dp->filter = tag_ops->filter;  	dp->rcv = tag_ops->rcv;  	dp->tag_ops = tag_ops; -	dp->master = master;  	dp->dst = dst;  	return 0; @@ -822,6 +849,19 @@ static int dsa_switch_parse(struct dsa_switch *ds, struct dsa_chip_data *cd)  	return dsa_switch_parse_ports(ds, cd);  } +static void dsa_switch_release_ports(struct dsa_switch *ds) +{ +	struct dsa_switch_tree *dst = ds->dst; +	struct dsa_port *dp, *next; + +	list_for_each_entry_safe(dp, next, &dst->ports, list) { +		if (dp->ds != ds) +			continue; +		list_del(&dp->list); +		kfree(dp); +	} +} +  static int dsa_switch_probe(struct dsa_switch *ds)  {  	struct dsa_switch_tree *dst; @@ -838,12 +878,17 @@ static int dsa_switch_probe(struct dsa_switch *ds)  	if (!ds->num_ports)  		return -EINVAL; -	if (np) +	if (np) {  		err = dsa_switch_parse_of(ds, np); -	else if (pdata) +		if (err) +			dsa_switch_release_ports(ds); +	} else if (pdata) {  		err = dsa_switch_parse(ds, pdata); -	else +		if (err) +			dsa_switch_release_ports(ds); +	} else {  		err = -ENODEV; +	}  	if (err)  		return err; @@ -851,8 +896,10 @@ static int dsa_switch_probe(struct dsa_switch *ds)  	dst = ds->dst;  	dsa_tree_get(dst);  	err = dsa_tree_setup(dst); -	if (err) +	if (err) { +		dsa_switch_release_ports(ds);  		dsa_tree_put(dst); +	}  	return err;  } @@ -873,15 +920,9 @@ EXPORT_SYMBOL_GPL(dsa_register_switch);  static void dsa_switch_remove(struct dsa_switch *ds)  {  	struct dsa_switch_tree *dst = ds->dst; -	struct dsa_port *dp, *next;  	dsa_tree_teardown(dst); - -	list_for_each_entry_safe(dp, next, &dst->ports, list) { -		list_del(&dp->list); -		kfree(dp); -	} - +	dsa_switch_release_ports(ds);  	dsa_tree_put(dst);  }  |