1 // SPDX-License-Identifier: GPL-2.0-or-later 2 /* 3 * DSA tagging protocol handling 4 * 5 * Copyright (c) 2008-2009 Marvell Semiconductor 6 * Copyright (c) 2013 Florian Fainelli <florian@openwrt.org> 7 * Copyright (c) 2016 Andrew Lunn <andrew@lunn.ch> 8 */ 9 10 #include <linux/netdevice.h> 11 #include <linux/ptp_classify.h> 12 #include <linux/skbuff.h> 13 #include <net/dsa.h> 14 #include <net/dst_metadata.h> 15 16 #include "tag.h" 17 #include "user.h" 18 19 static LIST_HEAD(dsa_tag_drivers_list); 20 static DEFINE_MUTEX(dsa_tag_drivers_lock); 21 22 /* Determine if we should defer delivery of skb until we have a rx timestamp. 23 * 24 * Called from dsa_switch_rcv. For now, this will only work if tagging is 25 * enabled on the switch. Normally the MAC driver would retrieve the hardware 26 * timestamp when it reads the packet out of the hardware. However in a DSA 27 * switch, the DSA driver owning the interface to which the packet is 28 * delivered is never notified unless we do so here. 29 */ 30 static bool dsa_skb_defer_rx_timestamp(struct dsa_user_priv *p, 31 struct sk_buff *skb) 32 { 33 struct dsa_switch *ds = p->dp->ds; 34 unsigned int type; 35 36 if (!ds->ops->port_rxtstamp) 37 return false; 38 39 if (skb_headroom(skb) < ETH_HLEN) 40 return false; 41 42 __skb_push(skb, ETH_HLEN); 43 44 type = ptp_classify_raw(skb); 45 46 __skb_pull(skb, ETH_HLEN); 47 48 if (type == PTP_CLASS_NONE) 49 return false; 50 51 return ds->ops->port_rxtstamp(ds, p->dp->index, skb, type); 52 } 53 54 static int dsa_switch_rcv(struct sk_buff *skb, struct net_device *dev, 55 struct packet_type *pt, struct net_device *unused) 56 { 57 struct metadata_dst *md_dst = skb_metadata_dst(skb); 58 struct dsa_port *cpu_dp = dev->dsa_ptr; 59 struct sk_buff *nskb = NULL; 60 struct dsa_user_priv *p; 61 62 if (unlikely(!cpu_dp)) { 63 kfree_skb(skb); 64 return 0; 65 } 66 67 skb = skb_unshare(skb, GFP_ATOMIC); 68 if (!skb) 69 return 0; 70 71 if (md_dst && md_dst->type == METADATA_HW_PORT_MUX) { 72 unsigned int port = md_dst->u.port_info.port_id; 73 74 skb_dst_drop(skb); 75 if (!skb_has_extensions(skb)) 76 skb->slow_gro = 0; 77 78 skb->dev = dsa_conduit_find_user(dev, 0, port); 79 if (likely(skb->dev)) { 80 dsa_default_offload_fwd_mark(skb); 81 nskb = skb; 82 } 83 } else { 84 nskb = cpu_dp->rcv(skb, dev); 85 } 86 87 if (!nskb) { 88 kfree_skb(skb); 89 return 0; 90 } 91 92 skb = nskb; 93 skb_push(skb, ETH_HLEN); 94 skb->pkt_type = PACKET_HOST; 95 skb->protocol = eth_type_trans(skb, skb->dev); 96 97 if (unlikely(!dsa_user_dev_check(skb->dev))) { 98 /* Packet is to be injected directly on an upper 99 * device, e.g. a team/bond, so skip all DSA-port 100 * specific actions. 101 */ 102 netif_rx(skb); 103 return 0; 104 } 105 106 p = netdev_priv(skb->dev); 107 108 if (unlikely(cpu_dp->ds->untag_bridge_pvid || 109 cpu_dp->ds->untag_vlan_aware_bridge_pvid)) { 110 nskb = dsa_software_vlan_untag(skb); 111 if (!nskb) { 112 kfree_skb(skb); 113 return 0; 114 } 115 skb = nskb; 116 } 117 118 dev_sw_netstats_rx_add(skb->dev, skb->len + ETH_HLEN); 119 120 if (dsa_skb_defer_rx_timestamp(p, skb)) 121 return 0; 122 123 gro_cells_receive(&p->gcells, skb); 124 125 return 0; 126 } 127 128 struct packet_type dsa_pack_type __read_mostly = { 129 .type = cpu_to_be16(ETH_P_XDSA), 130 .func = dsa_switch_rcv, 131 }; 132 133 static void dsa_tag_driver_register(struct dsa_tag_driver *dsa_tag_driver, 134 struct module *owner) 135 { 136 dsa_tag_driver->owner = owner; 137 138 mutex_lock(&dsa_tag_drivers_lock); 139 list_add_tail(&dsa_tag_driver->list, &dsa_tag_drivers_list); 140 mutex_unlock(&dsa_tag_drivers_lock); 141 } 142 143 void dsa_tag_drivers_register(struct dsa_tag_driver *dsa_tag_driver_array[], 144 unsigned int count, struct module *owner) 145 { 146 unsigned int i; 147 148 for (i = 0; i < count; i++) 149 dsa_tag_driver_register(dsa_tag_driver_array[i], owner); 150 } 151 152 static void dsa_tag_driver_unregister(struct dsa_tag_driver *dsa_tag_driver) 153 { 154 mutex_lock(&dsa_tag_drivers_lock); 155 list_del(&dsa_tag_driver->list); 156 mutex_unlock(&dsa_tag_drivers_lock); 157 } 158 EXPORT_SYMBOL_GPL(dsa_tag_drivers_register); 159 160 void dsa_tag_drivers_unregister(struct dsa_tag_driver *dsa_tag_driver_array[], 161 unsigned int count) 162 { 163 unsigned int i; 164 165 for (i = 0; i < count; i++) 166 dsa_tag_driver_unregister(dsa_tag_driver_array[i]); 167 } 168 EXPORT_SYMBOL_GPL(dsa_tag_drivers_unregister); 169 170 const char *dsa_tag_protocol_to_str(const struct dsa_device_ops *ops) 171 { 172 return ops->name; 173 }; 174 175 /* Function takes a reference on the module owning the tagger, 176 * so dsa_tag_driver_put must be called afterwards. 177 */ 178 const struct dsa_device_ops *dsa_tag_driver_get_by_name(const char *name) 179 { 180 const struct dsa_device_ops *ops = ERR_PTR(-ENOPROTOOPT); 181 struct dsa_tag_driver *dsa_tag_driver; 182 183 request_module("%s%s", DSA_TAG_DRIVER_ALIAS, name); 184 185 mutex_lock(&dsa_tag_drivers_lock); 186 list_for_each_entry(dsa_tag_driver, &dsa_tag_drivers_list, list) { 187 const struct dsa_device_ops *tmp = dsa_tag_driver->ops; 188 189 if (strcmp(name, tmp->name)) 190 continue; 191 192 if (!try_module_get(dsa_tag_driver->owner)) 193 break; 194 195 ops = tmp; 196 break; 197 } 198 mutex_unlock(&dsa_tag_drivers_lock); 199 200 return ops; 201 } 202 203 const struct dsa_device_ops *dsa_tag_driver_get_by_id(int tag_protocol) 204 { 205 struct dsa_tag_driver *dsa_tag_driver; 206 const struct dsa_device_ops *ops; 207 bool found = false; 208 209 request_module("%sid-%d", DSA_TAG_DRIVER_ALIAS, tag_protocol); 210 211 mutex_lock(&dsa_tag_drivers_lock); 212 list_for_each_entry(dsa_tag_driver, &dsa_tag_drivers_list, list) { 213 ops = dsa_tag_driver->ops; 214 if (ops->proto == tag_protocol) { 215 found = true; 216 break; 217 } 218 } 219 220 if (found) { 221 if (!try_module_get(dsa_tag_driver->owner)) 222 ops = ERR_PTR(-ENOPROTOOPT); 223 } else { 224 ops = ERR_PTR(-ENOPROTOOPT); 225 } 226 227 mutex_unlock(&dsa_tag_drivers_lock); 228 229 return ops; 230 } 231 232 void dsa_tag_driver_put(const struct dsa_device_ops *ops) 233 { 234 struct dsa_tag_driver *dsa_tag_driver; 235 236 mutex_lock(&dsa_tag_drivers_lock); 237 list_for_each_entry(dsa_tag_driver, &dsa_tag_drivers_list, list) { 238 if (dsa_tag_driver->ops == ops) { 239 module_put(dsa_tag_driver->owner); 240 break; 241 } 242 } 243 mutex_unlock(&dsa_tag_drivers_lock); 244 } 245
Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.