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
19static LIST_HEAD(dsa_tag_drivers_list);
20static 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 */
30static 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
54static 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, device: 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, dev: 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(dev: skb->dev);
107
108 if (unlikely(cpu_dp->ds->untag_bridge_pvid)) {
109 nskb = dsa_untag_bridge_pvid(skb);
110 if (!nskb) {
111 kfree_skb(skb);
112 return 0;
113 }
114 skb = nskb;
115 }
116
117 dev_sw_netstats_rx_add(dev: skb->dev, len: skb->len + ETH_HLEN);
118
119 if (dsa_skb_defer_rx_timestamp(p, skb))
120 return 0;
121
122 gro_cells_receive(gcells: &p->gcells, skb);
123
124 return 0;
125}
126
127struct packet_type dsa_pack_type __read_mostly = {
128 .type = cpu_to_be16(ETH_P_XDSA),
129 .func = dsa_switch_rcv,
130};
131
132static void dsa_tag_driver_register(struct dsa_tag_driver *dsa_tag_driver,
133 struct module *owner)
134{
135 dsa_tag_driver->owner = owner;
136
137 mutex_lock(&dsa_tag_drivers_lock);
138 list_add_tail(new: &dsa_tag_driver->list, head: &dsa_tag_drivers_list);
139 mutex_unlock(lock: &dsa_tag_drivers_lock);
140}
141
142void dsa_tag_drivers_register(struct dsa_tag_driver *dsa_tag_driver_array[],
143 unsigned int count, struct module *owner)
144{
145 unsigned int i;
146
147 for (i = 0; i < count; i++)
148 dsa_tag_driver_register(dsa_tag_driver: dsa_tag_driver_array[i], owner);
149}
150
151static void dsa_tag_driver_unregister(struct dsa_tag_driver *dsa_tag_driver)
152{
153 mutex_lock(&dsa_tag_drivers_lock);
154 list_del(entry: &dsa_tag_driver->list);
155 mutex_unlock(lock: &dsa_tag_drivers_lock);
156}
157EXPORT_SYMBOL_GPL(dsa_tag_drivers_register);
158
159void dsa_tag_drivers_unregister(struct dsa_tag_driver *dsa_tag_driver_array[],
160 unsigned int count)
161{
162 unsigned int i;
163
164 for (i = 0; i < count; i++)
165 dsa_tag_driver_unregister(dsa_tag_driver: dsa_tag_driver_array[i]);
166}
167EXPORT_SYMBOL_GPL(dsa_tag_drivers_unregister);
168
169const char *dsa_tag_protocol_to_str(const struct dsa_device_ops *ops)
170{
171 return ops->name;
172};
173
174/* Function takes a reference on the module owning the tagger,
175 * so dsa_tag_driver_put must be called afterwards.
176 */
177const struct dsa_device_ops *dsa_tag_driver_get_by_name(const char *name)
178{
179 const struct dsa_device_ops *ops = ERR_PTR(error: -ENOPROTOOPT);
180 struct dsa_tag_driver *dsa_tag_driver;
181
182 request_module("%s%s", DSA_TAG_DRIVER_ALIAS, name);
183
184 mutex_lock(&dsa_tag_drivers_lock);
185 list_for_each_entry(dsa_tag_driver, &dsa_tag_drivers_list, list) {
186 const struct dsa_device_ops *tmp = dsa_tag_driver->ops;
187
188 if (strcmp(name, tmp->name))
189 continue;
190
191 if (!try_module_get(module: dsa_tag_driver->owner))
192 break;
193
194 ops = tmp;
195 break;
196 }
197 mutex_unlock(lock: &dsa_tag_drivers_lock);
198
199 return ops;
200}
201
202const struct dsa_device_ops *dsa_tag_driver_get_by_id(int tag_protocol)
203{
204 struct dsa_tag_driver *dsa_tag_driver;
205 const struct dsa_device_ops *ops;
206 bool found = false;
207
208 request_module("%sid-%d", DSA_TAG_DRIVER_ALIAS, tag_protocol);
209
210 mutex_lock(&dsa_tag_drivers_lock);
211 list_for_each_entry(dsa_tag_driver, &dsa_tag_drivers_list, list) {
212 ops = dsa_tag_driver->ops;
213 if (ops->proto == tag_protocol) {
214 found = true;
215 break;
216 }
217 }
218
219 if (found) {
220 if (!try_module_get(module: dsa_tag_driver->owner))
221 ops = ERR_PTR(error: -ENOPROTOOPT);
222 } else {
223 ops = ERR_PTR(error: -ENOPROTOOPT);
224 }
225
226 mutex_unlock(lock: &dsa_tag_drivers_lock);
227
228 return ops;
229}
230
231void dsa_tag_driver_put(const struct dsa_device_ops *ops)
232{
233 struct dsa_tag_driver *dsa_tag_driver;
234
235 mutex_lock(&dsa_tag_drivers_lock);
236 list_for_each_entry(dsa_tag_driver, &dsa_tag_drivers_list, list) {
237 if (dsa_tag_driver->ops == ops) {
238 module_put(module: dsa_tag_driver->owner);
239 break;
240 }
241 }
242 mutex_unlock(lock: &dsa_tag_drivers_lock);
243}
244

source code of linux/net/dsa/tag.c