Linux Audio

Check our new training course

Loading...
Note: File does not exist in v4.6.
  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, 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
128struct packet_type dsa_pack_type __read_mostly = {
129	.type	= cpu_to_be16(ETH_P_XDSA),
130	.func	= dsa_switch_rcv,
131};
132
133static 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
143void 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
152static 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}
158EXPORT_SYMBOL_GPL(dsa_tag_drivers_register);
159
160void 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}
168EXPORT_SYMBOL_GPL(dsa_tag_drivers_unregister);
169
170const 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 */
178const 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
203const 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
232void 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}