mirror of
https://github.com/followmsi/android_kernel_google_msm.git
synced 2024-11-06 23:17:41 +00:00
vlan: Fix header ops passthru when doing TX VLAN offload.
[ Upstream commit 2205369a31
]
When the vlan code detects that the real device can do TX VLAN offloads
in hardware, it tries to arrange for the real device's header_ops to
be invoked directly.
But it does so illegally, by simply hooking the real device's
header_ops up to the VLAN device.
This doesn't work because we will end up invoking a set of header_ops
routines which expect a device type which matches the real device, but
will see a VLAN device instead.
Fix this by providing a pass-thru set of header_ops which will arrange
to pass the proper real device instead.
To facilitate this add a dev_rebuild_header(). There are
implementations which provide a ->cache and ->create but not a
->rebuild (f.e. PLIP). So we need a helper function just like
dev_hard_header() to avoid crashes.
Use this helper in the one existing place where the
header_ops->rebuild was being invoked, the neighbour code.
With lots of help from Florian Westphal.
Signed-off-by: David S. Miller <davem@davemloft.net>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
parent
e25027c9bf
commit
86dc6b93ee
2 changed files with 27 additions and 1 deletions
|
@ -1702,6 +1702,15 @@ static inline int dev_parse_header(const struct sk_buff *skb,
|
|||
return dev->header_ops->parse(skb, haddr);
|
||||
}
|
||||
|
||||
static inline int dev_rebuild_header(struct sk_buff *skb)
|
||||
{
|
||||
const struct net_device *dev = skb->dev;
|
||||
|
||||
if (!dev->header_ops || !dev->header_ops->rebuild)
|
||||
return 0;
|
||||
return dev->header_ops->rebuild(skb);
|
||||
}
|
||||
|
||||
typedef int gifconf_func_t(struct net_device * dev, char __user * bufptr, int len);
|
||||
extern int register_gifconf(unsigned int family, gifconf_func_t * gifconf);
|
||||
static inline int unregister_gifconf(unsigned int family)
|
||||
|
|
|
@ -525,6 +525,23 @@ static const struct header_ops vlan_header_ops = {
|
|||
.parse = eth_header_parse,
|
||||
};
|
||||
|
||||
static int vlan_passthru_hard_header(struct sk_buff *skb, struct net_device *dev,
|
||||
unsigned short type,
|
||||
const void *daddr, const void *saddr,
|
||||
unsigned int len)
|
||||
{
|
||||
struct vlan_dev_priv *vlan = vlan_dev_priv(dev);
|
||||
struct net_device *real_dev = vlan->real_dev;
|
||||
|
||||
return dev_hard_header(skb, real_dev, type, daddr, saddr, len);
|
||||
}
|
||||
|
||||
static const struct header_ops vlan_passthru_header_ops = {
|
||||
.create = vlan_passthru_hard_header,
|
||||
.rebuild = dev_rebuild_header,
|
||||
.parse = eth_header_parse,
|
||||
};
|
||||
|
||||
static const struct net_device_ops vlan_netdev_ops;
|
||||
|
||||
static int vlan_dev_init(struct net_device *dev)
|
||||
|
@ -564,7 +581,7 @@ static int vlan_dev_init(struct net_device *dev)
|
|||
|
||||
dev->needed_headroom = real_dev->needed_headroom;
|
||||
if (real_dev->features & NETIF_F_HW_VLAN_TX) {
|
||||
dev->header_ops = real_dev->header_ops;
|
||||
dev->header_ops = &vlan_passthru_header_ops;
|
||||
dev->hard_header_len = real_dev->hard_header_len;
|
||||
} else {
|
||||
dev->header_ops = &vlan_header_ops;
|
||||
|
|
Loading…
Reference in a new issue