162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci 362306a36Sopenharmony_ci#include <linux/mrp_bridge.h> 462306a36Sopenharmony_ci#include "br_private_mrp.h" 562306a36Sopenharmony_ci 662306a36Sopenharmony_cistatic const u8 mrp_test_dmac[ETH_ALEN] = { 0x1, 0x15, 0x4e, 0x0, 0x0, 0x1 }; 762306a36Sopenharmony_cistatic const u8 mrp_in_test_dmac[ETH_ALEN] = { 0x1, 0x15, 0x4e, 0x0, 0x0, 0x3 }; 862306a36Sopenharmony_ci 962306a36Sopenharmony_cistatic int br_mrp_process(struct net_bridge_port *p, struct sk_buff *skb); 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_cistatic struct br_frame_type mrp_frame_type __read_mostly = { 1262306a36Sopenharmony_ci .type = cpu_to_be16(ETH_P_MRP), 1362306a36Sopenharmony_ci .frame_handler = br_mrp_process, 1462306a36Sopenharmony_ci}; 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_cistatic bool br_mrp_is_ring_port(struct net_bridge_port *p_port, 1762306a36Sopenharmony_ci struct net_bridge_port *s_port, 1862306a36Sopenharmony_ci struct net_bridge_port *port) 1962306a36Sopenharmony_ci{ 2062306a36Sopenharmony_ci if (port == p_port || 2162306a36Sopenharmony_ci port == s_port) 2262306a36Sopenharmony_ci return true; 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci return false; 2562306a36Sopenharmony_ci} 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_cistatic bool br_mrp_is_in_port(struct net_bridge_port *i_port, 2862306a36Sopenharmony_ci struct net_bridge_port *port) 2962306a36Sopenharmony_ci{ 3062306a36Sopenharmony_ci if (port == i_port) 3162306a36Sopenharmony_ci return true; 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci return false; 3462306a36Sopenharmony_ci} 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_cistatic struct net_bridge_port *br_mrp_get_port(struct net_bridge *br, 3762306a36Sopenharmony_ci u32 ifindex) 3862306a36Sopenharmony_ci{ 3962306a36Sopenharmony_ci struct net_bridge_port *res = NULL; 4062306a36Sopenharmony_ci struct net_bridge_port *port; 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci list_for_each_entry(port, &br->port_list, list) { 4362306a36Sopenharmony_ci if (port->dev->ifindex == ifindex) { 4462306a36Sopenharmony_ci res = port; 4562306a36Sopenharmony_ci break; 4662306a36Sopenharmony_ci } 4762306a36Sopenharmony_ci } 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci return res; 5062306a36Sopenharmony_ci} 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_cistatic struct br_mrp *br_mrp_find_id(struct net_bridge *br, u32 ring_id) 5362306a36Sopenharmony_ci{ 5462306a36Sopenharmony_ci struct br_mrp *res = NULL; 5562306a36Sopenharmony_ci struct br_mrp *mrp; 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci hlist_for_each_entry_rcu(mrp, &br->mrp_list, list, 5862306a36Sopenharmony_ci lockdep_rtnl_is_held()) { 5962306a36Sopenharmony_ci if (mrp->ring_id == ring_id) { 6062306a36Sopenharmony_ci res = mrp; 6162306a36Sopenharmony_ci break; 6262306a36Sopenharmony_ci } 6362306a36Sopenharmony_ci } 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci return res; 6662306a36Sopenharmony_ci} 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_cistatic struct br_mrp *br_mrp_find_in_id(struct net_bridge *br, u32 in_id) 6962306a36Sopenharmony_ci{ 7062306a36Sopenharmony_ci struct br_mrp *res = NULL; 7162306a36Sopenharmony_ci struct br_mrp *mrp; 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci hlist_for_each_entry_rcu(mrp, &br->mrp_list, list, 7462306a36Sopenharmony_ci lockdep_rtnl_is_held()) { 7562306a36Sopenharmony_ci if (mrp->in_id == in_id) { 7662306a36Sopenharmony_ci res = mrp; 7762306a36Sopenharmony_ci break; 7862306a36Sopenharmony_ci } 7962306a36Sopenharmony_ci } 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci return res; 8262306a36Sopenharmony_ci} 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_cistatic bool br_mrp_unique_ifindex(struct net_bridge *br, u32 ifindex) 8562306a36Sopenharmony_ci{ 8662306a36Sopenharmony_ci struct br_mrp *mrp; 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci hlist_for_each_entry_rcu(mrp, &br->mrp_list, list, 8962306a36Sopenharmony_ci lockdep_rtnl_is_held()) { 9062306a36Sopenharmony_ci struct net_bridge_port *p; 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci p = rtnl_dereference(mrp->p_port); 9362306a36Sopenharmony_ci if (p && p->dev->ifindex == ifindex) 9462306a36Sopenharmony_ci return false; 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci p = rtnl_dereference(mrp->s_port); 9762306a36Sopenharmony_ci if (p && p->dev->ifindex == ifindex) 9862306a36Sopenharmony_ci return false; 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci p = rtnl_dereference(mrp->i_port); 10162306a36Sopenharmony_ci if (p && p->dev->ifindex == ifindex) 10262306a36Sopenharmony_ci return false; 10362306a36Sopenharmony_ci } 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci return true; 10662306a36Sopenharmony_ci} 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_cistatic struct br_mrp *br_mrp_find_port(struct net_bridge *br, 10962306a36Sopenharmony_ci struct net_bridge_port *p) 11062306a36Sopenharmony_ci{ 11162306a36Sopenharmony_ci struct br_mrp *res = NULL; 11262306a36Sopenharmony_ci struct br_mrp *mrp; 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci hlist_for_each_entry_rcu(mrp, &br->mrp_list, list, 11562306a36Sopenharmony_ci lockdep_rtnl_is_held()) { 11662306a36Sopenharmony_ci if (rcu_access_pointer(mrp->p_port) == p || 11762306a36Sopenharmony_ci rcu_access_pointer(mrp->s_port) == p || 11862306a36Sopenharmony_ci rcu_access_pointer(mrp->i_port) == p) { 11962306a36Sopenharmony_ci res = mrp; 12062306a36Sopenharmony_ci break; 12162306a36Sopenharmony_ci } 12262306a36Sopenharmony_ci } 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci return res; 12562306a36Sopenharmony_ci} 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_cistatic int br_mrp_next_seq(struct br_mrp *mrp) 12862306a36Sopenharmony_ci{ 12962306a36Sopenharmony_ci mrp->seq_id++; 13062306a36Sopenharmony_ci return mrp->seq_id; 13162306a36Sopenharmony_ci} 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_cistatic struct sk_buff *br_mrp_skb_alloc(struct net_bridge_port *p, 13462306a36Sopenharmony_ci const u8 *src, const u8 *dst) 13562306a36Sopenharmony_ci{ 13662306a36Sopenharmony_ci struct ethhdr *eth_hdr; 13762306a36Sopenharmony_ci struct sk_buff *skb; 13862306a36Sopenharmony_ci __be16 *version; 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci skb = dev_alloc_skb(MRP_MAX_FRAME_LENGTH); 14162306a36Sopenharmony_ci if (!skb) 14262306a36Sopenharmony_ci return NULL; 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci skb->dev = p->dev; 14562306a36Sopenharmony_ci skb->protocol = htons(ETH_P_MRP); 14662306a36Sopenharmony_ci skb->priority = MRP_FRAME_PRIO; 14762306a36Sopenharmony_ci skb_reserve(skb, sizeof(*eth_hdr)); 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci eth_hdr = skb_push(skb, sizeof(*eth_hdr)); 15062306a36Sopenharmony_ci ether_addr_copy(eth_hdr->h_dest, dst); 15162306a36Sopenharmony_ci ether_addr_copy(eth_hdr->h_source, src); 15262306a36Sopenharmony_ci eth_hdr->h_proto = htons(ETH_P_MRP); 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci version = skb_put(skb, sizeof(*version)); 15562306a36Sopenharmony_ci *version = cpu_to_be16(MRP_VERSION); 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci return skb; 15862306a36Sopenharmony_ci} 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_cistatic void br_mrp_skb_tlv(struct sk_buff *skb, 16162306a36Sopenharmony_ci enum br_mrp_tlv_header_type type, 16262306a36Sopenharmony_ci u8 length) 16362306a36Sopenharmony_ci{ 16462306a36Sopenharmony_ci struct br_mrp_tlv_hdr *hdr; 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci hdr = skb_put(skb, sizeof(*hdr)); 16762306a36Sopenharmony_ci hdr->type = type; 16862306a36Sopenharmony_ci hdr->length = length; 16962306a36Sopenharmony_ci} 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_cistatic void br_mrp_skb_common(struct sk_buff *skb, struct br_mrp *mrp) 17262306a36Sopenharmony_ci{ 17362306a36Sopenharmony_ci struct br_mrp_common_hdr *hdr; 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci br_mrp_skb_tlv(skb, BR_MRP_TLV_HEADER_COMMON, sizeof(*hdr)); 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci hdr = skb_put(skb, sizeof(*hdr)); 17862306a36Sopenharmony_ci hdr->seq_id = cpu_to_be16(br_mrp_next_seq(mrp)); 17962306a36Sopenharmony_ci memset(hdr->domain, 0xff, MRP_DOMAIN_UUID_LENGTH); 18062306a36Sopenharmony_ci} 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_cistatic struct sk_buff *br_mrp_alloc_test_skb(struct br_mrp *mrp, 18362306a36Sopenharmony_ci struct net_bridge_port *p, 18462306a36Sopenharmony_ci enum br_mrp_port_role_type port_role) 18562306a36Sopenharmony_ci{ 18662306a36Sopenharmony_ci struct br_mrp_ring_test_hdr *hdr = NULL; 18762306a36Sopenharmony_ci struct sk_buff *skb = NULL; 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci if (!p) 19062306a36Sopenharmony_ci return NULL; 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci skb = br_mrp_skb_alloc(p, p->dev->dev_addr, mrp_test_dmac); 19362306a36Sopenharmony_ci if (!skb) 19462306a36Sopenharmony_ci return NULL; 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci br_mrp_skb_tlv(skb, BR_MRP_TLV_HEADER_RING_TEST, sizeof(*hdr)); 19762306a36Sopenharmony_ci hdr = skb_put(skb, sizeof(*hdr)); 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci hdr->prio = cpu_to_be16(mrp->prio); 20062306a36Sopenharmony_ci ether_addr_copy(hdr->sa, p->br->dev->dev_addr); 20162306a36Sopenharmony_ci hdr->port_role = cpu_to_be16(port_role); 20262306a36Sopenharmony_ci hdr->state = cpu_to_be16(mrp->ring_state); 20362306a36Sopenharmony_ci hdr->transitions = cpu_to_be16(mrp->ring_transitions); 20462306a36Sopenharmony_ci hdr->timestamp = cpu_to_be32(jiffies_to_msecs(jiffies)); 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci br_mrp_skb_common(skb, mrp); 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ci /* In case the node behaves as MRA then the Test frame needs to have 20962306a36Sopenharmony_ci * an Option TLV which includes eventually a sub-option TLV that has 21062306a36Sopenharmony_ci * the type AUTO_MGR 21162306a36Sopenharmony_ci */ 21262306a36Sopenharmony_ci if (mrp->ring_role == BR_MRP_RING_ROLE_MRA) { 21362306a36Sopenharmony_ci struct br_mrp_sub_option1_hdr *sub_opt = NULL; 21462306a36Sopenharmony_ci struct br_mrp_tlv_hdr *sub_tlv = NULL; 21562306a36Sopenharmony_ci struct br_mrp_oui_hdr *oui = NULL; 21662306a36Sopenharmony_ci u8 length; 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ci length = sizeof(*sub_opt) + sizeof(*sub_tlv) + sizeof(oui) + 21962306a36Sopenharmony_ci MRP_OPT_PADDING; 22062306a36Sopenharmony_ci br_mrp_skb_tlv(skb, BR_MRP_TLV_HEADER_OPTION, length); 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci oui = skb_put(skb, sizeof(*oui)); 22362306a36Sopenharmony_ci memset(oui, 0x0, sizeof(*oui)); 22462306a36Sopenharmony_ci sub_opt = skb_put(skb, sizeof(*sub_opt)); 22562306a36Sopenharmony_ci memset(sub_opt, 0x0, sizeof(*sub_opt)); 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ci sub_tlv = skb_put(skb, sizeof(*sub_tlv)); 22862306a36Sopenharmony_ci sub_tlv->type = BR_MRP_SUB_TLV_HEADER_TEST_AUTO_MGR; 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ci /* 32 bit alligment shall be ensured therefore add 2 bytes */ 23162306a36Sopenharmony_ci skb_put(skb, MRP_OPT_PADDING); 23262306a36Sopenharmony_ci } 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci br_mrp_skb_tlv(skb, BR_MRP_TLV_HEADER_END, 0x0); 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci return skb; 23762306a36Sopenharmony_ci} 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_cistatic struct sk_buff *br_mrp_alloc_in_test_skb(struct br_mrp *mrp, 24062306a36Sopenharmony_ci struct net_bridge_port *p, 24162306a36Sopenharmony_ci enum br_mrp_port_role_type port_role) 24262306a36Sopenharmony_ci{ 24362306a36Sopenharmony_ci struct br_mrp_in_test_hdr *hdr = NULL; 24462306a36Sopenharmony_ci struct sk_buff *skb = NULL; 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ci if (!p) 24762306a36Sopenharmony_ci return NULL; 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ci skb = br_mrp_skb_alloc(p, p->dev->dev_addr, mrp_in_test_dmac); 25062306a36Sopenharmony_ci if (!skb) 25162306a36Sopenharmony_ci return NULL; 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_ci br_mrp_skb_tlv(skb, BR_MRP_TLV_HEADER_IN_TEST, sizeof(*hdr)); 25462306a36Sopenharmony_ci hdr = skb_put(skb, sizeof(*hdr)); 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ci hdr->id = cpu_to_be16(mrp->in_id); 25762306a36Sopenharmony_ci ether_addr_copy(hdr->sa, p->br->dev->dev_addr); 25862306a36Sopenharmony_ci hdr->port_role = cpu_to_be16(port_role); 25962306a36Sopenharmony_ci hdr->state = cpu_to_be16(mrp->in_state); 26062306a36Sopenharmony_ci hdr->transitions = cpu_to_be16(mrp->in_transitions); 26162306a36Sopenharmony_ci hdr->timestamp = cpu_to_be32(jiffies_to_msecs(jiffies)); 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci br_mrp_skb_common(skb, mrp); 26462306a36Sopenharmony_ci br_mrp_skb_tlv(skb, BR_MRP_TLV_HEADER_END, 0x0); 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_ci return skb; 26762306a36Sopenharmony_ci} 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci/* This function is continuously called in the following cases: 27062306a36Sopenharmony_ci * - when node role is MRM, in this case test_monitor is always set to false 27162306a36Sopenharmony_ci * because it needs to notify the userspace that the ring is open and needs to 27262306a36Sopenharmony_ci * send MRP_Test frames 27362306a36Sopenharmony_ci * - when node role is MRA, there are 2 subcases: 27462306a36Sopenharmony_ci * - when MRA behaves as MRM, in this case is similar with MRM role 27562306a36Sopenharmony_ci * - when MRA behaves as MRC, in this case test_monitor is set to true, 27662306a36Sopenharmony_ci * because it needs to detect when it stops seeing MRP_Test frames 27762306a36Sopenharmony_ci * from MRM node but it doesn't need to send MRP_Test frames. 27862306a36Sopenharmony_ci */ 27962306a36Sopenharmony_cistatic void br_mrp_test_work_expired(struct work_struct *work) 28062306a36Sopenharmony_ci{ 28162306a36Sopenharmony_ci struct delayed_work *del_work = to_delayed_work(work); 28262306a36Sopenharmony_ci struct br_mrp *mrp = container_of(del_work, struct br_mrp, test_work); 28362306a36Sopenharmony_ci struct net_bridge_port *p; 28462306a36Sopenharmony_ci bool notify_open = false; 28562306a36Sopenharmony_ci struct sk_buff *skb; 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_ci if (time_before_eq(mrp->test_end, jiffies)) 28862306a36Sopenharmony_ci return; 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_ci if (mrp->test_count_miss < mrp->test_max_miss) { 29162306a36Sopenharmony_ci mrp->test_count_miss++; 29262306a36Sopenharmony_ci } else { 29362306a36Sopenharmony_ci /* Notify that the ring is open only if the ring state is 29462306a36Sopenharmony_ci * closed, otherwise it would continue to notify at every 29562306a36Sopenharmony_ci * interval. 29662306a36Sopenharmony_ci * Also notify that the ring is open when the node has the 29762306a36Sopenharmony_ci * role MRA and behaves as MRC. The reason is that the 29862306a36Sopenharmony_ci * userspace needs to know when the MRM stopped sending 29962306a36Sopenharmony_ci * MRP_Test frames so that the current node to try to take 30062306a36Sopenharmony_ci * the role of a MRM. 30162306a36Sopenharmony_ci */ 30262306a36Sopenharmony_ci if (mrp->ring_state == BR_MRP_RING_STATE_CLOSED || 30362306a36Sopenharmony_ci mrp->test_monitor) 30462306a36Sopenharmony_ci notify_open = true; 30562306a36Sopenharmony_ci } 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_ci rcu_read_lock(); 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ci p = rcu_dereference(mrp->p_port); 31062306a36Sopenharmony_ci if (p) { 31162306a36Sopenharmony_ci if (!mrp->test_monitor) { 31262306a36Sopenharmony_ci skb = br_mrp_alloc_test_skb(mrp, p, 31362306a36Sopenharmony_ci BR_MRP_PORT_ROLE_PRIMARY); 31462306a36Sopenharmony_ci if (!skb) 31562306a36Sopenharmony_ci goto out; 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_ci skb_reset_network_header(skb); 31862306a36Sopenharmony_ci dev_queue_xmit(skb); 31962306a36Sopenharmony_ci } 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_ci if (notify_open && !mrp->ring_role_offloaded) 32262306a36Sopenharmony_ci br_mrp_ring_port_open(p->dev, true); 32362306a36Sopenharmony_ci } 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_ci p = rcu_dereference(mrp->s_port); 32662306a36Sopenharmony_ci if (p) { 32762306a36Sopenharmony_ci if (!mrp->test_monitor) { 32862306a36Sopenharmony_ci skb = br_mrp_alloc_test_skb(mrp, p, 32962306a36Sopenharmony_ci BR_MRP_PORT_ROLE_SECONDARY); 33062306a36Sopenharmony_ci if (!skb) 33162306a36Sopenharmony_ci goto out; 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_ci skb_reset_network_header(skb); 33462306a36Sopenharmony_ci dev_queue_xmit(skb); 33562306a36Sopenharmony_ci } 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_ci if (notify_open && !mrp->ring_role_offloaded) 33862306a36Sopenharmony_ci br_mrp_ring_port_open(p->dev, true); 33962306a36Sopenharmony_ci } 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_ciout: 34262306a36Sopenharmony_ci rcu_read_unlock(); 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_ci queue_delayed_work(system_wq, &mrp->test_work, 34562306a36Sopenharmony_ci usecs_to_jiffies(mrp->test_interval)); 34662306a36Sopenharmony_ci} 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_ci/* This function is continuously called when the node has the interconnect role 34962306a36Sopenharmony_ci * MIM. It would generate interconnect test frames and will send them on all 3 35062306a36Sopenharmony_ci * ports. But will also check if it stop receiving interconnect test frames. 35162306a36Sopenharmony_ci */ 35262306a36Sopenharmony_cistatic void br_mrp_in_test_work_expired(struct work_struct *work) 35362306a36Sopenharmony_ci{ 35462306a36Sopenharmony_ci struct delayed_work *del_work = to_delayed_work(work); 35562306a36Sopenharmony_ci struct br_mrp *mrp = container_of(del_work, struct br_mrp, in_test_work); 35662306a36Sopenharmony_ci struct net_bridge_port *p; 35762306a36Sopenharmony_ci bool notify_open = false; 35862306a36Sopenharmony_ci struct sk_buff *skb; 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_ci if (time_before_eq(mrp->in_test_end, jiffies)) 36162306a36Sopenharmony_ci return; 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_ci if (mrp->in_test_count_miss < mrp->in_test_max_miss) { 36462306a36Sopenharmony_ci mrp->in_test_count_miss++; 36562306a36Sopenharmony_ci } else { 36662306a36Sopenharmony_ci /* Notify that the interconnect ring is open only if the 36762306a36Sopenharmony_ci * interconnect ring state is closed, otherwise it would 36862306a36Sopenharmony_ci * continue to notify at every interval. 36962306a36Sopenharmony_ci */ 37062306a36Sopenharmony_ci if (mrp->in_state == BR_MRP_IN_STATE_CLOSED) 37162306a36Sopenharmony_ci notify_open = true; 37262306a36Sopenharmony_ci } 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_ci rcu_read_lock(); 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_ci p = rcu_dereference(mrp->p_port); 37762306a36Sopenharmony_ci if (p) { 37862306a36Sopenharmony_ci skb = br_mrp_alloc_in_test_skb(mrp, p, 37962306a36Sopenharmony_ci BR_MRP_PORT_ROLE_PRIMARY); 38062306a36Sopenharmony_ci if (!skb) 38162306a36Sopenharmony_ci goto out; 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_ci skb_reset_network_header(skb); 38462306a36Sopenharmony_ci dev_queue_xmit(skb); 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_ci if (notify_open && !mrp->in_role_offloaded) 38762306a36Sopenharmony_ci br_mrp_in_port_open(p->dev, true); 38862306a36Sopenharmony_ci } 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_ci p = rcu_dereference(mrp->s_port); 39162306a36Sopenharmony_ci if (p) { 39262306a36Sopenharmony_ci skb = br_mrp_alloc_in_test_skb(mrp, p, 39362306a36Sopenharmony_ci BR_MRP_PORT_ROLE_SECONDARY); 39462306a36Sopenharmony_ci if (!skb) 39562306a36Sopenharmony_ci goto out; 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_ci skb_reset_network_header(skb); 39862306a36Sopenharmony_ci dev_queue_xmit(skb); 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_ci if (notify_open && !mrp->in_role_offloaded) 40162306a36Sopenharmony_ci br_mrp_in_port_open(p->dev, true); 40262306a36Sopenharmony_ci } 40362306a36Sopenharmony_ci 40462306a36Sopenharmony_ci p = rcu_dereference(mrp->i_port); 40562306a36Sopenharmony_ci if (p) { 40662306a36Sopenharmony_ci skb = br_mrp_alloc_in_test_skb(mrp, p, 40762306a36Sopenharmony_ci BR_MRP_PORT_ROLE_INTER); 40862306a36Sopenharmony_ci if (!skb) 40962306a36Sopenharmony_ci goto out; 41062306a36Sopenharmony_ci 41162306a36Sopenharmony_ci skb_reset_network_header(skb); 41262306a36Sopenharmony_ci dev_queue_xmit(skb); 41362306a36Sopenharmony_ci 41462306a36Sopenharmony_ci if (notify_open && !mrp->in_role_offloaded) 41562306a36Sopenharmony_ci br_mrp_in_port_open(p->dev, true); 41662306a36Sopenharmony_ci } 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_ciout: 41962306a36Sopenharmony_ci rcu_read_unlock(); 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_ci queue_delayed_work(system_wq, &mrp->in_test_work, 42262306a36Sopenharmony_ci usecs_to_jiffies(mrp->in_test_interval)); 42362306a36Sopenharmony_ci} 42462306a36Sopenharmony_ci 42562306a36Sopenharmony_ci/* Deletes the MRP instance. 42662306a36Sopenharmony_ci * note: called under rtnl_lock 42762306a36Sopenharmony_ci */ 42862306a36Sopenharmony_cistatic void br_mrp_del_impl(struct net_bridge *br, struct br_mrp *mrp) 42962306a36Sopenharmony_ci{ 43062306a36Sopenharmony_ci struct net_bridge_port *p; 43162306a36Sopenharmony_ci u8 state; 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_ci /* Stop sending MRP_Test frames */ 43462306a36Sopenharmony_ci cancel_delayed_work_sync(&mrp->test_work); 43562306a36Sopenharmony_ci br_mrp_switchdev_send_ring_test(br, mrp, 0, 0, 0, 0); 43662306a36Sopenharmony_ci 43762306a36Sopenharmony_ci /* Stop sending MRP_InTest frames if has an interconnect role */ 43862306a36Sopenharmony_ci cancel_delayed_work_sync(&mrp->in_test_work); 43962306a36Sopenharmony_ci br_mrp_switchdev_send_in_test(br, mrp, 0, 0, 0); 44062306a36Sopenharmony_ci 44162306a36Sopenharmony_ci /* Disable the roles */ 44262306a36Sopenharmony_ci br_mrp_switchdev_set_ring_role(br, mrp, BR_MRP_RING_ROLE_DISABLED); 44362306a36Sopenharmony_ci p = rtnl_dereference(mrp->i_port); 44462306a36Sopenharmony_ci if (p) 44562306a36Sopenharmony_ci br_mrp_switchdev_set_in_role(br, mrp, mrp->in_id, mrp->ring_id, 44662306a36Sopenharmony_ci BR_MRP_IN_ROLE_DISABLED); 44762306a36Sopenharmony_ci 44862306a36Sopenharmony_ci br_mrp_switchdev_del(br, mrp); 44962306a36Sopenharmony_ci 45062306a36Sopenharmony_ci /* Reset the ports */ 45162306a36Sopenharmony_ci p = rtnl_dereference(mrp->p_port); 45262306a36Sopenharmony_ci if (p) { 45362306a36Sopenharmony_ci spin_lock_bh(&br->lock); 45462306a36Sopenharmony_ci state = netif_running(br->dev) ? 45562306a36Sopenharmony_ci BR_STATE_FORWARDING : BR_STATE_DISABLED; 45662306a36Sopenharmony_ci p->state = state; 45762306a36Sopenharmony_ci p->flags &= ~BR_MRP_AWARE; 45862306a36Sopenharmony_ci spin_unlock_bh(&br->lock); 45962306a36Sopenharmony_ci br_mrp_port_switchdev_set_state(p, state); 46062306a36Sopenharmony_ci rcu_assign_pointer(mrp->p_port, NULL); 46162306a36Sopenharmony_ci } 46262306a36Sopenharmony_ci 46362306a36Sopenharmony_ci p = rtnl_dereference(mrp->s_port); 46462306a36Sopenharmony_ci if (p) { 46562306a36Sopenharmony_ci spin_lock_bh(&br->lock); 46662306a36Sopenharmony_ci state = netif_running(br->dev) ? 46762306a36Sopenharmony_ci BR_STATE_FORWARDING : BR_STATE_DISABLED; 46862306a36Sopenharmony_ci p->state = state; 46962306a36Sopenharmony_ci p->flags &= ~BR_MRP_AWARE; 47062306a36Sopenharmony_ci spin_unlock_bh(&br->lock); 47162306a36Sopenharmony_ci br_mrp_port_switchdev_set_state(p, state); 47262306a36Sopenharmony_ci rcu_assign_pointer(mrp->s_port, NULL); 47362306a36Sopenharmony_ci } 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_ci p = rtnl_dereference(mrp->i_port); 47662306a36Sopenharmony_ci if (p) { 47762306a36Sopenharmony_ci spin_lock_bh(&br->lock); 47862306a36Sopenharmony_ci state = netif_running(br->dev) ? 47962306a36Sopenharmony_ci BR_STATE_FORWARDING : BR_STATE_DISABLED; 48062306a36Sopenharmony_ci p->state = state; 48162306a36Sopenharmony_ci p->flags &= ~BR_MRP_AWARE; 48262306a36Sopenharmony_ci spin_unlock_bh(&br->lock); 48362306a36Sopenharmony_ci br_mrp_port_switchdev_set_state(p, state); 48462306a36Sopenharmony_ci rcu_assign_pointer(mrp->i_port, NULL); 48562306a36Sopenharmony_ci } 48662306a36Sopenharmony_ci 48762306a36Sopenharmony_ci hlist_del_rcu(&mrp->list); 48862306a36Sopenharmony_ci kfree_rcu(mrp, rcu); 48962306a36Sopenharmony_ci 49062306a36Sopenharmony_ci if (hlist_empty(&br->mrp_list)) 49162306a36Sopenharmony_ci br_del_frame(br, &mrp_frame_type); 49262306a36Sopenharmony_ci} 49362306a36Sopenharmony_ci 49462306a36Sopenharmony_ci/* Adds a new MRP instance. 49562306a36Sopenharmony_ci * note: called under rtnl_lock 49662306a36Sopenharmony_ci */ 49762306a36Sopenharmony_ciint br_mrp_add(struct net_bridge *br, struct br_mrp_instance *instance) 49862306a36Sopenharmony_ci{ 49962306a36Sopenharmony_ci struct net_bridge_port *p; 50062306a36Sopenharmony_ci struct br_mrp *mrp; 50162306a36Sopenharmony_ci int err; 50262306a36Sopenharmony_ci 50362306a36Sopenharmony_ci /* If the ring exists, it is not possible to create another one with the 50462306a36Sopenharmony_ci * same ring_id 50562306a36Sopenharmony_ci */ 50662306a36Sopenharmony_ci mrp = br_mrp_find_id(br, instance->ring_id); 50762306a36Sopenharmony_ci if (mrp) 50862306a36Sopenharmony_ci return -EINVAL; 50962306a36Sopenharmony_ci 51062306a36Sopenharmony_ci if (!br_mrp_get_port(br, instance->p_ifindex) || 51162306a36Sopenharmony_ci !br_mrp_get_port(br, instance->s_ifindex)) 51262306a36Sopenharmony_ci return -EINVAL; 51362306a36Sopenharmony_ci 51462306a36Sopenharmony_ci /* It is not possible to have the same port part of multiple rings */ 51562306a36Sopenharmony_ci if (!br_mrp_unique_ifindex(br, instance->p_ifindex) || 51662306a36Sopenharmony_ci !br_mrp_unique_ifindex(br, instance->s_ifindex)) 51762306a36Sopenharmony_ci return -EINVAL; 51862306a36Sopenharmony_ci 51962306a36Sopenharmony_ci mrp = kzalloc(sizeof(*mrp), GFP_KERNEL); 52062306a36Sopenharmony_ci if (!mrp) 52162306a36Sopenharmony_ci return -ENOMEM; 52262306a36Sopenharmony_ci 52362306a36Sopenharmony_ci mrp->ring_id = instance->ring_id; 52462306a36Sopenharmony_ci mrp->prio = instance->prio; 52562306a36Sopenharmony_ci 52662306a36Sopenharmony_ci p = br_mrp_get_port(br, instance->p_ifindex); 52762306a36Sopenharmony_ci spin_lock_bh(&br->lock); 52862306a36Sopenharmony_ci p->state = BR_STATE_FORWARDING; 52962306a36Sopenharmony_ci p->flags |= BR_MRP_AWARE; 53062306a36Sopenharmony_ci spin_unlock_bh(&br->lock); 53162306a36Sopenharmony_ci rcu_assign_pointer(mrp->p_port, p); 53262306a36Sopenharmony_ci 53362306a36Sopenharmony_ci p = br_mrp_get_port(br, instance->s_ifindex); 53462306a36Sopenharmony_ci spin_lock_bh(&br->lock); 53562306a36Sopenharmony_ci p->state = BR_STATE_FORWARDING; 53662306a36Sopenharmony_ci p->flags |= BR_MRP_AWARE; 53762306a36Sopenharmony_ci spin_unlock_bh(&br->lock); 53862306a36Sopenharmony_ci rcu_assign_pointer(mrp->s_port, p); 53962306a36Sopenharmony_ci 54062306a36Sopenharmony_ci if (hlist_empty(&br->mrp_list)) 54162306a36Sopenharmony_ci br_add_frame(br, &mrp_frame_type); 54262306a36Sopenharmony_ci 54362306a36Sopenharmony_ci INIT_DELAYED_WORK(&mrp->test_work, br_mrp_test_work_expired); 54462306a36Sopenharmony_ci INIT_DELAYED_WORK(&mrp->in_test_work, br_mrp_in_test_work_expired); 54562306a36Sopenharmony_ci hlist_add_tail_rcu(&mrp->list, &br->mrp_list); 54662306a36Sopenharmony_ci 54762306a36Sopenharmony_ci err = br_mrp_switchdev_add(br, mrp); 54862306a36Sopenharmony_ci if (err) 54962306a36Sopenharmony_ci goto delete_mrp; 55062306a36Sopenharmony_ci 55162306a36Sopenharmony_ci return 0; 55262306a36Sopenharmony_ci 55362306a36Sopenharmony_cidelete_mrp: 55462306a36Sopenharmony_ci br_mrp_del_impl(br, mrp); 55562306a36Sopenharmony_ci 55662306a36Sopenharmony_ci return err; 55762306a36Sopenharmony_ci} 55862306a36Sopenharmony_ci 55962306a36Sopenharmony_ci/* Deletes the MRP instance from which the port is part of 56062306a36Sopenharmony_ci * note: called under rtnl_lock 56162306a36Sopenharmony_ci */ 56262306a36Sopenharmony_civoid br_mrp_port_del(struct net_bridge *br, struct net_bridge_port *p) 56362306a36Sopenharmony_ci{ 56462306a36Sopenharmony_ci struct br_mrp *mrp = br_mrp_find_port(br, p); 56562306a36Sopenharmony_ci 56662306a36Sopenharmony_ci /* If the port is not part of a MRP instance just bail out */ 56762306a36Sopenharmony_ci if (!mrp) 56862306a36Sopenharmony_ci return; 56962306a36Sopenharmony_ci 57062306a36Sopenharmony_ci br_mrp_del_impl(br, mrp); 57162306a36Sopenharmony_ci} 57262306a36Sopenharmony_ci 57362306a36Sopenharmony_ci/* Deletes existing MRP instance based on ring_id 57462306a36Sopenharmony_ci * note: called under rtnl_lock 57562306a36Sopenharmony_ci */ 57662306a36Sopenharmony_ciint br_mrp_del(struct net_bridge *br, struct br_mrp_instance *instance) 57762306a36Sopenharmony_ci{ 57862306a36Sopenharmony_ci struct br_mrp *mrp = br_mrp_find_id(br, instance->ring_id); 57962306a36Sopenharmony_ci 58062306a36Sopenharmony_ci if (!mrp) 58162306a36Sopenharmony_ci return -EINVAL; 58262306a36Sopenharmony_ci 58362306a36Sopenharmony_ci br_mrp_del_impl(br, mrp); 58462306a36Sopenharmony_ci 58562306a36Sopenharmony_ci return 0; 58662306a36Sopenharmony_ci} 58762306a36Sopenharmony_ci 58862306a36Sopenharmony_ci/* Set port state, port state can be forwarding, blocked or disabled 58962306a36Sopenharmony_ci * note: already called with rtnl_lock 59062306a36Sopenharmony_ci */ 59162306a36Sopenharmony_ciint br_mrp_set_port_state(struct net_bridge_port *p, 59262306a36Sopenharmony_ci enum br_mrp_port_state_type state) 59362306a36Sopenharmony_ci{ 59462306a36Sopenharmony_ci u32 port_state; 59562306a36Sopenharmony_ci 59662306a36Sopenharmony_ci if (!p || !(p->flags & BR_MRP_AWARE)) 59762306a36Sopenharmony_ci return -EINVAL; 59862306a36Sopenharmony_ci 59962306a36Sopenharmony_ci spin_lock_bh(&p->br->lock); 60062306a36Sopenharmony_ci 60162306a36Sopenharmony_ci if (state == BR_MRP_PORT_STATE_FORWARDING) 60262306a36Sopenharmony_ci port_state = BR_STATE_FORWARDING; 60362306a36Sopenharmony_ci else 60462306a36Sopenharmony_ci port_state = BR_STATE_BLOCKING; 60562306a36Sopenharmony_ci 60662306a36Sopenharmony_ci p->state = port_state; 60762306a36Sopenharmony_ci spin_unlock_bh(&p->br->lock); 60862306a36Sopenharmony_ci 60962306a36Sopenharmony_ci br_mrp_port_switchdev_set_state(p, port_state); 61062306a36Sopenharmony_ci 61162306a36Sopenharmony_ci return 0; 61262306a36Sopenharmony_ci} 61362306a36Sopenharmony_ci 61462306a36Sopenharmony_ci/* Set port role, port role can be primary or secondary 61562306a36Sopenharmony_ci * note: already called with rtnl_lock 61662306a36Sopenharmony_ci */ 61762306a36Sopenharmony_ciint br_mrp_set_port_role(struct net_bridge_port *p, 61862306a36Sopenharmony_ci enum br_mrp_port_role_type role) 61962306a36Sopenharmony_ci{ 62062306a36Sopenharmony_ci struct br_mrp *mrp; 62162306a36Sopenharmony_ci 62262306a36Sopenharmony_ci if (!p || !(p->flags & BR_MRP_AWARE)) 62362306a36Sopenharmony_ci return -EINVAL; 62462306a36Sopenharmony_ci 62562306a36Sopenharmony_ci mrp = br_mrp_find_port(p->br, p); 62662306a36Sopenharmony_ci 62762306a36Sopenharmony_ci if (!mrp) 62862306a36Sopenharmony_ci return -EINVAL; 62962306a36Sopenharmony_ci 63062306a36Sopenharmony_ci switch (role) { 63162306a36Sopenharmony_ci case BR_MRP_PORT_ROLE_PRIMARY: 63262306a36Sopenharmony_ci rcu_assign_pointer(mrp->p_port, p); 63362306a36Sopenharmony_ci break; 63462306a36Sopenharmony_ci case BR_MRP_PORT_ROLE_SECONDARY: 63562306a36Sopenharmony_ci rcu_assign_pointer(mrp->s_port, p); 63662306a36Sopenharmony_ci break; 63762306a36Sopenharmony_ci default: 63862306a36Sopenharmony_ci return -EINVAL; 63962306a36Sopenharmony_ci } 64062306a36Sopenharmony_ci 64162306a36Sopenharmony_ci br_mrp_port_switchdev_set_role(p, role); 64262306a36Sopenharmony_ci 64362306a36Sopenharmony_ci return 0; 64462306a36Sopenharmony_ci} 64562306a36Sopenharmony_ci 64662306a36Sopenharmony_ci/* Set ring state, ring state can be only Open or Closed 64762306a36Sopenharmony_ci * note: already called with rtnl_lock 64862306a36Sopenharmony_ci */ 64962306a36Sopenharmony_ciint br_mrp_set_ring_state(struct net_bridge *br, 65062306a36Sopenharmony_ci struct br_mrp_ring_state *state) 65162306a36Sopenharmony_ci{ 65262306a36Sopenharmony_ci struct br_mrp *mrp = br_mrp_find_id(br, state->ring_id); 65362306a36Sopenharmony_ci 65462306a36Sopenharmony_ci if (!mrp) 65562306a36Sopenharmony_ci return -EINVAL; 65662306a36Sopenharmony_ci 65762306a36Sopenharmony_ci if (mrp->ring_state != state->ring_state) 65862306a36Sopenharmony_ci mrp->ring_transitions++; 65962306a36Sopenharmony_ci 66062306a36Sopenharmony_ci mrp->ring_state = state->ring_state; 66162306a36Sopenharmony_ci 66262306a36Sopenharmony_ci br_mrp_switchdev_set_ring_state(br, mrp, state->ring_state); 66362306a36Sopenharmony_ci 66462306a36Sopenharmony_ci return 0; 66562306a36Sopenharmony_ci} 66662306a36Sopenharmony_ci 66762306a36Sopenharmony_ci/* Set ring role, ring role can be only MRM(Media Redundancy Manager) or 66862306a36Sopenharmony_ci * MRC(Media Redundancy Client). 66962306a36Sopenharmony_ci * note: already called with rtnl_lock 67062306a36Sopenharmony_ci */ 67162306a36Sopenharmony_ciint br_mrp_set_ring_role(struct net_bridge *br, 67262306a36Sopenharmony_ci struct br_mrp_ring_role *role) 67362306a36Sopenharmony_ci{ 67462306a36Sopenharmony_ci struct br_mrp *mrp = br_mrp_find_id(br, role->ring_id); 67562306a36Sopenharmony_ci enum br_mrp_hw_support support; 67662306a36Sopenharmony_ci 67762306a36Sopenharmony_ci if (!mrp) 67862306a36Sopenharmony_ci return -EINVAL; 67962306a36Sopenharmony_ci 68062306a36Sopenharmony_ci mrp->ring_role = role->ring_role; 68162306a36Sopenharmony_ci 68262306a36Sopenharmony_ci /* If there is an error just bailed out */ 68362306a36Sopenharmony_ci support = br_mrp_switchdev_set_ring_role(br, mrp, role->ring_role); 68462306a36Sopenharmony_ci if (support == BR_MRP_NONE) 68562306a36Sopenharmony_ci return -EOPNOTSUPP; 68662306a36Sopenharmony_ci 68762306a36Sopenharmony_ci /* Now detect if the HW actually applied the role or not. If the HW 68862306a36Sopenharmony_ci * applied the role it means that the SW will not to do those operations 68962306a36Sopenharmony_ci * anymore. For example if the role ir MRM then the HW will notify the 69062306a36Sopenharmony_ci * SW when ring is open, but if the is not pushed to the HW the SW will 69162306a36Sopenharmony_ci * need to detect when the ring is open 69262306a36Sopenharmony_ci */ 69362306a36Sopenharmony_ci mrp->ring_role_offloaded = support == BR_MRP_SW ? 0 : 1; 69462306a36Sopenharmony_ci 69562306a36Sopenharmony_ci return 0; 69662306a36Sopenharmony_ci} 69762306a36Sopenharmony_ci 69862306a36Sopenharmony_ci/* Start to generate or monitor MRP test frames, the frames are generated by 69962306a36Sopenharmony_ci * HW and if it fails, they are generated by the SW. 70062306a36Sopenharmony_ci * note: already called with rtnl_lock 70162306a36Sopenharmony_ci */ 70262306a36Sopenharmony_ciint br_mrp_start_test(struct net_bridge *br, 70362306a36Sopenharmony_ci struct br_mrp_start_test *test) 70462306a36Sopenharmony_ci{ 70562306a36Sopenharmony_ci struct br_mrp *mrp = br_mrp_find_id(br, test->ring_id); 70662306a36Sopenharmony_ci enum br_mrp_hw_support support; 70762306a36Sopenharmony_ci 70862306a36Sopenharmony_ci if (!mrp) 70962306a36Sopenharmony_ci return -EINVAL; 71062306a36Sopenharmony_ci 71162306a36Sopenharmony_ci /* Try to push it to the HW and if it fails then continue with SW 71262306a36Sopenharmony_ci * implementation and if that also fails then return error. 71362306a36Sopenharmony_ci */ 71462306a36Sopenharmony_ci support = br_mrp_switchdev_send_ring_test(br, mrp, test->interval, 71562306a36Sopenharmony_ci test->max_miss, test->period, 71662306a36Sopenharmony_ci test->monitor); 71762306a36Sopenharmony_ci if (support == BR_MRP_NONE) 71862306a36Sopenharmony_ci return -EOPNOTSUPP; 71962306a36Sopenharmony_ci 72062306a36Sopenharmony_ci if (support == BR_MRP_HW) 72162306a36Sopenharmony_ci return 0; 72262306a36Sopenharmony_ci 72362306a36Sopenharmony_ci mrp->test_interval = test->interval; 72462306a36Sopenharmony_ci mrp->test_end = jiffies + usecs_to_jiffies(test->period); 72562306a36Sopenharmony_ci mrp->test_max_miss = test->max_miss; 72662306a36Sopenharmony_ci mrp->test_monitor = test->monitor; 72762306a36Sopenharmony_ci mrp->test_count_miss = 0; 72862306a36Sopenharmony_ci queue_delayed_work(system_wq, &mrp->test_work, 72962306a36Sopenharmony_ci usecs_to_jiffies(test->interval)); 73062306a36Sopenharmony_ci 73162306a36Sopenharmony_ci return 0; 73262306a36Sopenharmony_ci} 73362306a36Sopenharmony_ci 73462306a36Sopenharmony_ci/* Set in state, int state can be only Open or Closed 73562306a36Sopenharmony_ci * note: already called with rtnl_lock 73662306a36Sopenharmony_ci */ 73762306a36Sopenharmony_ciint br_mrp_set_in_state(struct net_bridge *br, struct br_mrp_in_state *state) 73862306a36Sopenharmony_ci{ 73962306a36Sopenharmony_ci struct br_mrp *mrp = br_mrp_find_in_id(br, state->in_id); 74062306a36Sopenharmony_ci 74162306a36Sopenharmony_ci if (!mrp) 74262306a36Sopenharmony_ci return -EINVAL; 74362306a36Sopenharmony_ci 74462306a36Sopenharmony_ci if (mrp->in_state != state->in_state) 74562306a36Sopenharmony_ci mrp->in_transitions++; 74662306a36Sopenharmony_ci 74762306a36Sopenharmony_ci mrp->in_state = state->in_state; 74862306a36Sopenharmony_ci 74962306a36Sopenharmony_ci br_mrp_switchdev_set_in_state(br, mrp, state->in_state); 75062306a36Sopenharmony_ci 75162306a36Sopenharmony_ci return 0; 75262306a36Sopenharmony_ci} 75362306a36Sopenharmony_ci 75462306a36Sopenharmony_ci/* Set in role, in role can be only MIM(Media Interconnection Manager) or 75562306a36Sopenharmony_ci * MIC(Media Interconnection Client). 75662306a36Sopenharmony_ci * note: already called with rtnl_lock 75762306a36Sopenharmony_ci */ 75862306a36Sopenharmony_ciint br_mrp_set_in_role(struct net_bridge *br, struct br_mrp_in_role *role) 75962306a36Sopenharmony_ci{ 76062306a36Sopenharmony_ci struct br_mrp *mrp = br_mrp_find_id(br, role->ring_id); 76162306a36Sopenharmony_ci enum br_mrp_hw_support support; 76262306a36Sopenharmony_ci struct net_bridge_port *p; 76362306a36Sopenharmony_ci 76462306a36Sopenharmony_ci if (!mrp) 76562306a36Sopenharmony_ci return -EINVAL; 76662306a36Sopenharmony_ci 76762306a36Sopenharmony_ci if (!br_mrp_get_port(br, role->i_ifindex)) 76862306a36Sopenharmony_ci return -EINVAL; 76962306a36Sopenharmony_ci 77062306a36Sopenharmony_ci if (role->in_role == BR_MRP_IN_ROLE_DISABLED) { 77162306a36Sopenharmony_ci u8 state; 77262306a36Sopenharmony_ci 77362306a36Sopenharmony_ci /* It is not allowed to disable a port that doesn't exist */ 77462306a36Sopenharmony_ci p = rtnl_dereference(mrp->i_port); 77562306a36Sopenharmony_ci if (!p) 77662306a36Sopenharmony_ci return -EINVAL; 77762306a36Sopenharmony_ci 77862306a36Sopenharmony_ci /* Stop the generating MRP_InTest frames */ 77962306a36Sopenharmony_ci cancel_delayed_work_sync(&mrp->in_test_work); 78062306a36Sopenharmony_ci br_mrp_switchdev_send_in_test(br, mrp, 0, 0, 0); 78162306a36Sopenharmony_ci 78262306a36Sopenharmony_ci /* Remove the port */ 78362306a36Sopenharmony_ci spin_lock_bh(&br->lock); 78462306a36Sopenharmony_ci state = netif_running(br->dev) ? 78562306a36Sopenharmony_ci BR_STATE_FORWARDING : BR_STATE_DISABLED; 78662306a36Sopenharmony_ci p->state = state; 78762306a36Sopenharmony_ci p->flags &= ~BR_MRP_AWARE; 78862306a36Sopenharmony_ci spin_unlock_bh(&br->lock); 78962306a36Sopenharmony_ci br_mrp_port_switchdev_set_state(p, state); 79062306a36Sopenharmony_ci rcu_assign_pointer(mrp->i_port, NULL); 79162306a36Sopenharmony_ci 79262306a36Sopenharmony_ci mrp->in_role = role->in_role; 79362306a36Sopenharmony_ci mrp->in_id = 0; 79462306a36Sopenharmony_ci 79562306a36Sopenharmony_ci return 0; 79662306a36Sopenharmony_ci } 79762306a36Sopenharmony_ci 79862306a36Sopenharmony_ci /* It is not possible to have the same port part of multiple rings */ 79962306a36Sopenharmony_ci if (!br_mrp_unique_ifindex(br, role->i_ifindex)) 80062306a36Sopenharmony_ci return -EINVAL; 80162306a36Sopenharmony_ci 80262306a36Sopenharmony_ci /* It is not allowed to set a different interconnect port if the mrp 80362306a36Sopenharmony_ci * instance has already one. First it needs to be disabled and after 80462306a36Sopenharmony_ci * that set the new port 80562306a36Sopenharmony_ci */ 80662306a36Sopenharmony_ci if (rcu_access_pointer(mrp->i_port)) 80762306a36Sopenharmony_ci return -EINVAL; 80862306a36Sopenharmony_ci 80962306a36Sopenharmony_ci p = br_mrp_get_port(br, role->i_ifindex); 81062306a36Sopenharmony_ci spin_lock_bh(&br->lock); 81162306a36Sopenharmony_ci p->state = BR_STATE_FORWARDING; 81262306a36Sopenharmony_ci p->flags |= BR_MRP_AWARE; 81362306a36Sopenharmony_ci spin_unlock_bh(&br->lock); 81462306a36Sopenharmony_ci rcu_assign_pointer(mrp->i_port, p); 81562306a36Sopenharmony_ci 81662306a36Sopenharmony_ci mrp->in_role = role->in_role; 81762306a36Sopenharmony_ci mrp->in_id = role->in_id; 81862306a36Sopenharmony_ci 81962306a36Sopenharmony_ci /* If there is an error just bailed out */ 82062306a36Sopenharmony_ci support = br_mrp_switchdev_set_in_role(br, mrp, role->in_id, 82162306a36Sopenharmony_ci role->ring_id, role->in_role); 82262306a36Sopenharmony_ci if (support == BR_MRP_NONE) 82362306a36Sopenharmony_ci return -EOPNOTSUPP; 82462306a36Sopenharmony_ci 82562306a36Sopenharmony_ci /* Now detect if the HW actually applied the role or not. If the HW 82662306a36Sopenharmony_ci * applied the role it means that the SW will not to do those operations 82762306a36Sopenharmony_ci * anymore. For example if the role is MIM then the HW will notify the 82862306a36Sopenharmony_ci * SW when interconnect ring is open, but if the is not pushed to the HW 82962306a36Sopenharmony_ci * the SW will need to detect when the interconnect ring is open. 83062306a36Sopenharmony_ci */ 83162306a36Sopenharmony_ci mrp->in_role_offloaded = support == BR_MRP_SW ? 0 : 1; 83262306a36Sopenharmony_ci 83362306a36Sopenharmony_ci return 0; 83462306a36Sopenharmony_ci} 83562306a36Sopenharmony_ci 83662306a36Sopenharmony_ci/* Start to generate MRP_InTest frames, the frames are generated by 83762306a36Sopenharmony_ci * HW and if it fails, they are generated by the SW. 83862306a36Sopenharmony_ci * note: already called with rtnl_lock 83962306a36Sopenharmony_ci */ 84062306a36Sopenharmony_ciint br_mrp_start_in_test(struct net_bridge *br, 84162306a36Sopenharmony_ci struct br_mrp_start_in_test *in_test) 84262306a36Sopenharmony_ci{ 84362306a36Sopenharmony_ci struct br_mrp *mrp = br_mrp_find_in_id(br, in_test->in_id); 84462306a36Sopenharmony_ci enum br_mrp_hw_support support; 84562306a36Sopenharmony_ci 84662306a36Sopenharmony_ci if (!mrp) 84762306a36Sopenharmony_ci return -EINVAL; 84862306a36Sopenharmony_ci 84962306a36Sopenharmony_ci if (mrp->in_role != BR_MRP_IN_ROLE_MIM) 85062306a36Sopenharmony_ci return -EINVAL; 85162306a36Sopenharmony_ci 85262306a36Sopenharmony_ci /* Try to push it to the HW and if it fails then continue with SW 85362306a36Sopenharmony_ci * implementation and if that also fails then return error. 85462306a36Sopenharmony_ci */ 85562306a36Sopenharmony_ci support = br_mrp_switchdev_send_in_test(br, mrp, in_test->interval, 85662306a36Sopenharmony_ci in_test->max_miss, 85762306a36Sopenharmony_ci in_test->period); 85862306a36Sopenharmony_ci if (support == BR_MRP_NONE) 85962306a36Sopenharmony_ci return -EOPNOTSUPP; 86062306a36Sopenharmony_ci 86162306a36Sopenharmony_ci if (support == BR_MRP_HW) 86262306a36Sopenharmony_ci return 0; 86362306a36Sopenharmony_ci 86462306a36Sopenharmony_ci mrp->in_test_interval = in_test->interval; 86562306a36Sopenharmony_ci mrp->in_test_end = jiffies + usecs_to_jiffies(in_test->period); 86662306a36Sopenharmony_ci mrp->in_test_max_miss = in_test->max_miss; 86762306a36Sopenharmony_ci mrp->in_test_count_miss = 0; 86862306a36Sopenharmony_ci queue_delayed_work(system_wq, &mrp->in_test_work, 86962306a36Sopenharmony_ci usecs_to_jiffies(in_test->interval)); 87062306a36Sopenharmony_ci 87162306a36Sopenharmony_ci return 0; 87262306a36Sopenharmony_ci} 87362306a36Sopenharmony_ci 87462306a36Sopenharmony_ci/* Determine if the frame type is a ring frame */ 87562306a36Sopenharmony_cistatic bool br_mrp_ring_frame(struct sk_buff *skb) 87662306a36Sopenharmony_ci{ 87762306a36Sopenharmony_ci const struct br_mrp_tlv_hdr *hdr; 87862306a36Sopenharmony_ci struct br_mrp_tlv_hdr _hdr; 87962306a36Sopenharmony_ci 88062306a36Sopenharmony_ci hdr = skb_header_pointer(skb, sizeof(uint16_t), sizeof(_hdr), &_hdr); 88162306a36Sopenharmony_ci if (!hdr) 88262306a36Sopenharmony_ci return false; 88362306a36Sopenharmony_ci 88462306a36Sopenharmony_ci if (hdr->type == BR_MRP_TLV_HEADER_RING_TEST || 88562306a36Sopenharmony_ci hdr->type == BR_MRP_TLV_HEADER_RING_TOPO || 88662306a36Sopenharmony_ci hdr->type == BR_MRP_TLV_HEADER_RING_LINK_DOWN || 88762306a36Sopenharmony_ci hdr->type == BR_MRP_TLV_HEADER_RING_LINK_UP || 88862306a36Sopenharmony_ci hdr->type == BR_MRP_TLV_HEADER_OPTION) 88962306a36Sopenharmony_ci return true; 89062306a36Sopenharmony_ci 89162306a36Sopenharmony_ci return false; 89262306a36Sopenharmony_ci} 89362306a36Sopenharmony_ci 89462306a36Sopenharmony_ci/* Determine if the frame type is an interconnect frame */ 89562306a36Sopenharmony_cistatic bool br_mrp_in_frame(struct sk_buff *skb) 89662306a36Sopenharmony_ci{ 89762306a36Sopenharmony_ci const struct br_mrp_tlv_hdr *hdr; 89862306a36Sopenharmony_ci struct br_mrp_tlv_hdr _hdr; 89962306a36Sopenharmony_ci 90062306a36Sopenharmony_ci hdr = skb_header_pointer(skb, sizeof(uint16_t), sizeof(_hdr), &_hdr); 90162306a36Sopenharmony_ci if (!hdr) 90262306a36Sopenharmony_ci return false; 90362306a36Sopenharmony_ci 90462306a36Sopenharmony_ci if (hdr->type == BR_MRP_TLV_HEADER_IN_TEST || 90562306a36Sopenharmony_ci hdr->type == BR_MRP_TLV_HEADER_IN_TOPO || 90662306a36Sopenharmony_ci hdr->type == BR_MRP_TLV_HEADER_IN_LINK_DOWN || 90762306a36Sopenharmony_ci hdr->type == BR_MRP_TLV_HEADER_IN_LINK_UP || 90862306a36Sopenharmony_ci hdr->type == BR_MRP_TLV_HEADER_IN_LINK_STATUS) 90962306a36Sopenharmony_ci return true; 91062306a36Sopenharmony_ci 91162306a36Sopenharmony_ci return false; 91262306a36Sopenharmony_ci} 91362306a36Sopenharmony_ci 91462306a36Sopenharmony_ci/* Process only MRP Test frame. All the other MRP frames are processed by 91562306a36Sopenharmony_ci * userspace application 91662306a36Sopenharmony_ci * note: already called with rcu_read_lock 91762306a36Sopenharmony_ci */ 91862306a36Sopenharmony_cistatic void br_mrp_mrm_process(struct br_mrp *mrp, struct net_bridge_port *port, 91962306a36Sopenharmony_ci struct sk_buff *skb) 92062306a36Sopenharmony_ci{ 92162306a36Sopenharmony_ci const struct br_mrp_tlv_hdr *hdr; 92262306a36Sopenharmony_ci struct br_mrp_tlv_hdr _hdr; 92362306a36Sopenharmony_ci 92462306a36Sopenharmony_ci /* Each MRP header starts with a version field which is 16 bits. 92562306a36Sopenharmony_ci * Therefore skip the version and get directly the TLV header. 92662306a36Sopenharmony_ci */ 92762306a36Sopenharmony_ci hdr = skb_header_pointer(skb, sizeof(uint16_t), sizeof(_hdr), &_hdr); 92862306a36Sopenharmony_ci if (!hdr) 92962306a36Sopenharmony_ci return; 93062306a36Sopenharmony_ci 93162306a36Sopenharmony_ci if (hdr->type != BR_MRP_TLV_HEADER_RING_TEST) 93262306a36Sopenharmony_ci return; 93362306a36Sopenharmony_ci 93462306a36Sopenharmony_ci mrp->test_count_miss = 0; 93562306a36Sopenharmony_ci 93662306a36Sopenharmony_ci /* Notify the userspace that the ring is closed only when the ring is 93762306a36Sopenharmony_ci * not closed 93862306a36Sopenharmony_ci */ 93962306a36Sopenharmony_ci if (mrp->ring_state != BR_MRP_RING_STATE_CLOSED) 94062306a36Sopenharmony_ci br_mrp_ring_port_open(port->dev, false); 94162306a36Sopenharmony_ci} 94262306a36Sopenharmony_ci 94362306a36Sopenharmony_ci/* Determine if the test hdr has a better priority than the node */ 94462306a36Sopenharmony_cistatic bool br_mrp_test_better_than_own(struct br_mrp *mrp, 94562306a36Sopenharmony_ci struct net_bridge *br, 94662306a36Sopenharmony_ci const struct br_mrp_ring_test_hdr *hdr) 94762306a36Sopenharmony_ci{ 94862306a36Sopenharmony_ci u16 prio = be16_to_cpu(hdr->prio); 94962306a36Sopenharmony_ci 95062306a36Sopenharmony_ci if (prio < mrp->prio || 95162306a36Sopenharmony_ci (prio == mrp->prio && 95262306a36Sopenharmony_ci ether_addr_to_u64(hdr->sa) < ether_addr_to_u64(br->dev->dev_addr))) 95362306a36Sopenharmony_ci return true; 95462306a36Sopenharmony_ci 95562306a36Sopenharmony_ci return false; 95662306a36Sopenharmony_ci} 95762306a36Sopenharmony_ci 95862306a36Sopenharmony_ci/* Process only MRP Test frame. All the other MRP frames are processed by 95962306a36Sopenharmony_ci * userspace application 96062306a36Sopenharmony_ci * note: already called with rcu_read_lock 96162306a36Sopenharmony_ci */ 96262306a36Sopenharmony_cistatic void br_mrp_mra_process(struct br_mrp *mrp, struct net_bridge *br, 96362306a36Sopenharmony_ci struct net_bridge_port *port, 96462306a36Sopenharmony_ci struct sk_buff *skb) 96562306a36Sopenharmony_ci{ 96662306a36Sopenharmony_ci const struct br_mrp_ring_test_hdr *test_hdr; 96762306a36Sopenharmony_ci struct br_mrp_ring_test_hdr _test_hdr; 96862306a36Sopenharmony_ci const struct br_mrp_tlv_hdr *hdr; 96962306a36Sopenharmony_ci struct br_mrp_tlv_hdr _hdr; 97062306a36Sopenharmony_ci 97162306a36Sopenharmony_ci /* Each MRP header starts with a version field which is 16 bits. 97262306a36Sopenharmony_ci * Therefore skip the version and get directly the TLV header. 97362306a36Sopenharmony_ci */ 97462306a36Sopenharmony_ci hdr = skb_header_pointer(skb, sizeof(uint16_t), sizeof(_hdr), &_hdr); 97562306a36Sopenharmony_ci if (!hdr) 97662306a36Sopenharmony_ci return; 97762306a36Sopenharmony_ci 97862306a36Sopenharmony_ci if (hdr->type != BR_MRP_TLV_HEADER_RING_TEST) 97962306a36Sopenharmony_ci return; 98062306a36Sopenharmony_ci 98162306a36Sopenharmony_ci test_hdr = skb_header_pointer(skb, sizeof(uint16_t) + sizeof(_hdr), 98262306a36Sopenharmony_ci sizeof(_test_hdr), &_test_hdr); 98362306a36Sopenharmony_ci if (!test_hdr) 98462306a36Sopenharmony_ci return; 98562306a36Sopenharmony_ci 98662306a36Sopenharmony_ci /* Only frames that have a better priority than the node will 98762306a36Sopenharmony_ci * clear the miss counter because otherwise the node will need to behave 98862306a36Sopenharmony_ci * as MRM. 98962306a36Sopenharmony_ci */ 99062306a36Sopenharmony_ci if (br_mrp_test_better_than_own(mrp, br, test_hdr)) 99162306a36Sopenharmony_ci mrp->test_count_miss = 0; 99262306a36Sopenharmony_ci} 99362306a36Sopenharmony_ci 99462306a36Sopenharmony_ci/* Process only MRP InTest frame. All the other MRP frames are processed by 99562306a36Sopenharmony_ci * userspace application 99662306a36Sopenharmony_ci * note: already called with rcu_read_lock 99762306a36Sopenharmony_ci */ 99862306a36Sopenharmony_cistatic bool br_mrp_mim_process(struct br_mrp *mrp, struct net_bridge_port *port, 99962306a36Sopenharmony_ci struct sk_buff *skb) 100062306a36Sopenharmony_ci{ 100162306a36Sopenharmony_ci const struct br_mrp_in_test_hdr *in_hdr; 100262306a36Sopenharmony_ci struct br_mrp_in_test_hdr _in_hdr; 100362306a36Sopenharmony_ci const struct br_mrp_tlv_hdr *hdr; 100462306a36Sopenharmony_ci struct br_mrp_tlv_hdr _hdr; 100562306a36Sopenharmony_ci 100662306a36Sopenharmony_ci /* Each MRP header starts with a version field which is 16 bits. 100762306a36Sopenharmony_ci * Therefore skip the version and get directly the TLV header. 100862306a36Sopenharmony_ci */ 100962306a36Sopenharmony_ci hdr = skb_header_pointer(skb, sizeof(uint16_t), sizeof(_hdr), &_hdr); 101062306a36Sopenharmony_ci if (!hdr) 101162306a36Sopenharmony_ci return false; 101262306a36Sopenharmony_ci 101362306a36Sopenharmony_ci /* The check for InTest frame type was already done */ 101462306a36Sopenharmony_ci in_hdr = skb_header_pointer(skb, sizeof(uint16_t) + sizeof(_hdr), 101562306a36Sopenharmony_ci sizeof(_in_hdr), &_in_hdr); 101662306a36Sopenharmony_ci if (!in_hdr) 101762306a36Sopenharmony_ci return false; 101862306a36Sopenharmony_ci 101962306a36Sopenharmony_ci /* It needs to process only it's own InTest frames. */ 102062306a36Sopenharmony_ci if (mrp->in_id != ntohs(in_hdr->id)) 102162306a36Sopenharmony_ci return false; 102262306a36Sopenharmony_ci 102362306a36Sopenharmony_ci mrp->in_test_count_miss = 0; 102462306a36Sopenharmony_ci 102562306a36Sopenharmony_ci /* Notify the userspace that the ring is closed only when the ring is 102662306a36Sopenharmony_ci * not closed 102762306a36Sopenharmony_ci */ 102862306a36Sopenharmony_ci if (mrp->in_state != BR_MRP_IN_STATE_CLOSED) 102962306a36Sopenharmony_ci br_mrp_in_port_open(port->dev, false); 103062306a36Sopenharmony_ci 103162306a36Sopenharmony_ci return true; 103262306a36Sopenharmony_ci} 103362306a36Sopenharmony_ci 103462306a36Sopenharmony_ci/* Get the MRP frame type 103562306a36Sopenharmony_ci * note: already called with rcu_read_lock 103662306a36Sopenharmony_ci */ 103762306a36Sopenharmony_cistatic u8 br_mrp_get_frame_type(struct sk_buff *skb) 103862306a36Sopenharmony_ci{ 103962306a36Sopenharmony_ci const struct br_mrp_tlv_hdr *hdr; 104062306a36Sopenharmony_ci struct br_mrp_tlv_hdr _hdr; 104162306a36Sopenharmony_ci 104262306a36Sopenharmony_ci /* Each MRP header starts with a version field which is 16 bits. 104362306a36Sopenharmony_ci * Therefore skip the version and get directly the TLV header. 104462306a36Sopenharmony_ci */ 104562306a36Sopenharmony_ci hdr = skb_header_pointer(skb, sizeof(uint16_t), sizeof(_hdr), &_hdr); 104662306a36Sopenharmony_ci if (!hdr) 104762306a36Sopenharmony_ci return 0xff; 104862306a36Sopenharmony_ci 104962306a36Sopenharmony_ci return hdr->type; 105062306a36Sopenharmony_ci} 105162306a36Sopenharmony_ci 105262306a36Sopenharmony_cistatic bool br_mrp_mrm_behaviour(struct br_mrp *mrp) 105362306a36Sopenharmony_ci{ 105462306a36Sopenharmony_ci if (mrp->ring_role == BR_MRP_RING_ROLE_MRM || 105562306a36Sopenharmony_ci (mrp->ring_role == BR_MRP_RING_ROLE_MRA && !mrp->test_monitor)) 105662306a36Sopenharmony_ci return true; 105762306a36Sopenharmony_ci 105862306a36Sopenharmony_ci return false; 105962306a36Sopenharmony_ci} 106062306a36Sopenharmony_ci 106162306a36Sopenharmony_cistatic bool br_mrp_mrc_behaviour(struct br_mrp *mrp) 106262306a36Sopenharmony_ci{ 106362306a36Sopenharmony_ci if (mrp->ring_role == BR_MRP_RING_ROLE_MRC || 106462306a36Sopenharmony_ci (mrp->ring_role == BR_MRP_RING_ROLE_MRA && mrp->test_monitor)) 106562306a36Sopenharmony_ci return true; 106662306a36Sopenharmony_ci 106762306a36Sopenharmony_ci return false; 106862306a36Sopenharmony_ci} 106962306a36Sopenharmony_ci 107062306a36Sopenharmony_ci/* This will just forward the frame to the other mrp ring ports, depending on 107162306a36Sopenharmony_ci * the frame type, ring role and interconnect role 107262306a36Sopenharmony_ci * note: already called with rcu_read_lock 107362306a36Sopenharmony_ci */ 107462306a36Sopenharmony_cistatic int br_mrp_rcv(struct net_bridge_port *p, 107562306a36Sopenharmony_ci struct sk_buff *skb, struct net_device *dev) 107662306a36Sopenharmony_ci{ 107762306a36Sopenharmony_ci struct net_bridge_port *p_port, *s_port, *i_port = NULL; 107862306a36Sopenharmony_ci struct net_bridge_port *p_dst, *s_dst, *i_dst = NULL; 107962306a36Sopenharmony_ci struct net_bridge *br; 108062306a36Sopenharmony_ci struct br_mrp *mrp; 108162306a36Sopenharmony_ci 108262306a36Sopenharmony_ci /* If port is disabled don't accept any frames */ 108362306a36Sopenharmony_ci if (p->state == BR_STATE_DISABLED) 108462306a36Sopenharmony_ci return 0; 108562306a36Sopenharmony_ci 108662306a36Sopenharmony_ci br = p->br; 108762306a36Sopenharmony_ci mrp = br_mrp_find_port(br, p); 108862306a36Sopenharmony_ci if (unlikely(!mrp)) 108962306a36Sopenharmony_ci return 0; 109062306a36Sopenharmony_ci 109162306a36Sopenharmony_ci p_port = rcu_dereference(mrp->p_port); 109262306a36Sopenharmony_ci if (!p_port) 109362306a36Sopenharmony_ci return 0; 109462306a36Sopenharmony_ci p_dst = p_port; 109562306a36Sopenharmony_ci 109662306a36Sopenharmony_ci s_port = rcu_dereference(mrp->s_port); 109762306a36Sopenharmony_ci if (!s_port) 109862306a36Sopenharmony_ci return 0; 109962306a36Sopenharmony_ci s_dst = s_port; 110062306a36Sopenharmony_ci 110162306a36Sopenharmony_ci /* If the frame is a ring frame then it is not required to check the 110262306a36Sopenharmony_ci * interconnect role and ports to process or forward the frame 110362306a36Sopenharmony_ci */ 110462306a36Sopenharmony_ci if (br_mrp_ring_frame(skb)) { 110562306a36Sopenharmony_ci /* If the role is MRM then don't forward the frames */ 110662306a36Sopenharmony_ci if (mrp->ring_role == BR_MRP_RING_ROLE_MRM) { 110762306a36Sopenharmony_ci br_mrp_mrm_process(mrp, p, skb); 110862306a36Sopenharmony_ci goto no_forward; 110962306a36Sopenharmony_ci } 111062306a36Sopenharmony_ci 111162306a36Sopenharmony_ci /* If the role is MRA then don't forward the frames if it 111262306a36Sopenharmony_ci * behaves as MRM node 111362306a36Sopenharmony_ci */ 111462306a36Sopenharmony_ci if (mrp->ring_role == BR_MRP_RING_ROLE_MRA) { 111562306a36Sopenharmony_ci if (!mrp->test_monitor) { 111662306a36Sopenharmony_ci br_mrp_mrm_process(mrp, p, skb); 111762306a36Sopenharmony_ci goto no_forward; 111862306a36Sopenharmony_ci } 111962306a36Sopenharmony_ci 112062306a36Sopenharmony_ci br_mrp_mra_process(mrp, br, p, skb); 112162306a36Sopenharmony_ci } 112262306a36Sopenharmony_ci 112362306a36Sopenharmony_ci goto forward; 112462306a36Sopenharmony_ci } 112562306a36Sopenharmony_ci 112662306a36Sopenharmony_ci if (br_mrp_in_frame(skb)) { 112762306a36Sopenharmony_ci u8 in_type = br_mrp_get_frame_type(skb); 112862306a36Sopenharmony_ci 112962306a36Sopenharmony_ci i_port = rcu_dereference(mrp->i_port); 113062306a36Sopenharmony_ci i_dst = i_port; 113162306a36Sopenharmony_ci 113262306a36Sopenharmony_ci /* If the ring port is in block state it should not forward 113362306a36Sopenharmony_ci * In_Test frames 113462306a36Sopenharmony_ci */ 113562306a36Sopenharmony_ci if (br_mrp_is_ring_port(p_port, s_port, p) && 113662306a36Sopenharmony_ci p->state == BR_STATE_BLOCKING && 113762306a36Sopenharmony_ci in_type == BR_MRP_TLV_HEADER_IN_TEST) 113862306a36Sopenharmony_ci goto no_forward; 113962306a36Sopenharmony_ci 114062306a36Sopenharmony_ci /* Nodes that behaves as MRM needs to stop forwarding the 114162306a36Sopenharmony_ci * frames in case the ring is closed, otherwise will be a loop. 114262306a36Sopenharmony_ci * In this case the frame is no forward between the ring ports. 114362306a36Sopenharmony_ci */ 114462306a36Sopenharmony_ci if (br_mrp_mrm_behaviour(mrp) && 114562306a36Sopenharmony_ci br_mrp_is_ring_port(p_port, s_port, p) && 114662306a36Sopenharmony_ci (s_port->state != BR_STATE_FORWARDING || 114762306a36Sopenharmony_ci p_port->state != BR_STATE_FORWARDING)) { 114862306a36Sopenharmony_ci p_dst = NULL; 114962306a36Sopenharmony_ci s_dst = NULL; 115062306a36Sopenharmony_ci } 115162306a36Sopenharmony_ci 115262306a36Sopenharmony_ci /* A node that behaves as MRC and doesn't have a interconnect 115362306a36Sopenharmony_ci * role then it should forward all frames between the ring ports 115462306a36Sopenharmony_ci * because it doesn't have an interconnect port 115562306a36Sopenharmony_ci */ 115662306a36Sopenharmony_ci if (br_mrp_mrc_behaviour(mrp) && 115762306a36Sopenharmony_ci mrp->in_role == BR_MRP_IN_ROLE_DISABLED) 115862306a36Sopenharmony_ci goto forward; 115962306a36Sopenharmony_ci 116062306a36Sopenharmony_ci if (mrp->in_role == BR_MRP_IN_ROLE_MIM) { 116162306a36Sopenharmony_ci if (in_type == BR_MRP_TLV_HEADER_IN_TEST) { 116262306a36Sopenharmony_ci /* MIM should not forward it's own InTest 116362306a36Sopenharmony_ci * frames 116462306a36Sopenharmony_ci */ 116562306a36Sopenharmony_ci if (br_mrp_mim_process(mrp, p, skb)) { 116662306a36Sopenharmony_ci goto no_forward; 116762306a36Sopenharmony_ci } else { 116862306a36Sopenharmony_ci if (br_mrp_is_ring_port(p_port, s_port, 116962306a36Sopenharmony_ci p)) 117062306a36Sopenharmony_ci i_dst = NULL; 117162306a36Sopenharmony_ci 117262306a36Sopenharmony_ci if (br_mrp_is_in_port(i_port, p)) 117362306a36Sopenharmony_ci goto no_forward; 117462306a36Sopenharmony_ci } 117562306a36Sopenharmony_ci } else { 117662306a36Sopenharmony_ci /* MIM should forward IntLinkChange/Status and 117762306a36Sopenharmony_ci * IntTopoChange between ring ports but MIM 117862306a36Sopenharmony_ci * should not forward IntLinkChange/Status and 117962306a36Sopenharmony_ci * IntTopoChange if the frame was received at 118062306a36Sopenharmony_ci * the interconnect port 118162306a36Sopenharmony_ci */ 118262306a36Sopenharmony_ci if (br_mrp_is_ring_port(p_port, s_port, p)) 118362306a36Sopenharmony_ci i_dst = NULL; 118462306a36Sopenharmony_ci 118562306a36Sopenharmony_ci if (br_mrp_is_in_port(i_port, p)) 118662306a36Sopenharmony_ci goto no_forward; 118762306a36Sopenharmony_ci } 118862306a36Sopenharmony_ci } 118962306a36Sopenharmony_ci 119062306a36Sopenharmony_ci if (mrp->in_role == BR_MRP_IN_ROLE_MIC) { 119162306a36Sopenharmony_ci /* MIC should forward InTest frames on all ports 119262306a36Sopenharmony_ci * regardless of the received port 119362306a36Sopenharmony_ci */ 119462306a36Sopenharmony_ci if (in_type == BR_MRP_TLV_HEADER_IN_TEST) 119562306a36Sopenharmony_ci goto forward; 119662306a36Sopenharmony_ci 119762306a36Sopenharmony_ci /* MIC should forward IntLinkChange frames only if they 119862306a36Sopenharmony_ci * are received on ring ports to all the ports 119962306a36Sopenharmony_ci */ 120062306a36Sopenharmony_ci if (br_mrp_is_ring_port(p_port, s_port, p) && 120162306a36Sopenharmony_ci (in_type == BR_MRP_TLV_HEADER_IN_LINK_UP || 120262306a36Sopenharmony_ci in_type == BR_MRP_TLV_HEADER_IN_LINK_DOWN)) 120362306a36Sopenharmony_ci goto forward; 120462306a36Sopenharmony_ci 120562306a36Sopenharmony_ci /* MIC should forward IntLinkStatus frames only to 120662306a36Sopenharmony_ci * interconnect port if it was received on a ring port. 120762306a36Sopenharmony_ci * If it is received on interconnect port then, it 120862306a36Sopenharmony_ci * should be forward on both ring ports 120962306a36Sopenharmony_ci */ 121062306a36Sopenharmony_ci if (br_mrp_is_ring_port(p_port, s_port, p) && 121162306a36Sopenharmony_ci in_type == BR_MRP_TLV_HEADER_IN_LINK_STATUS) { 121262306a36Sopenharmony_ci p_dst = NULL; 121362306a36Sopenharmony_ci s_dst = NULL; 121462306a36Sopenharmony_ci } 121562306a36Sopenharmony_ci 121662306a36Sopenharmony_ci /* Should forward the InTopo frames only between the 121762306a36Sopenharmony_ci * ring ports 121862306a36Sopenharmony_ci */ 121962306a36Sopenharmony_ci if (in_type == BR_MRP_TLV_HEADER_IN_TOPO) { 122062306a36Sopenharmony_ci i_dst = NULL; 122162306a36Sopenharmony_ci goto forward; 122262306a36Sopenharmony_ci } 122362306a36Sopenharmony_ci 122462306a36Sopenharmony_ci /* In all the other cases don't forward the frames */ 122562306a36Sopenharmony_ci goto no_forward; 122662306a36Sopenharmony_ci } 122762306a36Sopenharmony_ci } 122862306a36Sopenharmony_ci 122962306a36Sopenharmony_ciforward: 123062306a36Sopenharmony_ci if (p_dst) 123162306a36Sopenharmony_ci br_forward(p_dst, skb, true, false); 123262306a36Sopenharmony_ci if (s_dst) 123362306a36Sopenharmony_ci br_forward(s_dst, skb, true, false); 123462306a36Sopenharmony_ci if (i_dst) 123562306a36Sopenharmony_ci br_forward(i_dst, skb, true, false); 123662306a36Sopenharmony_ci 123762306a36Sopenharmony_cino_forward: 123862306a36Sopenharmony_ci return 1; 123962306a36Sopenharmony_ci} 124062306a36Sopenharmony_ci 124162306a36Sopenharmony_ci/* Check if the frame was received on a port that is part of MRP ring 124262306a36Sopenharmony_ci * and if the frame has MRP eth. In that case process the frame otherwise do 124362306a36Sopenharmony_ci * normal forwarding. 124462306a36Sopenharmony_ci * note: already called with rcu_read_lock 124562306a36Sopenharmony_ci */ 124662306a36Sopenharmony_cistatic int br_mrp_process(struct net_bridge_port *p, struct sk_buff *skb) 124762306a36Sopenharmony_ci{ 124862306a36Sopenharmony_ci /* If there is no MRP instance do normal forwarding */ 124962306a36Sopenharmony_ci if (likely(!(p->flags & BR_MRP_AWARE))) 125062306a36Sopenharmony_ci goto out; 125162306a36Sopenharmony_ci 125262306a36Sopenharmony_ci return br_mrp_rcv(p, skb, p->dev); 125362306a36Sopenharmony_ciout: 125462306a36Sopenharmony_ci return 0; 125562306a36Sopenharmony_ci} 125662306a36Sopenharmony_ci 125762306a36Sopenharmony_cibool br_mrp_enabled(struct net_bridge *br) 125862306a36Sopenharmony_ci{ 125962306a36Sopenharmony_ci return !hlist_empty(&br->mrp_list); 126062306a36Sopenharmony_ci} 1261