12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829 |
- // SPDX-License-Identifier: GPL-2.0-or-later
- /*
- * net/dsa/dsa2.c - Hardware switch handling, binding version 2
- * Copyright (c) 2008-2009 Marvell Semiconductor
- * Copyright (c) 2013 Florian Fainelli <[email protected]>
- * Copyright (c) 2016 Andrew Lunn <[email protected]>
- */
- #include <linux/device.h>
- #include <linux/err.h>
- #include <linux/list.h>
- #include <linux/netdevice.h>
- #include <linux/slab.h>
- #include <linux/rtnetlink.h>
- #include <linux/of.h>
- #include <linux/of_mdio.h>
- #include <linux/of_net.h>
- #include <net/devlink.h>
- #include <net/sch_generic.h>
- #include "dsa_priv.h"
- static DEFINE_MUTEX(dsa2_mutex);
- LIST_HEAD(dsa_tree_list);
- /* Track the bridges with forwarding offload enabled */
- static unsigned long dsa_fwd_offloading_bridges;
- /**
- * dsa_tree_notify - Execute code for all switches in a DSA switch tree.
- * @dst: collection of struct dsa_switch devices to notify.
- * @e: event, must be of type DSA_NOTIFIER_*
- * @v: event-specific value.
- *
- * Given a struct dsa_switch_tree, this can be used to run a function once for
- * each member DSA switch. The other alternative of traversing the tree is only
- * through its ports list, which does not uniquely list the switches.
- */
- int dsa_tree_notify(struct dsa_switch_tree *dst, unsigned long e, void *v)
- {
- struct raw_notifier_head *nh = &dst->nh;
- int err;
- err = raw_notifier_call_chain(nh, e, v);
- return notifier_to_errno(err);
- }
- /**
- * dsa_broadcast - Notify all DSA trees in the system.
- * @e: event, must be of type DSA_NOTIFIER_*
- * @v: event-specific value.
- *
- * Can be used to notify the switching fabric of events such as cross-chip
- * bridging between disjoint trees (such as islands of tagger-compatible
- * switches bridged by an incompatible middle switch).
- *
- * WARNING: this function is not reliable during probe time, because probing
- * between trees is asynchronous and not all DSA trees might have probed.
- */
- int dsa_broadcast(unsigned long e, void *v)
- {
- struct dsa_switch_tree *dst;
- int err = 0;
- list_for_each_entry(dst, &dsa_tree_list, list) {
- err = dsa_tree_notify(dst, e, v);
- if (err)
- break;
- }
- return err;
- }
- /**
- * dsa_lag_map() - Map LAG structure to a linear LAG array
- * @dst: Tree in which to record the mapping.
- * @lag: LAG structure that is to be mapped to the tree's array.
- *
- * dsa_lag_id/dsa_lag_by_id can then be used to translate between the
- * two spaces. The size of the mapping space is determined by the
- * driver by setting ds->num_lag_ids. It is perfectly legal to leave
- * it unset if it is not needed, in which case these functions become
- * no-ops.
- */
- void dsa_lag_map(struct dsa_switch_tree *dst, struct dsa_lag *lag)
- {
- unsigned int id;
- for (id = 1; id <= dst->lags_len; id++) {
- if (!dsa_lag_by_id(dst, id)) {
- dst->lags[id - 1] = lag;
- lag->id = id;
- return;
- }
- }
- /* No IDs left, which is OK. Some drivers do not need it. The
- * ones that do, e.g. mv88e6xxx, will discover that dsa_lag_id
- * returns an error for this device when joining the LAG. The
- * driver can then return -EOPNOTSUPP back to DSA, which will
- * fall back to a software LAG.
- */
- }
- /**
- * dsa_lag_unmap() - Remove a LAG ID mapping
- * @dst: Tree in which the mapping is recorded.
- * @lag: LAG structure that was mapped.
- *
- * As there may be multiple users of the mapping, it is only removed
- * if there are no other references to it.
- */
- void dsa_lag_unmap(struct dsa_switch_tree *dst, struct dsa_lag *lag)
- {
- unsigned int id;
- dsa_lags_foreach_id(id, dst) {
- if (dsa_lag_by_id(dst, id) == lag) {
- dst->lags[id - 1] = NULL;
- lag->id = 0;
- break;
- }
- }
- }
- struct dsa_lag *dsa_tree_lag_find(struct dsa_switch_tree *dst,
- const struct net_device *lag_dev)
- {
- struct dsa_port *dp;
- list_for_each_entry(dp, &dst->ports, list)
- if (dsa_port_lag_dev_get(dp) == lag_dev)
- return dp->lag;
- return NULL;
- }
- struct dsa_bridge *dsa_tree_bridge_find(struct dsa_switch_tree *dst,
- const struct net_device *br)
- {
- struct dsa_port *dp;
- list_for_each_entry(dp, &dst->ports, list)
- if (dsa_port_bridge_dev_get(dp) == br)
- return dp->bridge;
- return NULL;
- }
- static int dsa_bridge_num_find(const struct net_device *bridge_dev)
- {
- struct dsa_switch_tree *dst;
- list_for_each_entry(dst, &dsa_tree_list, list) {
- struct dsa_bridge *bridge;
- bridge = dsa_tree_bridge_find(dst, bridge_dev);
- if (bridge)
- return bridge->num;
- }
- return 0;
- }
- unsigned int dsa_bridge_num_get(const struct net_device *bridge_dev, int max)
- {
- unsigned int bridge_num = dsa_bridge_num_find(bridge_dev);
- /* Switches without FDB isolation support don't get unique
- * bridge numbering
- */
- if (!max)
- return 0;
- if (!bridge_num) {
- /* First port that requests FDB isolation or TX forwarding
- * offload for this bridge
- */
- bridge_num = find_next_zero_bit(&dsa_fwd_offloading_bridges,
- DSA_MAX_NUM_OFFLOADING_BRIDGES,
- 1);
- if (bridge_num >= max)
- return 0;
- set_bit(bridge_num, &dsa_fwd_offloading_bridges);
- }
- return bridge_num;
- }
- void dsa_bridge_num_put(const struct net_device *bridge_dev,
- unsigned int bridge_num)
- {
- /* Since we refcount bridges, we know that when we call this function
- * it is no longer in use, so we can just go ahead and remove it from
- * the bit mask.
- */
- clear_bit(bridge_num, &dsa_fwd_offloading_bridges);
- }
- struct dsa_switch *dsa_switch_find(int tree_index, int sw_index)
- {
- struct dsa_switch_tree *dst;
- struct dsa_port *dp;
- list_for_each_entry(dst, &dsa_tree_list, list) {
- if (dst->index != tree_index)
- continue;
- list_for_each_entry(dp, &dst->ports, list) {
- if (dp->ds->index != sw_index)
- continue;
- return dp->ds;
- }
- }
- return NULL;
- }
- EXPORT_SYMBOL_GPL(dsa_switch_find);
- static struct dsa_switch_tree *dsa_tree_find(int index)
- {
- struct dsa_switch_tree *dst;
- list_for_each_entry(dst, &dsa_tree_list, list)
- if (dst->index == index)
- return dst;
- return NULL;
- }
- static struct dsa_switch_tree *dsa_tree_alloc(int index)
- {
- struct dsa_switch_tree *dst;
- dst = kzalloc(sizeof(*dst), GFP_KERNEL);
- if (!dst)
- return NULL;
- dst->index = index;
- INIT_LIST_HEAD(&dst->rtable);
- INIT_LIST_HEAD(&dst->ports);
- INIT_LIST_HEAD(&dst->list);
- list_add_tail(&dst->list, &dsa_tree_list);
- kref_init(&dst->refcount);
- return dst;
- }
- static void dsa_tree_free(struct dsa_switch_tree *dst)
- {
- if (dst->tag_ops)
- dsa_tag_driver_put(dst->tag_ops);
- list_del(&dst->list);
- kfree(dst);
- }
- static struct dsa_switch_tree *dsa_tree_get(struct dsa_switch_tree *dst)
- {
- if (dst)
- kref_get(&dst->refcount);
- return dst;
- }
- static struct dsa_switch_tree *dsa_tree_touch(int index)
- {
- struct dsa_switch_tree *dst;
- dst = dsa_tree_find(index);
- if (dst)
- return dsa_tree_get(dst);
- else
- return dsa_tree_alloc(index);
- }
- static void dsa_tree_release(struct kref *ref)
- {
- struct dsa_switch_tree *dst;
- dst = container_of(ref, struct dsa_switch_tree, refcount);
- dsa_tree_free(dst);
- }
- static void dsa_tree_put(struct dsa_switch_tree *dst)
- {
- if (dst)
- kref_put(&dst->refcount, dsa_tree_release);
- }
- static struct dsa_port *dsa_tree_find_port_by_node(struct dsa_switch_tree *dst,
- struct device_node *dn)
- {
- struct dsa_port *dp;
- list_for_each_entry(dp, &dst->ports, list)
- if (dp->dn == dn)
- return dp;
- return NULL;
- }
- static struct dsa_link *dsa_link_touch(struct dsa_port *dp,
- struct dsa_port *link_dp)
- {
- struct dsa_switch *ds = dp->ds;
- struct dsa_switch_tree *dst;
- struct dsa_link *dl;
- dst = ds->dst;
- list_for_each_entry(dl, &dst->rtable, list)
- if (dl->dp == dp && dl->link_dp == link_dp)
- return dl;
- dl = kzalloc(sizeof(*dl), GFP_KERNEL);
- if (!dl)
- return NULL;
- dl->dp = dp;
- dl->link_dp = link_dp;
- INIT_LIST_HEAD(&dl->list);
- list_add_tail(&dl->list, &dst->rtable);
- return dl;
- }
- static bool dsa_port_setup_routing_table(struct dsa_port *dp)
- {
- struct dsa_switch *ds = dp->ds;
- struct dsa_switch_tree *dst = ds->dst;
- struct device_node *dn = dp->dn;
- struct of_phandle_iterator it;
- struct dsa_port *link_dp;
- struct dsa_link *dl;
- int err;
- of_for_each_phandle(&it, err, dn, "link", NULL, 0) {
- link_dp = dsa_tree_find_port_by_node(dst, it.node);
- if (!link_dp) {
- of_node_put(it.node);
- return false;
- }
- dl = dsa_link_touch(dp, link_dp);
- if (!dl) {
- of_node_put(it.node);
- return false;
- }
- }
- return true;
- }
- static bool dsa_tree_setup_routing_table(struct dsa_switch_tree *dst)
- {
- bool complete = true;
- struct dsa_port *dp;
- list_for_each_entry(dp, &dst->ports, list) {
- if (dsa_port_is_dsa(dp)) {
- complete = dsa_port_setup_routing_table(dp);
- if (!complete)
- break;
- }
- }
- return complete;
- }
- static struct dsa_port *dsa_tree_find_first_cpu(struct dsa_switch_tree *dst)
- {
- struct dsa_port *dp;
- list_for_each_entry(dp, &dst->ports, list)
- if (dsa_port_is_cpu(dp))
- return dp;
- return NULL;
- }
- struct net_device *dsa_tree_find_first_master(struct dsa_switch_tree *dst)
- {
- struct device_node *ethernet;
- struct net_device *master;
- struct dsa_port *cpu_dp;
- cpu_dp = dsa_tree_find_first_cpu(dst);
- ethernet = of_parse_phandle(cpu_dp->dn, "ethernet", 0);
- master = of_find_net_device_by_node(ethernet);
- of_node_put(ethernet);
- return master;
- }
- /* Assign the default CPU port (the first one in the tree) to all ports of the
- * fabric which don't already have one as part of their own switch.
- */
- static int dsa_tree_setup_default_cpu(struct dsa_switch_tree *dst)
- {
- struct dsa_port *cpu_dp, *dp;
- cpu_dp = dsa_tree_find_first_cpu(dst);
- if (!cpu_dp) {
- pr_err("DSA: tree %d has no CPU port\n", dst->index);
- return -EINVAL;
- }
- list_for_each_entry(dp, &dst->ports, list) {
- if (dp->cpu_dp)
- continue;
- if (dsa_port_is_user(dp) || dsa_port_is_dsa(dp))
- dp->cpu_dp = cpu_dp;
- }
- return 0;
- }
- /* Perform initial assignment of CPU ports to user ports and DSA links in the
- * fabric, giving preference to CPU ports local to each switch. Default to
- * using the first CPU port in the switch tree if the port does not have a CPU
- * port local to this switch.
- */
- static int dsa_tree_setup_cpu_ports(struct dsa_switch_tree *dst)
- {
- struct dsa_port *cpu_dp, *dp;
- list_for_each_entry(cpu_dp, &dst->ports, list) {
- if (!dsa_port_is_cpu(cpu_dp))
- continue;
- /* Prefer a local CPU port */
- dsa_switch_for_each_port(dp, cpu_dp->ds) {
- /* Prefer the first local CPU port found */
- if (dp->cpu_dp)
- continue;
- if (dsa_port_is_user(dp) || dsa_port_is_dsa(dp))
- dp->cpu_dp = cpu_dp;
- }
- }
- return dsa_tree_setup_default_cpu(dst);
- }
- static void dsa_tree_teardown_cpu_ports(struct dsa_switch_tree *dst)
- {
- struct dsa_port *dp;
- list_for_each_entry(dp, &dst->ports, list)
- if (dsa_port_is_user(dp) || dsa_port_is_dsa(dp))
- 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;
- bool dsa_port_link_registered = false;
- struct dsa_switch *ds = dp->ds;
- bool dsa_port_enabled = false;
- int err = 0;
- if (dp->setup)
- return 0;
- err = dsa_port_devlink_setup(dp);
- if (err)
- return err;
- switch (dp->type) {
- case DSA_PORT_TYPE_UNUSED:
- dsa_port_disable(dp);
- break;
- case DSA_PORT_TYPE_CPU:
- if (dp->dn) {
- err = dsa_shared_port_link_register_of(dp);
- if (err)
- break;
- dsa_port_link_registered = true;
- } else {
- dev_warn(ds->dev,
- "skipping link registration for CPU port %d\n",
- dp->index);
- }
- err = dsa_port_enable(dp, NULL);
- if (err)
- break;
- dsa_port_enabled = true;
- break;
- case DSA_PORT_TYPE_DSA:
- if (dp->dn) {
- err = dsa_shared_port_link_register_of(dp);
- if (err)
- break;
- dsa_port_link_registered = true;
- } else {
- dev_warn(ds->dev,
- "skipping link registration for DSA port %d\n",
- dp->index);
- }
- err = dsa_port_enable(dp, NULL);
- if (err)
- break;
- dsa_port_enabled = true;
- break;
- case DSA_PORT_TYPE_USER:
- of_get_mac_address(dp->dn, dp->mac);
- err = dsa_slave_create(dp);
- if (err)
- break;
- devlink_port_type_eth_set(dlp, dp->slave);
- break;
- }
- if (err && dsa_port_enabled)
- dsa_port_disable(dp);
- if (err && dsa_port_link_registered)
- dsa_shared_port_link_unregister_of(dp);
- if (err) {
- dsa_port_devlink_teardown(dp);
- return err;
- }
- dp->setup = true;
- return 0;
- }
- static void dsa_port_teardown(struct dsa_port *dp)
- {
- struct devlink_port *dlp = &dp->devlink_port;
- if (!dp->setup)
- return;
- devlink_port_type_clear(dlp);
- switch (dp->type) {
- case DSA_PORT_TYPE_UNUSED:
- break;
- case DSA_PORT_TYPE_CPU:
- dsa_port_disable(dp);
- if (dp->dn)
- dsa_shared_port_link_unregister_of(dp);
- break;
- case DSA_PORT_TYPE_DSA:
- dsa_port_disable(dp);
- if (dp->dn)
- dsa_shared_port_link_unregister_of(dp);
- break;
- case DSA_PORT_TYPE_USER:
- if (dp->slave) {
- dsa_slave_destroy(dp->slave);
- dp->slave = NULL;
- }
- break;
- }
- dsa_port_devlink_teardown(dp);
- dp->setup = false;
- }
- static int dsa_port_setup_as_unused(struct dsa_port *dp)
- {
- dp->type = DSA_PORT_TYPE_UNUSED;
- return dsa_port_setup(dp);
- }
- static int dsa_devlink_info_get(struct devlink *dl,
- struct devlink_info_req *req,
- struct netlink_ext_ack *extack)
- {
- struct dsa_switch *ds = dsa_devlink_to_ds(dl);
- if (ds->ops->devlink_info_get)
- return ds->ops->devlink_info_get(ds, req, extack);
- return -EOPNOTSUPP;
- }
- static int dsa_devlink_sb_pool_get(struct devlink *dl,
- unsigned int sb_index, u16 pool_index,
- struct devlink_sb_pool_info *pool_info)
- {
- struct dsa_switch *ds = dsa_devlink_to_ds(dl);
- if (!ds->ops->devlink_sb_pool_get)
- return -EOPNOTSUPP;
- return ds->ops->devlink_sb_pool_get(ds, sb_index, pool_index,
- pool_info);
- }
- static int dsa_devlink_sb_pool_set(struct devlink *dl, unsigned int sb_index,
- u16 pool_index, u32 size,
- enum devlink_sb_threshold_type threshold_type,
- struct netlink_ext_ack *extack)
- {
- struct dsa_switch *ds = dsa_devlink_to_ds(dl);
- if (!ds->ops->devlink_sb_pool_set)
- return -EOPNOTSUPP;
- return ds->ops->devlink_sb_pool_set(ds, sb_index, pool_index, size,
- threshold_type, extack);
- }
- static int dsa_devlink_sb_port_pool_get(struct devlink_port *dlp,
- unsigned int sb_index, u16 pool_index,
- u32 *p_threshold)
- {
- struct dsa_switch *ds = dsa_devlink_port_to_ds(dlp);
- int port = dsa_devlink_port_to_port(dlp);
- if (!ds->ops->devlink_sb_port_pool_get)
- return -EOPNOTSUPP;
- return ds->ops->devlink_sb_port_pool_get(ds, port, sb_index,
- pool_index, p_threshold);
- }
- static int dsa_devlink_sb_port_pool_set(struct devlink_port *dlp,
- unsigned int sb_index, u16 pool_index,
- u32 threshold,
- struct netlink_ext_ack *extack)
- {
- struct dsa_switch *ds = dsa_devlink_port_to_ds(dlp);
- int port = dsa_devlink_port_to_port(dlp);
- if (!ds->ops->devlink_sb_port_pool_set)
- return -EOPNOTSUPP;
- return ds->ops->devlink_sb_port_pool_set(ds, port, sb_index,
- pool_index, threshold, extack);
- }
- static int
- dsa_devlink_sb_tc_pool_bind_get(struct devlink_port *dlp,
- unsigned int sb_index, u16 tc_index,
- enum devlink_sb_pool_type pool_type,
- u16 *p_pool_index, u32 *p_threshold)
- {
- struct dsa_switch *ds = dsa_devlink_port_to_ds(dlp);
- int port = dsa_devlink_port_to_port(dlp);
- if (!ds->ops->devlink_sb_tc_pool_bind_get)
- return -EOPNOTSUPP;
- return ds->ops->devlink_sb_tc_pool_bind_get(ds, port, sb_index,
- tc_index, pool_type,
- p_pool_index, p_threshold);
- }
- static int
- dsa_devlink_sb_tc_pool_bind_set(struct devlink_port *dlp,
- unsigned int sb_index, u16 tc_index,
- enum devlink_sb_pool_type pool_type,
- u16 pool_index, u32 threshold,
- struct netlink_ext_ack *extack)
- {
- struct dsa_switch *ds = dsa_devlink_port_to_ds(dlp);
- int port = dsa_devlink_port_to_port(dlp);
- if (!ds->ops->devlink_sb_tc_pool_bind_set)
- return -EOPNOTSUPP;
- return ds->ops->devlink_sb_tc_pool_bind_set(ds, port, sb_index,
- tc_index, pool_type,
- pool_index, threshold,
- extack);
- }
- static int dsa_devlink_sb_occ_snapshot(struct devlink *dl,
- unsigned int sb_index)
- {
- struct dsa_switch *ds = dsa_devlink_to_ds(dl);
- if (!ds->ops->devlink_sb_occ_snapshot)
- return -EOPNOTSUPP;
- return ds->ops->devlink_sb_occ_snapshot(ds, sb_index);
- }
- static int dsa_devlink_sb_occ_max_clear(struct devlink *dl,
- unsigned int sb_index)
- {
- struct dsa_switch *ds = dsa_devlink_to_ds(dl);
- if (!ds->ops->devlink_sb_occ_max_clear)
- return -EOPNOTSUPP;
- return ds->ops->devlink_sb_occ_max_clear(ds, sb_index);
- }
- static int dsa_devlink_sb_occ_port_pool_get(struct devlink_port *dlp,
- unsigned int sb_index,
- u16 pool_index, u32 *p_cur,
- u32 *p_max)
- {
- struct dsa_switch *ds = dsa_devlink_port_to_ds(dlp);
- int port = dsa_devlink_port_to_port(dlp);
- if (!ds->ops->devlink_sb_occ_port_pool_get)
- return -EOPNOTSUPP;
- return ds->ops->devlink_sb_occ_port_pool_get(ds, port, sb_index,
- pool_index, p_cur, p_max);
- }
- static int
- dsa_devlink_sb_occ_tc_port_bind_get(struct devlink_port *dlp,
- unsigned int sb_index, u16 tc_index,
- enum devlink_sb_pool_type pool_type,
- u32 *p_cur, u32 *p_max)
- {
- struct dsa_switch *ds = dsa_devlink_port_to_ds(dlp);
- int port = dsa_devlink_port_to_port(dlp);
- if (!ds->ops->devlink_sb_occ_tc_port_bind_get)
- return -EOPNOTSUPP;
- return ds->ops->devlink_sb_occ_tc_port_bind_get(ds, port,
- sb_index, tc_index,
- pool_type, p_cur,
- p_max);
- }
- static const struct devlink_ops dsa_devlink_ops = {
- .info_get = dsa_devlink_info_get,
- .sb_pool_get = dsa_devlink_sb_pool_get,
- .sb_pool_set = dsa_devlink_sb_pool_set,
- .sb_port_pool_get = dsa_devlink_sb_port_pool_get,
- .sb_port_pool_set = dsa_devlink_sb_port_pool_set,
- .sb_tc_pool_bind_get = dsa_devlink_sb_tc_pool_bind_get,
- .sb_tc_pool_bind_set = dsa_devlink_sb_tc_pool_bind_set,
- .sb_occ_snapshot = dsa_devlink_sb_occ_snapshot,
- .sb_occ_max_clear = dsa_devlink_sb_occ_max_clear,
- .sb_occ_port_pool_get = dsa_devlink_sb_occ_port_pool_get,
- .sb_occ_tc_port_bind_get = dsa_devlink_sb_occ_tc_port_bind_get,
- };
- static int dsa_switch_setup_tag_protocol(struct dsa_switch *ds)
- {
- const struct dsa_device_ops *tag_ops = ds->dst->tag_ops;
- struct dsa_switch_tree *dst = ds->dst;
- int err;
- if (tag_ops->proto == dst->default_proto)
- goto connect;
- rtnl_lock();
- err = ds->ops->change_tag_protocol(ds, tag_ops->proto);
- rtnl_unlock();
- if (err) {
- dev_err(ds->dev, "Unable to use tag protocol \"%s\": %pe\n",
- tag_ops->name, ERR_PTR(err));
- return err;
- }
- connect:
- if (tag_ops->connect) {
- err = tag_ops->connect(ds);
- if (err)
- return err;
- }
- if (ds->ops->connect_tag_protocol) {
- err = ds->ops->connect_tag_protocol(ds, tag_ops->proto);
- if (err) {
- dev_err(ds->dev,
- "Unable to connect to tag protocol \"%s\": %pe\n",
- tag_ops->name, ERR_PTR(err));
- goto disconnect;
- }
- }
- return 0;
- disconnect:
- if (tag_ops->disconnect)
- tag_ops->disconnect(ds);
- return err;
- }
- static void dsa_switch_teardown_tag_protocol(struct dsa_switch *ds)
- {
- const struct dsa_device_ops *tag_ops = ds->dst->tag_ops;
- if (tag_ops->disconnect)
- tag_ops->disconnect(ds);
- }
- static int dsa_switch_setup(struct dsa_switch *ds)
- {
- struct dsa_devlink_priv *dl_priv;
- struct device_node *dn;
- int err;
- if (ds->setup)
- return 0;
- /* Initialize ds->phys_mii_mask before registering the slave MDIO bus
- * driver and before ops->setup() has run, since the switch drivers and
- * the slave MDIO bus driver rely on these values for probing PHY
- * devices or not
- */
- ds->phys_mii_mask |= dsa_user_ports(ds);
- /* Add the switch to devlink before calling setup, so that setup can
- * add dpipe tables
- */
- ds->devlink =
- devlink_alloc(&dsa_devlink_ops, sizeof(*dl_priv), ds->dev);
- if (!ds->devlink)
- return -ENOMEM;
- dl_priv = devlink_priv(ds->devlink);
- dl_priv->ds = ds;
- err = dsa_switch_register_notifier(ds);
- if (err)
- goto devlink_free;
- ds->configure_vlan_while_not_filtering = true;
- err = ds->ops->setup(ds);
- if (err < 0)
- goto unregister_notifier;
- err = dsa_switch_setup_tag_protocol(ds);
- if (err)
- goto teardown;
- if (!ds->slave_mii_bus && ds->ops->phy_read) {
- ds->slave_mii_bus = mdiobus_alloc();
- if (!ds->slave_mii_bus) {
- err = -ENOMEM;
- goto teardown;
- }
- dsa_slave_mii_bus_init(ds);
- dn = of_get_child_by_name(ds->dev->of_node, "mdio");
- err = of_mdiobus_register(ds->slave_mii_bus, dn);
- of_node_put(dn);
- if (err < 0)
- goto free_slave_mii_bus;
- }
- ds->setup = true;
- devlink_register(ds->devlink);
- return 0;
- free_slave_mii_bus:
- if (ds->slave_mii_bus && ds->ops->phy_read)
- mdiobus_free(ds->slave_mii_bus);
- teardown:
- if (ds->ops->teardown)
- ds->ops->teardown(ds);
- unregister_notifier:
- dsa_switch_unregister_notifier(ds);
- devlink_free:
- devlink_free(ds->devlink);
- ds->devlink = NULL;
- return err;
- }
- static void dsa_switch_teardown(struct dsa_switch *ds)
- {
- if (!ds->setup)
- return;
- if (ds->devlink)
- devlink_unregister(ds->devlink);
- if (ds->slave_mii_bus && ds->ops->phy_read) {
- mdiobus_unregister(ds->slave_mii_bus);
- mdiobus_free(ds->slave_mii_bus);
- ds->slave_mii_bus = NULL;
- }
- dsa_switch_teardown_tag_protocol(ds);
- if (ds->ops->teardown)
- ds->ops->teardown(ds);
- dsa_switch_unregister_notifier(ds);
- if (ds->devlink) {
- devlink_free(ds->devlink);
- ds->devlink = NULL;
- }
- ds->setup = false;
- }
- /* First tear down the non-shared, then the shared ports. This ensures that
- * all work items scheduled by our switchdev handlers for user ports have
- * completed before we destroy the refcounting kept on the shared ports.
- */
- static void dsa_tree_teardown_ports(struct dsa_switch_tree *dst)
- {
- struct dsa_port *dp;
- list_for_each_entry(dp, &dst->ports, list)
- if (dsa_port_is_user(dp) || dsa_port_is_unused(dp))
- dsa_port_teardown(dp);
- dsa_flush_workqueue();
- list_for_each_entry(dp, &dst->ports, list)
- if (dsa_port_is_dsa(dp) || dsa_port_is_cpu(dp))
- dsa_port_teardown(dp);
- }
- static void dsa_tree_teardown_switches(struct dsa_switch_tree *dst)
- {
- struct dsa_port *dp;
- list_for_each_entry(dp, &dst->ports, list)
- dsa_switch_teardown(dp->ds);
- }
- /* Bring shared ports up first, then non-shared ports */
- static int dsa_tree_setup_ports(struct dsa_switch_tree *dst)
- {
- struct dsa_port *dp;
- int err = 0;
- list_for_each_entry(dp, &dst->ports, list) {
- if (dsa_port_is_dsa(dp) || dsa_port_is_cpu(dp)) {
- err = dsa_port_setup(dp);
- if (err)
- goto teardown;
- }
- }
- list_for_each_entry(dp, &dst->ports, list) {
- if (dsa_port_is_user(dp) || dsa_port_is_unused(dp)) {
- err = dsa_port_setup(dp);
- if (err) {
- err = dsa_port_setup_as_unused(dp);
- if (err)
- goto teardown;
- }
- }
- }
- return 0;
- teardown:
- dsa_tree_teardown_ports(dst);
- return err;
- }
- static int dsa_tree_setup_switches(struct dsa_switch_tree *dst)
- {
- struct dsa_port *dp;
- int err = 0;
- list_for_each_entry(dp, &dst->ports, list) {
- err = dsa_switch_setup(dp->ds);
- if (err) {
- dsa_tree_teardown_switches(dst);
- break;
- }
- }
- return err;
- }
- static int dsa_tree_setup_master(struct dsa_switch_tree *dst)
- {
- struct dsa_port *cpu_dp;
- int err = 0;
- rtnl_lock();
- dsa_tree_for_each_cpu_port(cpu_dp, dst) {
- struct net_device *master = cpu_dp->master;
- bool admin_up = (master->flags & IFF_UP) &&
- !qdisc_tx_is_noop(master);
- err = dsa_master_setup(master, cpu_dp);
- if (err)
- break;
- /* Replay master state event */
- dsa_tree_master_admin_state_change(dst, master, admin_up);
- dsa_tree_master_oper_state_change(dst, master,
- netif_oper_up(master));
- }
- rtnl_unlock();
- return err;
- }
- static void dsa_tree_teardown_master(struct dsa_switch_tree *dst)
- {
- struct dsa_port *cpu_dp;
- rtnl_lock();
- dsa_tree_for_each_cpu_port(cpu_dp, dst) {
- struct net_device *master = cpu_dp->master;
- /* Synthesizing an "admin down" state is sufficient for
- * the switches to get a notification if the master is
- * currently up and running.
- */
- dsa_tree_master_admin_state_change(dst, master, false);
- dsa_master_teardown(master);
- }
- rtnl_unlock();
- }
- static int dsa_tree_setup_lags(struct dsa_switch_tree *dst)
- {
- unsigned int len = 0;
- struct dsa_port *dp;
- list_for_each_entry(dp, &dst->ports, list) {
- if (dp->ds->num_lag_ids > len)
- len = dp->ds->num_lag_ids;
- }
- if (!len)
- return 0;
- dst->lags = kcalloc(len, sizeof(*dst->lags), GFP_KERNEL);
- if (!dst->lags)
- return -ENOMEM;
- dst->lags_len = len;
- return 0;
- }
- static void dsa_tree_teardown_lags(struct dsa_switch_tree *dst)
- {
- kfree(dst->lags);
- }
- static int dsa_tree_setup(struct dsa_switch_tree *dst)
- {
- bool complete;
- int err;
- if (dst->setup) {
- pr_err("DSA: tree %d already setup! Disjoint trees?\n",
- dst->index);
- return -EEXIST;
- }
- complete = dsa_tree_setup_routing_table(dst);
- if (!complete)
- return 0;
- err = dsa_tree_setup_cpu_ports(dst);
- if (err)
- return err;
- err = dsa_tree_setup_switches(dst);
- if (err)
- goto teardown_cpu_ports;
- err = dsa_tree_setup_ports(dst);
- if (err)
- goto teardown_switches;
- err = dsa_tree_setup_master(dst);
- if (err)
- goto teardown_ports;
- err = dsa_tree_setup_lags(dst);
- if (err)
- goto teardown_master;
- dst->setup = true;
- pr_info("DSA: tree %d setup\n", dst->index);
- return 0;
- teardown_master:
- dsa_tree_teardown_master(dst);
- teardown_ports:
- dsa_tree_teardown_ports(dst);
- teardown_switches:
- dsa_tree_teardown_switches(dst);
- teardown_cpu_ports:
- dsa_tree_teardown_cpu_ports(dst);
- return err;
- }
- static void dsa_tree_teardown(struct dsa_switch_tree *dst)
- {
- struct dsa_link *dl, *next;
- if (!dst->setup)
- return;
- dsa_tree_teardown_lags(dst);
- dsa_tree_teardown_master(dst);
- dsa_tree_teardown_ports(dst);
- dsa_tree_teardown_switches(dst);
- dsa_tree_teardown_cpu_ports(dst);
- list_for_each_entry_safe(dl, next, &dst->rtable, list) {
- list_del(&dl->list);
- kfree(dl);
- }
- pr_info("DSA: tree %d torn down\n", dst->index);
- dst->setup = false;
- }
- static int dsa_tree_bind_tag_proto(struct dsa_switch_tree *dst,
- const struct dsa_device_ops *tag_ops)
- {
- const struct dsa_device_ops *old_tag_ops = dst->tag_ops;
- struct dsa_notifier_tag_proto_info info;
- int err;
- dst->tag_ops = tag_ops;
- /* Notify the switches from this tree about the connection
- * to the new tagger
- */
- info.tag_ops = tag_ops;
- err = dsa_tree_notify(dst, DSA_NOTIFIER_TAG_PROTO_CONNECT, &info);
- if (err && err != -EOPNOTSUPP)
- goto out_disconnect;
- /* Notify the old tagger about the disconnection from this tree */
- info.tag_ops = old_tag_ops;
- dsa_tree_notify(dst, DSA_NOTIFIER_TAG_PROTO_DISCONNECT, &info);
- return 0;
- out_disconnect:
- info.tag_ops = tag_ops;
- dsa_tree_notify(dst, DSA_NOTIFIER_TAG_PROTO_DISCONNECT, &info);
- dst->tag_ops = old_tag_ops;
- return err;
- }
- /* Since the dsa/tagging sysfs device attribute is per master, the assumption
- * is that all DSA switches within a tree share the same tagger, otherwise
- * they would have formed disjoint trees (different "dsa,member" values).
- */
- int dsa_tree_change_tag_proto(struct dsa_switch_tree *dst,
- const struct dsa_device_ops *tag_ops,
- const struct dsa_device_ops *old_tag_ops)
- {
- struct dsa_notifier_tag_proto_info info;
- struct dsa_port *dp;
- int err = -EBUSY;
- if (!rtnl_trylock())
- return restart_syscall();
- /* At the moment we don't allow changing the tag protocol under
- * traffic. The rtnl_mutex also happens to serialize concurrent
- * attempts to change the tagging protocol. If we ever lift the IFF_UP
- * restriction, there needs to be another mutex which serializes this.
- */
- dsa_tree_for_each_user_port(dp, dst) {
- if (dsa_port_to_master(dp)->flags & IFF_UP)
- goto out_unlock;
- if (dp->slave->flags & IFF_UP)
- goto out_unlock;
- }
- /* Notify the tag protocol change */
- info.tag_ops = tag_ops;
- err = dsa_tree_notify(dst, DSA_NOTIFIER_TAG_PROTO, &info);
- if (err)
- goto out_unwind_tagger;
- err = dsa_tree_bind_tag_proto(dst, tag_ops);
- if (err)
- goto out_unwind_tagger;
- rtnl_unlock();
- return 0;
- out_unwind_tagger:
- info.tag_ops = old_tag_ops;
- dsa_tree_notify(dst, DSA_NOTIFIER_TAG_PROTO, &info);
- out_unlock:
- rtnl_unlock();
- return err;
- }
- static void dsa_tree_master_state_change(struct dsa_switch_tree *dst,
- struct net_device *master)
- {
- struct dsa_notifier_master_state_info info;
- struct dsa_port *cpu_dp = master->dsa_ptr;
- info.master = master;
- info.operational = dsa_port_master_is_operational(cpu_dp);
- dsa_tree_notify(dst, DSA_NOTIFIER_MASTER_STATE_CHANGE, &info);
- }
- void dsa_tree_master_admin_state_change(struct dsa_switch_tree *dst,
- struct net_device *master,
- bool up)
- {
- struct dsa_port *cpu_dp = master->dsa_ptr;
- bool notify = false;
- /* Don't keep track of admin state on LAG DSA masters,
- * but rather just of physical DSA masters
- */
- if (netif_is_lag_master(master))
- return;
- if ((dsa_port_master_is_operational(cpu_dp)) !=
- (up && cpu_dp->master_oper_up))
- notify = true;
- cpu_dp->master_admin_up = up;
- if (notify)
- dsa_tree_master_state_change(dst, master);
- }
- void dsa_tree_master_oper_state_change(struct dsa_switch_tree *dst,
- struct net_device *master,
- bool up)
- {
- struct dsa_port *cpu_dp = master->dsa_ptr;
- bool notify = false;
- /* Don't keep track of oper state on LAG DSA masters,
- * but rather just of physical DSA masters
- */
- if (netif_is_lag_master(master))
- return;
- if ((dsa_port_master_is_operational(cpu_dp)) !=
- (cpu_dp->master_admin_up && up))
- notify = true;
- cpu_dp->master_oper_up = up;
- if (notify)
- dsa_tree_master_state_change(dst, master);
- }
- static struct dsa_port *dsa_port_touch(struct dsa_switch *ds, int index)
- {
- struct dsa_switch_tree *dst = ds->dst;
- struct dsa_port *dp;
- dsa_switch_for_each_port(dp, ds)
- if (dp->index == index)
- return dp;
- dp = kzalloc(sizeof(*dp), GFP_KERNEL);
- if (!dp)
- return NULL;
- dp->ds = ds;
- dp->index = index;
- mutex_init(&dp->addr_lists_lock);
- mutex_init(&dp->vlans_lock);
- INIT_LIST_HEAD(&dp->fdbs);
- INIT_LIST_HEAD(&dp->mdbs);
- INIT_LIST_HEAD(&dp->vlans);
- INIT_LIST_HEAD(&dp->list);
- list_add_tail(&dp->list, &dst->ports);
- return dp;
- }
- static int dsa_port_parse_user(struct dsa_port *dp, const char *name)
- {
- if (!name)
- name = "eth%d";
- dp->type = DSA_PORT_TYPE_USER;
- dp->name = name;
- return 0;
- }
- static int dsa_port_parse_dsa(struct dsa_port *dp)
- {
- dp->type = DSA_PORT_TYPE_DSA;
- 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,
- const char *user_protocol)
- {
- const struct dsa_device_ops *tag_ops = NULL;
- struct dsa_switch *ds = dp->ds;
- struct dsa_switch_tree *dst = ds->dst;
- enum dsa_tag_protocol default_proto;
- /* Find out which protocol the switch would prefer. */
- default_proto = dsa_get_tag_protocol(dp, master);
- if (dst->default_proto) {
- if (dst->default_proto != default_proto) {
- dev_err(ds->dev,
- "A DSA switch tree can have only one tagging protocol\n");
- return -EINVAL;
- }
- } else {
- dst->default_proto = default_proto;
- }
- /* See if the user wants to override that preference. */
- if (user_protocol) {
- if (!ds->ops->change_tag_protocol) {
- dev_err(ds->dev, "Tag protocol cannot be modified\n");
- return -EINVAL;
- }
- tag_ops = dsa_find_tagger_by_name(user_protocol);
- if (IS_ERR(tag_ops)) {
- dev_warn(ds->dev,
- "Failed to find a tagging driver for protocol %s, using default\n",
- user_protocol);
- tag_ops = NULL;
- }
- }
- if (!tag_ops)
- tag_ops = dsa_tag_driver_get(default_proto);
- if (IS_ERR(tag_ops)) {
- if (PTR_ERR(tag_ops) == -ENOPROTOOPT)
- return -EPROBE_DEFER;
- dev_warn(ds->dev, "No tagger for this switch\n");
- return PTR_ERR(tag_ops);
- }
- if (dst->tag_ops) {
- if (dst->tag_ops != tag_ops) {
- dev_err(ds->dev,
- "A DSA switch tree can have only one tagging protocol\n");
- dsa_tag_driver_put(tag_ops);
- return -EINVAL;
- }
- /* In the case of multiple CPU ports per switch, the tagging
- * protocol is still reference-counted only per switch tree.
- */
- dsa_tag_driver_put(tag_ops);
- } else {
- dst->tag_ops = tag_ops;
- }
- dp->master = master;
- dp->type = DSA_PORT_TYPE_CPU;
- dsa_port_set_tag_protocol(dp, dst->tag_ops);
- dp->dst = dst;
- /* At this point, the tree may be configured to use a different
- * tagger than the one chosen by the switch driver during
- * .setup, in the case when a user selects a custom protocol
- * through the DT.
- *
- * This is resolved by syncing the driver with the tree in
- * dsa_switch_setup_tag_protocol once .setup has run and the
- * driver is ready to accept calls to .change_tag_protocol. If
- * the driver does not support the custom protocol at that
- * point, the tree is wholly rejected, thereby ensuring that the
- * tree and driver are always in agreement on the protocol to
- * use.
- */
- return 0;
- }
- static int dsa_port_parse_of(struct dsa_port *dp, struct device_node *dn)
- {
- struct device_node *ethernet = of_parse_phandle(dn, "ethernet", 0);
- const char *name = of_get_property(dn, "label", NULL);
- bool link = of_property_read_bool(dn, "link");
- dp->dn = dn;
- if (ethernet) {
- struct net_device *master;
- const char *user_protocol;
- master = of_find_net_device_by_node(ethernet);
- of_node_put(ethernet);
- if (!master)
- return -EPROBE_DEFER;
- user_protocol = of_get_property(dn, "dsa-tag-protocol", NULL);
- return dsa_port_parse_cpu(dp, master, user_protocol);
- }
- if (link)
- return dsa_port_parse_dsa(dp);
- return dsa_port_parse_user(dp, name);
- }
- static int dsa_switch_parse_ports_of(struct dsa_switch *ds,
- struct device_node *dn)
- {
- struct device_node *ports, *port;
- struct dsa_port *dp;
- int err = 0;
- u32 reg;
- ports = of_get_child_by_name(dn, "ports");
- if (!ports) {
- /* The second possibility is "ethernet-ports" */
- ports = of_get_child_by_name(dn, "ethernet-ports");
- if (!ports) {
- dev_err(ds->dev, "no ports child node found\n");
- return -EINVAL;
- }
- }
- for_each_available_child_of_node(ports, port) {
- err = of_property_read_u32(port, "reg", ®);
- if (err) {
- of_node_put(port);
- goto out_put_node;
- }
- if (reg >= ds->num_ports) {
- dev_err(ds->dev, "port %pOF index %u exceeds num_ports (%u)\n",
- port, reg, ds->num_ports);
- of_node_put(port);
- err = -EINVAL;
- goto out_put_node;
- }
- dp = dsa_to_port(ds, reg);
- err = dsa_port_parse_of(dp, port);
- if (err) {
- of_node_put(port);
- goto out_put_node;
- }
- }
- out_put_node:
- of_node_put(ports);
- return err;
- }
- static int dsa_switch_parse_member_of(struct dsa_switch *ds,
- struct device_node *dn)
- {
- u32 m[2] = { 0, 0 };
- int sz;
- /* Don't error out if this optional property isn't found */
- sz = of_property_read_variable_u32_array(dn, "dsa,member", m, 2, 2);
- if (sz < 0 && sz != -EINVAL)
- return sz;
- ds->index = m[1];
- ds->dst = dsa_tree_touch(m[0]);
- if (!ds->dst)
- return -ENOMEM;
- if (dsa_switch_find(ds->dst->index, ds->index)) {
- dev_err(ds->dev,
- "A DSA switch with index %d already exists in tree %d\n",
- ds->index, ds->dst->index);
- return -EEXIST;
- }
- if (ds->dst->last_switch < ds->index)
- ds->dst->last_switch = ds->index;
- return 0;
- }
- static int dsa_switch_touch_ports(struct dsa_switch *ds)
- {
- struct dsa_port *dp;
- int port;
- for (port = 0; port < ds->num_ports; port++) {
- dp = dsa_port_touch(ds, port);
- if (!dp)
- return -ENOMEM;
- }
- return 0;
- }
- static int dsa_switch_parse_of(struct dsa_switch *ds, struct device_node *dn)
- {
- int err;
- err = dsa_switch_parse_member_of(ds, dn);
- if (err)
- return err;
- err = dsa_switch_touch_ports(ds);
- if (err)
- return err;
- return dsa_switch_parse_ports_of(ds, dn);
- }
- static int dsa_port_parse(struct dsa_port *dp, const char *name,
- struct device *dev)
- {
- if (!strcmp(name, "cpu")) {
- struct net_device *master;
- master = dsa_dev_to_net_device(dev);
- if (!master)
- return -EPROBE_DEFER;
- dev_put(master);
- return dsa_port_parse_cpu(dp, master, NULL);
- }
- if (!strcmp(name, "dsa"))
- return dsa_port_parse_dsa(dp);
- return dsa_port_parse_user(dp, name);
- }
- static int dsa_switch_parse_ports(struct dsa_switch *ds,
- struct dsa_chip_data *cd)
- {
- bool valid_name_found = false;
- struct dsa_port *dp;
- struct device *dev;
- const char *name;
- unsigned int i;
- int err;
- for (i = 0; i < DSA_MAX_PORTS; i++) {
- name = cd->port_names[i];
- dev = cd->netdev[i];
- dp = dsa_to_port(ds, i);
- if (!name)
- continue;
- err = dsa_port_parse(dp, name, dev);
- if (err)
- return err;
- valid_name_found = true;
- }
- if (!valid_name_found && i == DSA_MAX_PORTS)
- return -EINVAL;
- return 0;
- }
- static int dsa_switch_parse(struct dsa_switch *ds, struct dsa_chip_data *cd)
- {
- int err;
- ds->cd = cd;
- /* We don't support interconnected switches nor multiple trees via
- * platform data, so this is the unique switch of the tree.
- */
- ds->index = 0;
- ds->dst = dsa_tree_touch(0);
- if (!ds->dst)
- return -ENOMEM;
- err = dsa_switch_touch_ports(ds);
- if (err)
- return err;
- return dsa_switch_parse_ports(ds, cd);
- }
- static void dsa_switch_release_ports(struct dsa_switch *ds)
- {
- struct dsa_port *dp, *next;
- dsa_switch_for_each_port_safe(dp, next, ds) {
- WARN_ON(!list_empty(&dp->fdbs));
- WARN_ON(!list_empty(&dp->mdbs));
- WARN_ON(!list_empty(&dp->vlans));
- list_del(&dp->list);
- kfree(dp);
- }
- }
- static int dsa_switch_probe(struct dsa_switch *ds)
- {
- struct dsa_switch_tree *dst;
- struct dsa_chip_data *pdata;
- struct device_node *np;
- int err;
- if (!ds->dev)
- return -ENODEV;
- pdata = ds->dev->platform_data;
- np = ds->dev->of_node;
- if (!ds->num_ports)
- return -EINVAL;
- if (np) {
- err = dsa_switch_parse_of(ds, np);
- if (err)
- dsa_switch_release_ports(ds);
- } else if (pdata) {
- err = dsa_switch_parse(ds, pdata);
- if (err)
- dsa_switch_release_ports(ds);
- } else {
- err = -ENODEV;
- }
- if (err)
- return err;
- dst = ds->dst;
- dsa_tree_get(dst);
- err = dsa_tree_setup(dst);
- if (err) {
- dsa_switch_release_ports(ds);
- dsa_tree_put(dst);
- }
- return err;
- }
- int dsa_register_switch(struct dsa_switch *ds)
- {
- int err;
- mutex_lock(&dsa2_mutex);
- err = dsa_switch_probe(ds);
- dsa_tree_put(ds->dst);
- mutex_unlock(&dsa2_mutex);
- return err;
- }
- EXPORT_SYMBOL_GPL(dsa_register_switch);
- static void dsa_switch_remove(struct dsa_switch *ds)
- {
- struct dsa_switch_tree *dst = ds->dst;
- dsa_tree_teardown(dst);
- dsa_switch_release_ports(ds);
- dsa_tree_put(dst);
- }
- void dsa_unregister_switch(struct dsa_switch *ds)
- {
- mutex_lock(&dsa2_mutex);
- dsa_switch_remove(ds);
- mutex_unlock(&dsa2_mutex);
- }
- EXPORT_SYMBOL_GPL(dsa_unregister_switch);
- /* If the DSA master chooses to unregister its net_device on .shutdown, DSA is
- * blocking that operation from completion, due to the dev_hold taken inside
- * netdev_upper_dev_link. Unlink the DSA slave interfaces from being uppers of
- * the DSA master, so that the system can reboot successfully.
- */
- void dsa_switch_shutdown(struct dsa_switch *ds)
- {
- struct net_device *master, *slave_dev;
- struct dsa_port *dp;
- mutex_lock(&dsa2_mutex);
- if (!ds->setup)
- goto out;
- rtnl_lock();
- dsa_switch_for_each_user_port(dp, ds) {
- master = dsa_port_to_master(dp);
- slave_dev = dp->slave;
- netdev_upper_dev_unlink(master, slave_dev);
- }
- /* Disconnect from further netdevice notifiers on the master,
- * since netdev_uses_dsa() will now return false.
- */
- dsa_switch_for_each_cpu_port(dp, ds)
- dp->master->dsa_ptr = NULL;
- rtnl_unlock();
- out:
- mutex_unlock(&dsa2_mutex);
- }
- EXPORT_SYMBOL_GPL(dsa_switch_shutdown);
|