diff options
author | Jiri Pirko <jpirko@redhat.com> | 2011-04-07 15:48:33 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2011-04-12 17:15:19 -0400 |
commit | bcc6d47903612c3861201cc3a866fb604f26b8b2 (patch) | |
tree | bd02aeaa18984de46d09a1cb41b1f5e09ec89dbb /net/8021q | |
parent | 143780c6562080c1117cd9197ee1b33c0d838376 (diff) |
net: vlan: make non-hw-accel rx path similar to hw-accel
Now there are 2 paths for rx vlan frames. When rx-vlan-hw-accel is
enabled, skb is untagged by NIC, vlan_tci is set and the skb gets into
vlan code in __netif_receive_skb - vlan_hwaccel_do_receive.
For non-rx-vlan-hw-accel however, tagged skb goes thru whole
__netif_receive_skb, it's untagged in ptype_base hander and reinjected
This incosistency is fixed by this patch. Vlan untagging happens early in
__netif_receive_skb so the rest of code (ptype_all handlers, rx_handlers)
see the skb like it was untagged by hw.
Signed-off-by: Jiri Pirko <jpirko@redhat.com>
v1->v2:
remove "inline" from vlan_core.c functions
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/8021q')
-rw-r--r-- | net/8021q/vlan.c | 8 | ||||
-rw-r--r-- | net/8021q/vlan.h | 2 | ||||
-rw-r--r-- | net/8021q/vlan_core.c | 85 | ||||
-rw-r--r-- | net/8021q/vlan_dev.c | 173 |
4 files changed, 84 insertions, 184 deletions
diff --git a/net/8021q/vlan.c b/net/8021q/vlan.c index e47600b4e2e3..14ef5efbc653 100644 --- a/net/8021q/vlan.c +++ b/net/8021q/vlan.c | |||
@@ -49,11 +49,6 @@ const char vlan_version[] = DRV_VERSION; | |||
49 | static const char vlan_copyright[] = "Ben Greear <greearb@candelatech.com>"; | 49 | static const char vlan_copyright[] = "Ben Greear <greearb@candelatech.com>"; |
50 | static const char vlan_buggyright[] = "David S. Miller <davem@redhat.com>"; | 50 | static const char vlan_buggyright[] = "David S. Miller <davem@redhat.com>"; |
51 | 51 | ||
52 | static struct packet_type vlan_packet_type __read_mostly = { | ||
53 | .type = cpu_to_be16(ETH_P_8021Q), | ||
54 | .func = vlan_skb_recv, /* VLAN receive method */ | ||
55 | }; | ||
56 | |||
57 | /* End of global variables definitions. */ | 52 | /* End of global variables definitions. */ |
58 | 53 | ||
59 | static void vlan_group_free(struct vlan_group *grp) | 54 | static void vlan_group_free(struct vlan_group *grp) |
@@ -684,7 +679,6 @@ static int __init vlan_proto_init(void) | |||
684 | if (err < 0) | 679 | if (err < 0) |
685 | goto err4; | 680 | goto err4; |
686 | 681 | ||
687 | dev_add_pack(&vlan_packet_type); | ||
688 | vlan_ioctl_set(vlan_ioctl_handler); | 682 | vlan_ioctl_set(vlan_ioctl_handler); |
689 | return 0; | 683 | return 0; |
690 | 684 | ||
@@ -705,8 +699,6 @@ static void __exit vlan_cleanup_module(void) | |||
705 | 699 | ||
706 | unregister_netdevice_notifier(&vlan_notifier_block); | 700 | unregister_netdevice_notifier(&vlan_notifier_block); |
707 | 701 | ||
708 | dev_remove_pack(&vlan_packet_type); | ||
709 | |||
710 | unregister_pernet_subsys(&vlan_net_ops); | 702 | unregister_pernet_subsys(&vlan_net_ops); |
711 | rcu_barrier(); /* Wait for completion of call_rcu()'s */ | 703 | rcu_barrier(); /* Wait for completion of call_rcu()'s */ |
712 | 704 | ||
diff --git a/net/8021q/vlan.h b/net/8021q/vlan.h index 5687c9b95f33..c3408def8a19 100644 --- a/net/8021q/vlan.h +++ b/net/8021q/vlan.h | |||
@@ -75,8 +75,6 @@ static inline struct vlan_dev_info *vlan_dev_info(const struct net_device *dev) | |||
75 | } | 75 | } |
76 | 76 | ||
77 | /* found in vlan_dev.c */ | 77 | /* found in vlan_dev.c */ |
78 | int vlan_skb_recv(struct sk_buff *skb, struct net_device *dev, | ||
79 | struct packet_type *ptype, struct net_device *orig_dev); | ||
80 | void vlan_dev_set_ingress_priority(const struct net_device *dev, | 78 | void vlan_dev_set_ingress_priority(const struct net_device *dev, |
81 | u32 skb_prio, u16 vlan_prio); | 79 | u32 skb_prio, u16 vlan_prio); |
82 | int vlan_dev_set_egress_priority(const struct net_device *dev, | 80 | int vlan_dev_set_egress_priority(const struct net_device *dev, |
diff --git a/net/8021q/vlan_core.c b/net/8021q/vlan_core.c index ce8e3ab3e7a5..41495dc2a4c9 100644 --- a/net/8021q/vlan_core.c +++ b/net/8021q/vlan_core.c | |||
@@ -4,7 +4,7 @@ | |||
4 | #include <linux/netpoll.h> | 4 | #include <linux/netpoll.h> |
5 | #include "vlan.h" | 5 | #include "vlan.h" |
6 | 6 | ||
7 | bool vlan_hwaccel_do_receive(struct sk_buff **skbp) | 7 | bool vlan_do_receive(struct sk_buff **skbp) |
8 | { | 8 | { |
9 | struct sk_buff *skb = *skbp; | 9 | struct sk_buff *skb = *skbp; |
10 | u16 vlan_id = skb->vlan_tci & VLAN_VID_MASK; | 10 | u16 vlan_id = skb->vlan_tci & VLAN_VID_MASK; |
@@ -88,3 +88,86 @@ gro_result_t vlan_gro_frags(struct napi_struct *napi, struct vlan_group *grp, | |||
88 | return napi_gro_frags(napi); | 88 | return napi_gro_frags(napi); |
89 | } | 89 | } |
90 | EXPORT_SYMBOL(vlan_gro_frags); | 90 | EXPORT_SYMBOL(vlan_gro_frags); |
91 | |||
92 | static struct sk_buff *vlan_check_reorder_header(struct sk_buff *skb) | ||
93 | { | ||
94 | if (vlan_dev_info(skb->dev)->flags & VLAN_FLAG_REORDER_HDR) { | ||
95 | if (skb_cow(skb, skb_headroom(skb)) < 0) | ||
96 | skb = NULL; | ||
97 | if (skb) { | ||
98 | /* Lifted from Gleb's VLAN code... */ | ||
99 | memmove(skb->data - ETH_HLEN, | ||
100 | skb->data - VLAN_ETH_HLEN, 12); | ||
101 | skb->mac_header += VLAN_HLEN; | ||
102 | } | ||
103 | } | ||
104 | return skb; | ||
105 | } | ||
106 | |||
107 | static void vlan_set_encap_proto(struct sk_buff *skb, struct vlan_hdr *vhdr) | ||
108 | { | ||
109 | __be16 proto; | ||
110 | unsigned char *rawp; | ||
111 | |||
112 | /* | ||
113 | * Was a VLAN packet, grab the encapsulated protocol, which the layer | ||
114 | * three protocols care about. | ||
115 | */ | ||
116 | |||
117 | proto = vhdr->h_vlan_encapsulated_proto; | ||
118 | if (ntohs(proto) >= 1536) { | ||
119 | skb->protocol = proto; | ||
120 | return; | ||
121 | } | ||
122 | |||
123 | rawp = skb->data; | ||
124 | if (*(unsigned short *) rawp == 0xFFFF) | ||
125 | /* | ||
126 | * This is a magic hack to spot IPX packets. Older Novell | ||
127 | * breaks the protocol design and runs IPX over 802.3 without | ||
128 | * an 802.2 LLC layer. We look for FFFF which isn't a used | ||
129 | * 802.2 SSAP/DSAP. This won't work for fault tolerant netware | ||
130 | * but does for the rest. | ||
131 | */ | ||
132 | skb->protocol = htons(ETH_P_802_3); | ||
133 | else | ||
134 | /* | ||
135 | * Real 802.2 LLC | ||
136 | */ | ||
137 | skb->protocol = htons(ETH_P_802_2); | ||
138 | } | ||
139 | |||
140 | struct sk_buff *vlan_untag(struct sk_buff *skb) | ||
141 | { | ||
142 | struct vlan_hdr *vhdr; | ||
143 | u16 vlan_tci; | ||
144 | |||
145 | if (unlikely(vlan_tx_tag_present(skb))) { | ||
146 | /* vlan_tci is already set-up so leave this for another time */ | ||
147 | return skb; | ||
148 | } | ||
149 | |||
150 | skb = skb_share_check(skb, GFP_ATOMIC); | ||
151 | if (unlikely(!skb)) | ||
152 | goto err_free; | ||
153 | |||
154 | if (unlikely(!pskb_may_pull(skb, VLAN_HLEN))) | ||
155 | goto err_free; | ||
156 | |||
157 | vhdr = (struct vlan_hdr *) skb->data; | ||
158 | vlan_tci = ntohs(vhdr->h_vlan_TCI); | ||
159 | __vlan_hwaccel_put_tag(skb, vlan_tci); | ||
160 | |||
161 | skb_pull_rcsum(skb, VLAN_HLEN); | ||
162 | vlan_set_encap_proto(skb, vhdr); | ||
163 | |||
164 | skb = vlan_check_reorder_header(skb); | ||
165 | if (unlikely(!skb)) | ||
166 | goto err_free; | ||
167 | |||
168 | return skb; | ||
169 | |||
170 | err_free: | ||
171 | kfree_skb(skb); | ||
172 | return NULL; | ||
173 | } | ||
diff --git a/net/8021q/vlan_dev.c b/net/8021q/vlan_dev.c index b84a46b30c0c..d174c312b7f1 100644 --- a/net/8021q/vlan_dev.c +++ b/net/8021q/vlan_dev.c | |||
@@ -65,179 +65,6 @@ static int vlan_dev_rebuild_header(struct sk_buff *skb) | |||
65 | return 0; | 65 | return 0; |
66 | } | 66 | } |
67 | 67 | ||
68 | static inline struct sk_buff *vlan_check_reorder_header(struct sk_buff *skb) | ||
69 | { | ||
70 | if (vlan_dev_info(skb->dev)->flags & VLAN_FLAG_REORDER_HDR) { | ||
71 | if (skb_cow(skb, skb_headroom(skb)) < 0) | ||
72 | skb = NULL; | ||
73 | if (skb) { | ||
74 | /* Lifted from Gleb's VLAN code... */ | ||
75 | memmove(skb->data - ETH_HLEN, | ||
76 | skb->data - VLAN_ETH_HLEN, 12); | ||
77 | skb->mac_header += VLAN_HLEN; | ||
78 | } | ||
79 | } | ||
80 | |||
81 | return skb; | ||
82 | } | ||
83 | |||
84 | static inline void vlan_set_encap_proto(struct sk_buff *skb, | ||
85 | struct vlan_hdr *vhdr) | ||
86 | { | ||
87 | __be16 proto; | ||
88 | unsigned char *rawp; | ||
89 | |||
90 | /* | ||
91 | * Was a VLAN packet, grab the encapsulated protocol, which the layer | ||
92 | * three protocols care about. | ||
93 | */ | ||
94 | |||
95 | proto = vhdr->h_vlan_encapsulated_proto; | ||
96 | if (ntohs(proto) >= 1536) { | ||
97 | skb->protocol = proto; | ||
98 | return; | ||
99 | } | ||
100 | |||
101 | rawp = skb->data; | ||
102 | if (*(unsigned short *)rawp == 0xFFFF) | ||
103 | /* | ||
104 | * This is a magic hack to spot IPX packets. Older Novell | ||
105 | * breaks the protocol design and runs IPX over 802.3 without | ||
106 | * an 802.2 LLC layer. We look for FFFF which isn't a used | ||
107 | * 802.2 SSAP/DSAP. This won't work for fault tolerant netware | ||
108 | * but does for the rest. | ||
109 | */ | ||
110 | skb->protocol = htons(ETH_P_802_3); | ||
111 | else | ||
112 | /* | ||
113 | * Real 802.2 LLC | ||
114 | */ | ||
115 | skb->protocol = htons(ETH_P_802_2); | ||
116 | } | ||
117 | |||
118 | /* | ||
119 | * Determine the packet's protocol ID. The rule here is that we | ||
120 | * assume 802.3 if the type field is short enough to be a length. | ||
121 | * This is normal practice and works for any 'now in use' protocol. | ||
122 | * | ||
123 | * Also, at this point we assume that we ARE dealing exclusively with | ||
124 | * VLAN packets, or packets that should be made into VLAN packets based | ||
125 | * on a default VLAN ID. | ||
126 | * | ||
127 | * NOTE: Should be similar to ethernet/eth.c. | ||
128 | * | ||
129 | * SANITY NOTE: This method is called when a packet is moving up the stack | ||
130 | * towards userland. To get here, it would have already passed | ||
131 | * through the ethernet/eth.c eth_type_trans() method. | ||
132 | * SANITY NOTE 2: We are referencing to the VLAN_HDR frields, which MAY be | ||
133 | * stored UNALIGNED in the memory. RISC systems don't like | ||
134 | * such cases very much... | ||
135 | * SANITY NOTE 2a: According to Dave Miller & Alexey, it will always be | ||
136 | * aligned, so there doesn't need to be any of the unaligned | ||
137 | * stuff. It has been commented out now... --Ben | ||
138 | * | ||
139 | */ | ||
140 | int vlan_skb_recv(struct sk_buff *skb, struct net_device *dev, | ||
141 | struct packet_type *ptype, struct net_device *orig_dev) | ||
142 | { | ||
143 | struct vlan_hdr *vhdr; | ||
144 | struct vlan_pcpu_stats *rx_stats; | ||
145 | struct net_device *vlan_dev; | ||
146 | u16 vlan_id; | ||
147 | u16 vlan_tci; | ||
148 | |||
149 | skb = skb_share_check(skb, GFP_ATOMIC); | ||
150 | if (skb == NULL) | ||
151 | goto err_free; | ||
152 | |||
153 | if (unlikely(!pskb_may_pull(skb, VLAN_HLEN))) | ||
154 | goto err_free; | ||
155 | |||
156 | vhdr = (struct vlan_hdr *)skb->data; | ||
157 | vlan_tci = ntohs(vhdr->h_vlan_TCI); | ||
158 | vlan_id = vlan_tci & VLAN_VID_MASK; | ||
159 | |||
160 | rcu_read_lock(); | ||
161 | vlan_dev = vlan_find_dev(dev, vlan_id); | ||
162 | |||
163 | /* If the VLAN device is defined, we use it. | ||
164 | * If not, and the VID is 0, it is a 802.1p packet (not | ||
165 | * really a VLAN), so we will just netif_rx it later to the | ||
166 | * original interface, but with the skb->proto set to the | ||
167 | * wrapped proto: we do nothing here. | ||
168 | */ | ||
169 | |||
170 | if (!vlan_dev) { | ||
171 | if (vlan_id) { | ||
172 | pr_debug("%s: ERROR: No net_device for VID: %u on dev: %s\n", | ||
173 | __func__, vlan_id, dev->name); | ||
174 | goto err_unlock; | ||
175 | } | ||
176 | rx_stats = NULL; | ||
177 | } else { | ||
178 | skb->dev = vlan_dev; | ||
179 | |||
180 | rx_stats = this_cpu_ptr(vlan_dev_info(skb->dev)->vlan_pcpu_stats); | ||
181 | |||
182 | u64_stats_update_begin(&rx_stats->syncp); | ||
183 | rx_stats->rx_packets++; | ||
184 | rx_stats->rx_bytes += skb->len; | ||
185 | |||
186 | skb->priority = vlan_get_ingress_priority(skb->dev, vlan_tci); | ||
187 | |||
188 | pr_debug("%s: priority: %u for TCI: %hu\n", | ||
189 | __func__, skb->priority, vlan_tci); | ||
190 | |||
191 | switch (skb->pkt_type) { | ||
192 | case PACKET_BROADCAST: | ||
193 | /* Yeah, stats collect these together.. */ | ||
194 | /* stats->broadcast ++; // no such counter :-( */ | ||
195 | break; | ||
196 | |||
197 | case PACKET_MULTICAST: | ||
198 | rx_stats->rx_multicast++; | ||
199 | break; | ||
200 | |||
201 | case PACKET_OTHERHOST: | ||
202 | /* Our lower layer thinks this is not local, let's make | ||
203 | * sure. | ||
204 | * This allows the VLAN to have a different MAC than the | ||
205 | * underlying device, and still route correctly. | ||
206 | */ | ||
207 | if (!compare_ether_addr(eth_hdr(skb)->h_dest, | ||
208 | skb->dev->dev_addr)) | ||
209 | skb->pkt_type = PACKET_HOST; | ||
210 | break; | ||
211 | default: | ||
212 | break; | ||
213 | } | ||
214 | u64_stats_update_end(&rx_stats->syncp); | ||
215 | } | ||
216 | |||
217 | skb_pull_rcsum(skb, VLAN_HLEN); | ||
218 | vlan_set_encap_proto(skb, vhdr); | ||
219 | |||
220 | if (vlan_dev) { | ||
221 | skb = vlan_check_reorder_header(skb); | ||
222 | if (!skb) { | ||
223 | rx_stats->rx_errors++; | ||
224 | goto err_unlock; | ||
225 | } | ||
226 | } | ||
227 | |||
228 | netif_rx(skb); | ||
229 | |||
230 | rcu_read_unlock(); | ||
231 | return NET_RX_SUCCESS; | ||
232 | |||
233 | err_unlock: | ||
234 | rcu_read_unlock(); | ||
235 | err_free: | ||
236 | atomic_long_inc(&dev->rx_dropped); | ||
237 | kfree_skb(skb); | ||
238 | return NET_RX_DROP; | ||
239 | } | ||
240 | |||
241 | static inline u16 | 68 | static inline u16 |
242 | vlan_dev_get_egress_qos_mask(struct net_device *dev, struct sk_buff *skb) | 69 | vlan_dev_get_egress_qos_mask(struct net_device *dev, struct sk_buff *skb) |
243 | { | 70 | { |