Commit b29d3145183da4e07d4b570fa8acdd3ac4a5c572
Committed by
David S. Miller
1 parent
6708c9e5cc
Exists in
smarc-l5.0.0_1.0.0-ga
and in
5 other branches
net: vlan,ethtool: netdev_features_t is more than 32 bit
Signed-off-by: Bjørn Mork <bjorn@mork.no> Signed-off-by: David S. Miller <davem@davemloft.net>
Showing 2 changed files with 2 additions and 2 deletions Inline Diff
net/8021q/vlan_dev.c
1 | /* -*- linux-c -*- | 1 | /* -*- linux-c -*- |
2 | * INET 802.1Q VLAN | 2 | * INET 802.1Q VLAN |
3 | * Ethernet-type device handling. | 3 | * Ethernet-type device handling. |
4 | * | 4 | * |
5 | * Authors: Ben Greear <greearb@candelatech.com> | 5 | * Authors: Ben Greear <greearb@candelatech.com> |
6 | * Please send support related email to: netdev@vger.kernel.org | 6 | * Please send support related email to: netdev@vger.kernel.org |
7 | * VLAN Home Page: http://www.candelatech.com/~greear/vlan.html | 7 | * VLAN Home Page: http://www.candelatech.com/~greear/vlan.html |
8 | * | 8 | * |
9 | * Fixes: Mar 22 2001: Martin Bokaemper <mbokaemper@unispherenetworks.com> | 9 | * Fixes: Mar 22 2001: Martin Bokaemper <mbokaemper@unispherenetworks.com> |
10 | * - reset skb->pkt_type on incoming packets when MAC was changed | 10 | * - reset skb->pkt_type on incoming packets when MAC was changed |
11 | * - see that changed MAC is saddr for outgoing packets | 11 | * - see that changed MAC is saddr for outgoing packets |
12 | * Oct 20, 2001: Ard van Breeman: | 12 | * Oct 20, 2001: Ard van Breeman: |
13 | * - Fix MC-list, finally. | 13 | * - Fix MC-list, finally. |
14 | * - Flush MC-list on VLAN destroy. | 14 | * - Flush MC-list on VLAN destroy. |
15 | * | 15 | * |
16 | * | 16 | * |
17 | * This program is free software; you can redistribute it and/or | 17 | * This program is free software; you can redistribute it and/or |
18 | * modify it under the terms of the GNU General Public License | 18 | * modify it under the terms of the GNU General Public License |
19 | * as published by the Free Software Foundation; either version | 19 | * as published by the Free Software Foundation; either version |
20 | * 2 of the License, or (at your option) any later version. | 20 | * 2 of the License, or (at your option) any later version. |
21 | */ | 21 | */ |
22 | 22 | ||
23 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt | 23 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt |
24 | 24 | ||
25 | #include <linux/module.h> | 25 | #include <linux/module.h> |
26 | #include <linux/slab.h> | 26 | #include <linux/slab.h> |
27 | #include <linux/skbuff.h> | 27 | #include <linux/skbuff.h> |
28 | #include <linux/netdevice.h> | 28 | #include <linux/netdevice.h> |
29 | #include <linux/etherdevice.h> | 29 | #include <linux/etherdevice.h> |
30 | #include <linux/ethtool.h> | 30 | #include <linux/ethtool.h> |
31 | #include <net/arp.h> | 31 | #include <net/arp.h> |
32 | 32 | ||
33 | #include "vlan.h" | 33 | #include "vlan.h" |
34 | #include "vlanproc.h" | 34 | #include "vlanproc.h" |
35 | #include <linux/if_vlan.h> | 35 | #include <linux/if_vlan.h> |
36 | #include <linux/netpoll.h> | 36 | #include <linux/netpoll.h> |
37 | 37 | ||
38 | /* | 38 | /* |
39 | * Rebuild the Ethernet MAC header. This is called after an ARP | 39 | * Rebuild the Ethernet MAC header. This is called after an ARP |
40 | * (or in future other address resolution) has completed on this | 40 | * (or in future other address resolution) has completed on this |
41 | * sk_buff. We now let ARP fill in the other fields. | 41 | * sk_buff. We now let ARP fill in the other fields. |
42 | * | 42 | * |
43 | * This routine CANNOT use cached dst->neigh! | 43 | * This routine CANNOT use cached dst->neigh! |
44 | * Really, it is used only when dst->neigh is wrong. | 44 | * Really, it is used only when dst->neigh is wrong. |
45 | * | 45 | * |
46 | * TODO: This needs a checkup, I'm ignorant here. --BLG | 46 | * TODO: This needs a checkup, I'm ignorant here. --BLG |
47 | */ | 47 | */ |
48 | static int vlan_dev_rebuild_header(struct sk_buff *skb) | 48 | static int vlan_dev_rebuild_header(struct sk_buff *skb) |
49 | { | 49 | { |
50 | struct net_device *dev = skb->dev; | 50 | struct net_device *dev = skb->dev; |
51 | struct vlan_ethhdr *veth = (struct vlan_ethhdr *)(skb->data); | 51 | struct vlan_ethhdr *veth = (struct vlan_ethhdr *)(skb->data); |
52 | 52 | ||
53 | switch (veth->h_vlan_encapsulated_proto) { | 53 | switch (veth->h_vlan_encapsulated_proto) { |
54 | #ifdef CONFIG_INET | 54 | #ifdef CONFIG_INET |
55 | case htons(ETH_P_IP): | 55 | case htons(ETH_P_IP): |
56 | 56 | ||
57 | /* TODO: Confirm this will work with VLAN headers... */ | 57 | /* TODO: Confirm this will work with VLAN headers... */ |
58 | return arp_find(veth->h_dest, skb); | 58 | return arp_find(veth->h_dest, skb); |
59 | #endif | 59 | #endif |
60 | default: | 60 | default: |
61 | pr_debug("%s: unable to resolve type %X addresses\n", | 61 | pr_debug("%s: unable to resolve type %X addresses\n", |
62 | dev->name, ntohs(veth->h_vlan_encapsulated_proto)); | 62 | dev->name, ntohs(veth->h_vlan_encapsulated_proto)); |
63 | 63 | ||
64 | memcpy(veth->h_source, dev->dev_addr, ETH_ALEN); | 64 | memcpy(veth->h_source, dev->dev_addr, ETH_ALEN); |
65 | break; | 65 | break; |
66 | } | 66 | } |
67 | 67 | ||
68 | return 0; | 68 | return 0; |
69 | } | 69 | } |
70 | 70 | ||
71 | static inline u16 | 71 | static inline u16 |
72 | vlan_dev_get_egress_qos_mask(struct net_device *dev, struct sk_buff *skb) | 72 | vlan_dev_get_egress_qos_mask(struct net_device *dev, struct sk_buff *skb) |
73 | { | 73 | { |
74 | struct vlan_priority_tci_mapping *mp; | 74 | struct vlan_priority_tci_mapping *mp; |
75 | 75 | ||
76 | mp = vlan_dev_priv(dev)->egress_priority_map[(skb->priority & 0xF)]; | 76 | mp = vlan_dev_priv(dev)->egress_priority_map[(skb->priority & 0xF)]; |
77 | while (mp) { | 77 | while (mp) { |
78 | if (mp->priority == skb->priority) { | 78 | if (mp->priority == skb->priority) { |
79 | return mp->vlan_qos; /* This should already be shifted | 79 | return mp->vlan_qos; /* This should already be shifted |
80 | * to mask correctly with the | 80 | * to mask correctly with the |
81 | * VLAN's TCI */ | 81 | * VLAN's TCI */ |
82 | } | 82 | } |
83 | mp = mp->next; | 83 | mp = mp->next; |
84 | } | 84 | } |
85 | return 0; | 85 | return 0; |
86 | } | 86 | } |
87 | 87 | ||
88 | /* | 88 | /* |
89 | * Create the VLAN header for an arbitrary protocol layer | 89 | * Create the VLAN header for an arbitrary protocol layer |
90 | * | 90 | * |
91 | * saddr=NULL means use device source address | 91 | * saddr=NULL means use device source address |
92 | * daddr=NULL means leave destination address (eg unresolved arp) | 92 | * daddr=NULL means leave destination address (eg unresolved arp) |
93 | * | 93 | * |
94 | * This is called when the SKB is moving down the stack towards the | 94 | * This is called when the SKB is moving down the stack towards the |
95 | * physical devices. | 95 | * physical devices. |
96 | */ | 96 | */ |
97 | static int vlan_dev_hard_header(struct sk_buff *skb, struct net_device *dev, | 97 | static int vlan_dev_hard_header(struct sk_buff *skb, struct net_device *dev, |
98 | unsigned short type, | 98 | unsigned short type, |
99 | const void *daddr, const void *saddr, | 99 | const void *daddr, const void *saddr, |
100 | unsigned int len) | 100 | unsigned int len) |
101 | { | 101 | { |
102 | struct vlan_dev_priv *vlan = vlan_dev_priv(dev); | 102 | struct vlan_dev_priv *vlan = vlan_dev_priv(dev); |
103 | struct vlan_hdr *vhdr; | 103 | struct vlan_hdr *vhdr; |
104 | unsigned int vhdrlen = 0; | 104 | unsigned int vhdrlen = 0; |
105 | u16 vlan_tci = 0; | 105 | u16 vlan_tci = 0; |
106 | int rc; | 106 | int rc; |
107 | 107 | ||
108 | if (!(vlan_dev_priv(dev)->flags & VLAN_FLAG_REORDER_HDR)) { | 108 | if (!(vlan_dev_priv(dev)->flags & VLAN_FLAG_REORDER_HDR)) { |
109 | vhdr = (struct vlan_hdr *) skb_push(skb, VLAN_HLEN); | 109 | vhdr = (struct vlan_hdr *) skb_push(skb, VLAN_HLEN); |
110 | 110 | ||
111 | vlan_tci = vlan_dev_priv(dev)->vlan_id; | 111 | vlan_tci = vlan_dev_priv(dev)->vlan_id; |
112 | vlan_tci |= vlan_dev_get_egress_qos_mask(dev, skb); | 112 | vlan_tci |= vlan_dev_get_egress_qos_mask(dev, skb); |
113 | vhdr->h_vlan_TCI = htons(vlan_tci); | 113 | vhdr->h_vlan_TCI = htons(vlan_tci); |
114 | 114 | ||
115 | /* | 115 | /* |
116 | * Set the protocol type. For a packet of type ETH_P_802_3/2 we | 116 | * Set the protocol type. For a packet of type ETH_P_802_3/2 we |
117 | * put the length in here instead. | 117 | * put the length in here instead. |
118 | */ | 118 | */ |
119 | if (type != ETH_P_802_3 && type != ETH_P_802_2) | 119 | if (type != ETH_P_802_3 && type != ETH_P_802_2) |
120 | vhdr->h_vlan_encapsulated_proto = htons(type); | 120 | vhdr->h_vlan_encapsulated_proto = htons(type); |
121 | else | 121 | else |
122 | vhdr->h_vlan_encapsulated_proto = htons(len); | 122 | vhdr->h_vlan_encapsulated_proto = htons(len); |
123 | 123 | ||
124 | skb->protocol = vlan->vlan_proto; | 124 | skb->protocol = vlan->vlan_proto; |
125 | type = ntohs(vlan->vlan_proto); | 125 | type = ntohs(vlan->vlan_proto); |
126 | vhdrlen = VLAN_HLEN; | 126 | vhdrlen = VLAN_HLEN; |
127 | } | 127 | } |
128 | 128 | ||
129 | /* Before delegating work to the lower layer, enter our MAC-address */ | 129 | /* Before delegating work to the lower layer, enter our MAC-address */ |
130 | if (saddr == NULL) | 130 | if (saddr == NULL) |
131 | saddr = dev->dev_addr; | 131 | saddr = dev->dev_addr; |
132 | 132 | ||
133 | /* Now make the underlying real hard header */ | 133 | /* Now make the underlying real hard header */ |
134 | dev = vlan_dev_priv(dev)->real_dev; | 134 | dev = vlan_dev_priv(dev)->real_dev; |
135 | rc = dev_hard_header(skb, dev, type, daddr, saddr, len + vhdrlen); | 135 | rc = dev_hard_header(skb, dev, type, daddr, saddr, len + vhdrlen); |
136 | if (rc > 0) | 136 | if (rc > 0) |
137 | rc += vhdrlen; | 137 | rc += vhdrlen; |
138 | return rc; | 138 | return rc; |
139 | } | 139 | } |
140 | 140 | ||
141 | static inline netdev_tx_t vlan_netpoll_send_skb(struct vlan_dev_priv *vlan, struct sk_buff *skb) | 141 | static inline netdev_tx_t vlan_netpoll_send_skb(struct vlan_dev_priv *vlan, struct sk_buff *skb) |
142 | { | 142 | { |
143 | #ifdef CONFIG_NET_POLL_CONTROLLER | 143 | #ifdef CONFIG_NET_POLL_CONTROLLER |
144 | if (vlan->netpoll) | 144 | if (vlan->netpoll) |
145 | netpoll_send_skb(vlan->netpoll, skb); | 145 | netpoll_send_skb(vlan->netpoll, skb); |
146 | #else | 146 | #else |
147 | BUG(); | 147 | BUG(); |
148 | #endif | 148 | #endif |
149 | return NETDEV_TX_OK; | 149 | return NETDEV_TX_OK; |
150 | } | 150 | } |
151 | 151 | ||
152 | static netdev_tx_t vlan_dev_hard_start_xmit(struct sk_buff *skb, | 152 | static netdev_tx_t vlan_dev_hard_start_xmit(struct sk_buff *skb, |
153 | struct net_device *dev) | 153 | struct net_device *dev) |
154 | { | 154 | { |
155 | struct vlan_dev_priv *vlan = vlan_dev_priv(dev); | 155 | struct vlan_dev_priv *vlan = vlan_dev_priv(dev); |
156 | struct vlan_ethhdr *veth = (struct vlan_ethhdr *)(skb->data); | 156 | struct vlan_ethhdr *veth = (struct vlan_ethhdr *)(skb->data); |
157 | unsigned int len; | 157 | unsigned int len; |
158 | int ret; | 158 | int ret; |
159 | 159 | ||
160 | /* Handle non-VLAN frames if they are sent to us, for example by DHCP. | 160 | /* Handle non-VLAN frames if they are sent to us, for example by DHCP. |
161 | * | 161 | * |
162 | * NOTE: THIS ASSUMES DIX ETHERNET, SPECIFICALLY NOT SUPPORTING | 162 | * NOTE: THIS ASSUMES DIX ETHERNET, SPECIFICALLY NOT SUPPORTING |
163 | * OTHER THINGS LIKE FDDI/TokenRing/802.3 SNAPs... | 163 | * OTHER THINGS LIKE FDDI/TokenRing/802.3 SNAPs... |
164 | */ | 164 | */ |
165 | if (veth->h_vlan_proto != vlan->vlan_proto || | 165 | if (veth->h_vlan_proto != vlan->vlan_proto || |
166 | vlan->flags & VLAN_FLAG_REORDER_HDR) { | 166 | vlan->flags & VLAN_FLAG_REORDER_HDR) { |
167 | u16 vlan_tci; | 167 | u16 vlan_tci; |
168 | vlan_tci = vlan->vlan_id; | 168 | vlan_tci = vlan->vlan_id; |
169 | vlan_tci |= vlan_dev_get_egress_qos_mask(dev, skb); | 169 | vlan_tci |= vlan_dev_get_egress_qos_mask(dev, skb); |
170 | skb = __vlan_hwaccel_put_tag(skb, vlan->vlan_proto, vlan_tci); | 170 | skb = __vlan_hwaccel_put_tag(skb, vlan->vlan_proto, vlan_tci); |
171 | } | 171 | } |
172 | 172 | ||
173 | skb->dev = vlan->real_dev; | 173 | skb->dev = vlan->real_dev; |
174 | len = skb->len; | 174 | len = skb->len; |
175 | if (unlikely(netpoll_tx_running(dev))) | 175 | if (unlikely(netpoll_tx_running(dev))) |
176 | return vlan_netpoll_send_skb(vlan, skb); | 176 | return vlan_netpoll_send_skb(vlan, skb); |
177 | 177 | ||
178 | ret = dev_queue_xmit(skb); | 178 | ret = dev_queue_xmit(skb); |
179 | 179 | ||
180 | if (likely(ret == NET_XMIT_SUCCESS || ret == NET_XMIT_CN)) { | 180 | if (likely(ret == NET_XMIT_SUCCESS || ret == NET_XMIT_CN)) { |
181 | struct vlan_pcpu_stats *stats; | 181 | struct vlan_pcpu_stats *stats; |
182 | 182 | ||
183 | stats = this_cpu_ptr(vlan->vlan_pcpu_stats); | 183 | stats = this_cpu_ptr(vlan->vlan_pcpu_stats); |
184 | u64_stats_update_begin(&stats->syncp); | 184 | u64_stats_update_begin(&stats->syncp); |
185 | stats->tx_packets++; | 185 | stats->tx_packets++; |
186 | stats->tx_bytes += len; | 186 | stats->tx_bytes += len; |
187 | u64_stats_update_end(&stats->syncp); | 187 | u64_stats_update_end(&stats->syncp); |
188 | } else { | 188 | } else { |
189 | this_cpu_inc(vlan->vlan_pcpu_stats->tx_dropped); | 189 | this_cpu_inc(vlan->vlan_pcpu_stats->tx_dropped); |
190 | } | 190 | } |
191 | 191 | ||
192 | return ret; | 192 | return ret; |
193 | } | 193 | } |
194 | 194 | ||
195 | static int vlan_dev_change_mtu(struct net_device *dev, int new_mtu) | 195 | static int vlan_dev_change_mtu(struct net_device *dev, int new_mtu) |
196 | { | 196 | { |
197 | /* TODO: gotta make sure the underlying layer can handle it, | 197 | /* TODO: gotta make sure the underlying layer can handle it, |
198 | * maybe an IFF_VLAN_CAPABLE flag for devices? | 198 | * maybe an IFF_VLAN_CAPABLE flag for devices? |
199 | */ | 199 | */ |
200 | if (vlan_dev_priv(dev)->real_dev->mtu < new_mtu) | 200 | if (vlan_dev_priv(dev)->real_dev->mtu < new_mtu) |
201 | return -ERANGE; | 201 | return -ERANGE; |
202 | 202 | ||
203 | dev->mtu = new_mtu; | 203 | dev->mtu = new_mtu; |
204 | 204 | ||
205 | return 0; | 205 | return 0; |
206 | } | 206 | } |
207 | 207 | ||
208 | void vlan_dev_set_ingress_priority(const struct net_device *dev, | 208 | void vlan_dev_set_ingress_priority(const struct net_device *dev, |
209 | u32 skb_prio, u16 vlan_prio) | 209 | u32 skb_prio, u16 vlan_prio) |
210 | { | 210 | { |
211 | struct vlan_dev_priv *vlan = vlan_dev_priv(dev); | 211 | struct vlan_dev_priv *vlan = vlan_dev_priv(dev); |
212 | 212 | ||
213 | if (vlan->ingress_priority_map[vlan_prio & 0x7] && !skb_prio) | 213 | if (vlan->ingress_priority_map[vlan_prio & 0x7] && !skb_prio) |
214 | vlan->nr_ingress_mappings--; | 214 | vlan->nr_ingress_mappings--; |
215 | else if (!vlan->ingress_priority_map[vlan_prio & 0x7] && skb_prio) | 215 | else if (!vlan->ingress_priority_map[vlan_prio & 0x7] && skb_prio) |
216 | vlan->nr_ingress_mappings++; | 216 | vlan->nr_ingress_mappings++; |
217 | 217 | ||
218 | vlan->ingress_priority_map[vlan_prio & 0x7] = skb_prio; | 218 | vlan->ingress_priority_map[vlan_prio & 0x7] = skb_prio; |
219 | } | 219 | } |
220 | 220 | ||
221 | int vlan_dev_set_egress_priority(const struct net_device *dev, | 221 | int vlan_dev_set_egress_priority(const struct net_device *dev, |
222 | u32 skb_prio, u16 vlan_prio) | 222 | u32 skb_prio, u16 vlan_prio) |
223 | { | 223 | { |
224 | struct vlan_dev_priv *vlan = vlan_dev_priv(dev); | 224 | struct vlan_dev_priv *vlan = vlan_dev_priv(dev); |
225 | struct vlan_priority_tci_mapping *mp = NULL; | 225 | struct vlan_priority_tci_mapping *mp = NULL; |
226 | struct vlan_priority_tci_mapping *np; | 226 | struct vlan_priority_tci_mapping *np; |
227 | u32 vlan_qos = (vlan_prio << VLAN_PRIO_SHIFT) & VLAN_PRIO_MASK; | 227 | u32 vlan_qos = (vlan_prio << VLAN_PRIO_SHIFT) & VLAN_PRIO_MASK; |
228 | 228 | ||
229 | /* See if a priority mapping exists.. */ | 229 | /* See if a priority mapping exists.. */ |
230 | mp = vlan->egress_priority_map[skb_prio & 0xF]; | 230 | mp = vlan->egress_priority_map[skb_prio & 0xF]; |
231 | while (mp) { | 231 | while (mp) { |
232 | if (mp->priority == skb_prio) { | 232 | if (mp->priority == skb_prio) { |
233 | if (mp->vlan_qos && !vlan_qos) | 233 | if (mp->vlan_qos && !vlan_qos) |
234 | vlan->nr_egress_mappings--; | 234 | vlan->nr_egress_mappings--; |
235 | else if (!mp->vlan_qos && vlan_qos) | 235 | else if (!mp->vlan_qos && vlan_qos) |
236 | vlan->nr_egress_mappings++; | 236 | vlan->nr_egress_mappings++; |
237 | mp->vlan_qos = vlan_qos; | 237 | mp->vlan_qos = vlan_qos; |
238 | return 0; | 238 | return 0; |
239 | } | 239 | } |
240 | mp = mp->next; | 240 | mp = mp->next; |
241 | } | 241 | } |
242 | 242 | ||
243 | /* Create a new mapping then. */ | 243 | /* Create a new mapping then. */ |
244 | mp = vlan->egress_priority_map[skb_prio & 0xF]; | 244 | mp = vlan->egress_priority_map[skb_prio & 0xF]; |
245 | np = kmalloc(sizeof(struct vlan_priority_tci_mapping), GFP_KERNEL); | 245 | np = kmalloc(sizeof(struct vlan_priority_tci_mapping), GFP_KERNEL); |
246 | if (!np) | 246 | if (!np) |
247 | return -ENOBUFS; | 247 | return -ENOBUFS; |
248 | 248 | ||
249 | np->next = mp; | 249 | np->next = mp; |
250 | np->priority = skb_prio; | 250 | np->priority = skb_prio; |
251 | np->vlan_qos = vlan_qos; | 251 | np->vlan_qos = vlan_qos; |
252 | vlan->egress_priority_map[skb_prio & 0xF] = np; | 252 | vlan->egress_priority_map[skb_prio & 0xF] = np; |
253 | if (vlan_qos) | 253 | if (vlan_qos) |
254 | vlan->nr_egress_mappings++; | 254 | vlan->nr_egress_mappings++; |
255 | return 0; | 255 | return 0; |
256 | } | 256 | } |
257 | 257 | ||
258 | /* Flags are defined in the vlan_flags enum in include/linux/if_vlan.h file. */ | 258 | /* Flags are defined in the vlan_flags enum in include/linux/if_vlan.h file. */ |
259 | int vlan_dev_change_flags(const struct net_device *dev, u32 flags, u32 mask) | 259 | int vlan_dev_change_flags(const struct net_device *dev, u32 flags, u32 mask) |
260 | { | 260 | { |
261 | struct vlan_dev_priv *vlan = vlan_dev_priv(dev); | 261 | struct vlan_dev_priv *vlan = vlan_dev_priv(dev); |
262 | u32 old_flags = vlan->flags; | 262 | u32 old_flags = vlan->flags; |
263 | 263 | ||
264 | if (mask & ~(VLAN_FLAG_REORDER_HDR | VLAN_FLAG_GVRP | | 264 | if (mask & ~(VLAN_FLAG_REORDER_HDR | VLAN_FLAG_GVRP | |
265 | VLAN_FLAG_LOOSE_BINDING | VLAN_FLAG_MVRP)) | 265 | VLAN_FLAG_LOOSE_BINDING | VLAN_FLAG_MVRP)) |
266 | return -EINVAL; | 266 | return -EINVAL; |
267 | 267 | ||
268 | vlan->flags = (old_flags & ~mask) | (flags & mask); | 268 | vlan->flags = (old_flags & ~mask) | (flags & mask); |
269 | 269 | ||
270 | if (netif_running(dev) && (vlan->flags ^ old_flags) & VLAN_FLAG_GVRP) { | 270 | if (netif_running(dev) && (vlan->flags ^ old_flags) & VLAN_FLAG_GVRP) { |
271 | if (vlan->flags & VLAN_FLAG_GVRP) | 271 | if (vlan->flags & VLAN_FLAG_GVRP) |
272 | vlan_gvrp_request_join(dev); | 272 | vlan_gvrp_request_join(dev); |
273 | else | 273 | else |
274 | vlan_gvrp_request_leave(dev); | 274 | vlan_gvrp_request_leave(dev); |
275 | } | 275 | } |
276 | 276 | ||
277 | if (netif_running(dev) && (vlan->flags ^ old_flags) & VLAN_FLAG_MVRP) { | 277 | if (netif_running(dev) && (vlan->flags ^ old_flags) & VLAN_FLAG_MVRP) { |
278 | if (vlan->flags & VLAN_FLAG_MVRP) | 278 | if (vlan->flags & VLAN_FLAG_MVRP) |
279 | vlan_mvrp_request_join(dev); | 279 | vlan_mvrp_request_join(dev); |
280 | else | 280 | else |
281 | vlan_mvrp_request_leave(dev); | 281 | vlan_mvrp_request_leave(dev); |
282 | } | 282 | } |
283 | return 0; | 283 | return 0; |
284 | } | 284 | } |
285 | 285 | ||
286 | void vlan_dev_get_realdev_name(const struct net_device *dev, char *result) | 286 | void vlan_dev_get_realdev_name(const struct net_device *dev, char *result) |
287 | { | 287 | { |
288 | strncpy(result, vlan_dev_priv(dev)->real_dev->name, 23); | 288 | strncpy(result, vlan_dev_priv(dev)->real_dev->name, 23); |
289 | } | 289 | } |
290 | 290 | ||
291 | static int vlan_dev_open(struct net_device *dev) | 291 | static int vlan_dev_open(struct net_device *dev) |
292 | { | 292 | { |
293 | struct vlan_dev_priv *vlan = vlan_dev_priv(dev); | 293 | struct vlan_dev_priv *vlan = vlan_dev_priv(dev); |
294 | struct net_device *real_dev = vlan->real_dev; | 294 | struct net_device *real_dev = vlan->real_dev; |
295 | int err; | 295 | int err; |
296 | 296 | ||
297 | if (!(real_dev->flags & IFF_UP) && | 297 | if (!(real_dev->flags & IFF_UP) && |
298 | !(vlan->flags & VLAN_FLAG_LOOSE_BINDING)) | 298 | !(vlan->flags & VLAN_FLAG_LOOSE_BINDING)) |
299 | return -ENETDOWN; | 299 | return -ENETDOWN; |
300 | 300 | ||
301 | if (!ether_addr_equal(dev->dev_addr, real_dev->dev_addr)) { | 301 | if (!ether_addr_equal(dev->dev_addr, real_dev->dev_addr)) { |
302 | err = dev_uc_add(real_dev, dev->dev_addr); | 302 | err = dev_uc_add(real_dev, dev->dev_addr); |
303 | if (err < 0) | 303 | if (err < 0) |
304 | goto out; | 304 | goto out; |
305 | } | 305 | } |
306 | 306 | ||
307 | if (dev->flags & IFF_ALLMULTI) { | 307 | if (dev->flags & IFF_ALLMULTI) { |
308 | err = dev_set_allmulti(real_dev, 1); | 308 | err = dev_set_allmulti(real_dev, 1); |
309 | if (err < 0) | 309 | if (err < 0) |
310 | goto del_unicast; | 310 | goto del_unicast; |
311 | } | 311 | } |
312 | if (dev->flags & IFF_PROMISC) { | 312 | if (dev->flags & IFF_PROMISC) { |
313 | err = dev_set_promiscuity(real_dev, 1); | 313 | err = dev_set_promiscuity(real_dev, 1); |
314 | if (err < 0) | 314 | if (err < 0) |
315 | goto clear_allmulti; | 315 | goto clear_allmulti; |
316 | } | 316 | } |
317 | 317 | ||
318 | memcpy(vlan->real_dev_addr, real_dev->dev_addr, ETH_ALEN); | 318 | memcpy(vlan->real_dev_addr, real_dev->dev_addr, ETH_ALEN); |
319 | 319 | ||
320 | if (vlan->flags & VLAN_FLAG_GVRP) | 320 | if (vlan->flags & VLAN_FLAG_GVRP) |
321 | vlan_gvrp_request_join(dev); | 321 | vlan_gvrp_request_join(dev); |
322 | 322 | ||
323 | if (vlan->flags & VLAN_FLAG_MVRP) | 323 | if (vlan->flags & VLAN_FLAG_MVRP) |
324 | vlan_mvrp_request_join(dev); | 324 | vlan_mvrp_request_join(dev); |
325 | 325 | ||
326 | if (netif_carrier_ok(real_dev)) | 326 | if (netif_carrier_ok(real_dev)) |
327 | netif_carrier_on(dev); | 327 | netif_carrier_on(dev); |
328 | return 0; | 328 | return 0; |
329 | 329 | ||
330 | clear_allmulti: | 330 | clear_allmulti: |
331 | if (dev->flags & IFF_ALLMULTI) | 331 | if (dev->flags & IFF_ALLMULTI) |
332 | dev_set_allmulti(real_dev, -1); | 332 | dev_set_allmulti(real_dev, -1); |
333 | del_unicast: | 333 | del_unicast: |
334 | if (!ether_addr_equal(dev->dev_addr, real_dev->dev_addr)) | 334 | if (!ether_addr_equal(dev->dev_addr, real_dev->dev_addr)) |
335 | dev_uc_del(real_dev, dev->dev_addr); | 335 | dev_uc_del(real_dev, dev->dev_addr); |
336 | out: | 336 | out: |
337 | netif_carrier_off(dev); | 337 | netif_carrier_off(dev); |
338 | return err; | 338 | return err; |
339 | } | 339 | } |
340 | 340 | ||
341 | static int vlan_dev_stop(struct net_device *dev) | 341 | static int vlan_dev_stop(struct net_device *dev) |
342 | { | 342 | { |
343 | struct vlan_dev_priv *vlan = vlan_dev_priv(dev); | 343 | struct vlan_dev_priv *vlan = vlan_dev_priv(dev); |
344 | struct net_device *real_dev = vlan->real_dev; | 344 | struct net_device *real_dev = vlan->real_dev; |
345 | 345 | ||
346 | dev_mc_unsync(real_dev, dev); | 346 | dev_mc_unsync(real_dev, dev); |
347 | dev_uc_unsync(real_dev, dev); | 347 | dev_uc_unsync(real_dev, dev); |
348 | if (dev->flags & IFF_ALLMULTI) | 348 | if (dev->flags & IFF_ALLMULTI) |
349 | dev_set_allmulti(real_dev, -1); | 349 | dev_set_allmulti(real_dev, -1); |
350 | if (dev->flags & IFF_PROMISC) | 350 | if (dev->flags & IFF_PROMISC) |
351 | dev_set_promiscuity(real_dev, -1); | 351 | dev_set_promiscuity(real_dev, -1); |
352 | 352 | ||
353 | if (!ether_addr_equal(dev->dev_addr, real_dev->dev_addr)) | 353 | if (!ether_addr_equal(dev->dev_addr, real_dev->dev_addr)) |
354 | dev_uc_del(real_dev, dev->dev_addr); | 354 | dev_uc_del(real_dev, dev->dev_addr); |
355 | 355 | ||
356 | netif_carrier_off(dev); | 356 | netif_carrier_off(dev); |
357 | return 0; | 357 | return 0; |
358 | } | 358 | } |
359 | 359 | ||
360 | static int vlan_dev_set_mac_address(struct net_device *dev, void *p) | 360 | static int vlan_dev_set_mac_address(struct net_device *dev, void *p) |
361 | { | 361 | { |
362 | struct net_device *real_dev = vlan_dev_priv(dev)->real_dev; | 362 | struct net_device *real_dev = vlan_dev_priv(dev)->real_dev; |
363 | struct sockaddr *addr = p; | 363 | struct sockaddr *addr = p; |
364 | int err; | 364 | int err; |
365 | 365 | ||
366 | if (!is_valid_ether_addr(addr->sa_data)) | 366 | if (!is_valid_ether_addr(addr->sa_data)) |
367 | return -EADDRNOTAVAIL; | 367 | return -EADDRNOTAVAIL; |
368 | 368 | ||
369 | if (!(dev->flags & IFF_UP)) | 369 | if (!(dev->flags & IFF_UP)) |
370 | goto out; | 370 | goto out; |
371 | 371 | ||
372 | if (!ether_addr_equal(addr->sa_data, real_dev->dev_addr)) { | 372 | if (!ether_addr_equal(addr->sa_data, real_dev->dev_addr)) { |
373 | err = dev_uc_add(real_dev, addr->sa_data); | 373 | err = dev_uc_add(real_dev, addr->sa_data); |
374 | if (err < 0) | 374 | if (err < 0) |
375 | return err; | 375 | return err; |
376 | } | 376 | } |
377 | 377 | ||
378 | if (!ether_addr_equal(dev->dev_addr, real_dev->dev_addr)) | 378 | if (!ether_addr_equal(dev->dev_addr, real_dev->dev_addr)) |
379 | dev_uc_del(real_dev, dev->dev_addr); | 379 | dev_uc_del(real_dev, dev->dev_addr); |
380 | 380 | ||
381 | out: | 381 | out: |
382 | memcpy(dev->dev_addr, addr->sa_data, ETH_ALEN); | 382 | memcpy(dev->dev_addr, addr->sa_data, ETH_ALEN); |
383 | return 0; | 383 | return 0; |
384 | } | 384 | } |
385 | 385 | ||
386 | static int vlan_dev_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) | 386 | static int vlan_dev_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) |
387 | { | 387 | { |
388 | struct net_device *real_dev = vlan_dev_priv(dev)->real_dev; | 388 | struct net_device *real_dev = vlan_dev_priv(dev)->real_dev; |
389 | const struct net_device_ops *ops = real_dev->netdev_ops; | 389 | const struct net_device_ops *ops = real_dev->netdev_ops; |
390 | struct ifreq ifrr; | 390 | struct ifreq ifrr; |
391 | int err = -EOPNOTSUPP; | 391 | int err = -EOPNOTSUPP; |
392 | 392 | ||
393 | strncpy(ifrr.ifr_name, real_dev->name, IFNAMSIZ); | 393 | strncpy(ifrr.ifr_name, real_dev->name, IFNAMSIZ); |
394 | ifrr.ifr_ifru = ifr->ifr_ifru; | 394 | ifrr.ifr_ifru = ifr->ifr_ifru; |
395 | 395 | ||
396 | switch (cmd) { | 396 | switch (cmd) { |
397 | case SIOCGMIIPHY: | 397 | case SIOCGMIIPHY: |
398 | case SIOCGMIIREG: | 398 | case SIOCGMIIREG: |
399 | case SIOCSMIIREG: | 399 | case SIOCSMIIREG: |
400 | if (netif_device_present(real_dev) && ops->ndo_do_ioctl) | 400 | if (netif_device_present(real_dev) && ops->ndo_do_ioctl) |
401 | err = ops->ndo_do_ioctl(real_dev, &ifrr, cmd); | 401 | err = ops->ndo_do_ioctl(real_dev, &ifrr, cmd); |
402 | break; | 402 | break; |
403 | } | 403 | } |
404 | 404 | ||
405 | if (!err) | 405 | if (!err) |
406 | ifr->ifr_ifru = ifrr.ifr_ifru; | 406 | ifr->ifr_ifru = ifrr.ifr_ifru; |
407 | 407 | ||
408 | return err; | 408 | return err; |
409 | } | 409 | } |
410 | 410 | ||
411 | static int vlan_dev_neigh_setup(struct net_device *dev, struct neigh_parms *pa) | 411 | static int vlan_dev_neigh_setup(struct net_device *dev, struct neigh_parms *pa) |
412 | { | 412 | { |
413 | struct net_device *real_dev = vlan_dev_priv(dev)->real_dev; | 413 | struct net_device *real_dev = vlan_dev_priv(dev)->real_dev; |
414 | const struct net_device_ops *ops = real_dev->netdev_ops; | 414 | const struct net_device_ops *ops = real_dev->netdev_ops; |
415 | int err = 0; | 415 | int err = 0; |
416 | 416 | ||
417 | if (netif_device_present(real_dev) && ops->ndo_neigh_setup) | 417 | if (netif_device_present(real_dev) && ops->ndo_neigh_setup) |
418 | err = ops->ndo_neigh_setup(real_dev, pa); | 418 | err = ops->ndo_neigh_setup(real_dev, pa); |
419 | 419 | ||
420 | return err; | 420 | return err; |
421 | } | 421 | } |
422 | 422 | ||
423 | #if IS_ENABLED(CONFIG_FCOE) | 423 | #if IS_ENABLED(CONFIG_FCOE) |
424 | static int vlan_dev_fcoe_ddp_setup(struct net_device *dev, u16 xid, | 424 | static int vlan_dev_fcoe_ddp_setup(struct net_device *dev, u16 xid, |
425 | struct scatterlist *sgl, unsigned int sgc) | 425 | struct scatterlist *sgl, unsigned int sgc) |
426 | { | 426 | { |
427 | struct net_device *real_dev = vlan_dev_priv(dev)->real_dev; | 427 | struct net_device *real_dev = vlan_dev_priv(dev)->real_dev; |
428 | const struct net_device_ops *ops = real_dev->netdev_ops; | 428 | const struct net_device_ops *ops = real_dev->netdev_ops; |
429 | int rc = 0; | 429 | int rc = 0; |
430 | 430 | ||
431 | if (ops->ndo_fcoe_ddp_setup) | 431 | if (ops->ndo_fcoe_ddp_setup) |
432 | rc = ops->ndo_fcoe_ddp_setup(real_dev, xid, sgl, sgc); | 432 | rc = ops->ndo_fcoe_ddp_setup(real_dev, xid, sgl, sgc); |
433 | 433 | ||
434 | return rc; | 434 | return rc; |
435 | } | 435 | } |
436 | 436 | ||
437 | static int vlan_dev_fcoe_ddp_done(struct net_device *dev, u16 xid) | 437 | static int vlan_dev_fcoe_ddp_done(struct net_device *dev, u16 xid) |
438 | { | 438 | { |
439 | struct net_device *real_dev = vlan_dev_priv(dev)->real_dev; | 439 | struct net_device *real_dev = vlan_dev_priv(dev)->real_dev; |
440 | const struct net_device_ops *ops = real_dev->netdev_ops; | 440 | const struct net_device_ops *ops = real_dev->netdev_ops; |
441 | int len = 0; | 441 | int len = 0; |
442 | 442 | ||
443 | if (ops->ndo_fcoe_ddp_done) | 443 | if (ops->ndo_fcoe_ddp_done) |
444 | len = ops->ndo_fcoe_ddp_done(real_dev, xid); | 444 | len = ops->ndo_fcoe_ddp_done(real_dev, xid); |
445 | 445 | ||
446 | return len; | 446 | return len; |
447 | } | 447 | } |
448 | 448 | ||
449 | static int vlan_dev_fcoe_enable(struct net_device *dev) | 449 | static int vlan_dev_fcoe_enable(struct net_device *dev) |
450 | { | 450 | { |
451 | struct net_device *real_dev = vlan_dev_priv(dev)->real_dev; | 451 | struct net_device *real_dev = vlan_dev_priv(dev)->real_dev; |
452 | const struct net_device_ops *ops = real_dev->netdev_ops; | 452 | const struct net_device_ops *ops = real_dev->netdev_ops; |
453 | int rc = -EINVAL; | 453 | int rc = -EINVAL; |
454 | 454 | ||
455 | if (ops->ndo_fcoe_enable) | 455 | if (ops->ndo_fcoe_enable) |
456 | rc = ops->ndo_fcoe_enable(real_dev); | 456 | rc = ops->ndo_fcoe_enable(real_dev); |
457 | return rc; | 457 | return rc; |
458 | } | 458 | } |
459 | 459 | ||
460 | static int vlan_dev_fcoe_disable(struct net_device *dev) | 460 | static int vlan_dev_fcoe_disable(struct net_device *dev) |
461 | { | 461 | { |
462 | struct net_device *real_dev = vlan_dev_priv(dev)->real_dev; | 462 | struct net_device *real_dev = vlan_dev_priv(dev)->real_dev; |
463 | const struct net_device_ops *ops = real_dev->netdev_ops; | 463 | const struct net_device_ops *ops = real_dev->netdev_ops; |
464 | int rc = -EINVAL; | 464 | int rc = -EINVAL; |
465 | 465 | ||
466 | if (ops->ndo_fcoe_disable) | 466 | if (ops->ndo_fcoe_disable) |
467 | rc = ops->ndo_fcoe_disable(real_dev); | 467 | rc = ops->ndo_fcoe_disable(real_dev); |
468 | return rc; | 468 | return rc; |
469 | } | 469 | } |
470 | 470 | ||
471 | static int vlan_dev_fcoe_get_wwn(struct net_device *dev, u64 *wwn, int type) | 471 | static int vlan_dev_fcoe_get_wwn(struct net_device *dev, u64 *wwn, int type) |
472 | { | 472 | { |
473 | struct net_device *real_dev = vlan_dev_priv(dev)->real_dev; | 473 | struct net_device *real_dev = vlan_dev_priv(dev)->real_dev; |
474 | const struct net_device_ops *ops = real_dev->netdev_ops; | 474 | const struct net_device_ops *ops = real_dev->netdev_ops; |
475 | int rc = -EINVAL; | 475 | int rc = -EINVAL; |
476 | 476 | ||
477 | if (ops->ndo_fcoe_get_wwn) | 477 | if (ops->ndo_fcoe_get_wwn) |
478 | rc = ops->ndo_fcoe_get_wwn(real_dev, wwn, type); | 478 | rc = ops->ndo_fcoe_get_wwn(real_dev, wwn, type); |
479 | return rc; | 479 | return rc; |
480 | } | 480 | } |
481 | 481 | ||
482 | static int vlan_dev_fcoe_ddp_target(struct net_device *dev, u16 xid, | 482 | static int vlan_dev_fcoe_ddp_target(struct net_device *dev, u16 xid, |
483 | struct scatterlist *sgl, unsigned int sgc) | 483 | struct scatterlist *sgl, unsigned int sgc) |
484 | { | 484 | { |
485 | struct net_device *real_dev = vlan_dev_priv(dev)->real_dev; | 485 | struct net_device *real_dev = vlan_dev_priv(dev)->real_dev; |
486 | const struct net_device_ops *ops = real_dev->netdev_ops; | 486 | const struct net_device_ops *ops = real_dev->netdev_ops; |
487 | int rc = 0; | 487 | int rc = 0; |
488 | 488 | ||
489 | if (ops->ndo_fcoe_ddp_target) | 489 | if (ops->ndo_fcoe_ddp_target) |
490 | rc = ops->ndo_fcoe_ddp_target(real_dev, xid, sgl, sgc); | 490 | rc = ops->ndo_fcoe_ddp_target(real_dev, xid, sgl, sgc); |
491 | 491 | ||
492 | return rc; | 492 | return rc; |
493 | } | 493 | } |
494 | #endif | 494 | #endif |
495 | 495 | ||
496 | static void vlan_dev_change_rx_flags(struct net_device *dev, int change) | 496 | static void vlan_dev_change_rx_flags(struct net_device *dev, int change) |
497 | { | 497 | { |
498 | struct net_device *real_dev = vlan_dev_priv(dev)->real_dev; | 498 | struct net_device *real_dev = vlan_dev_priv(dev)->real_dev; |
499 | 499 | ||
500 | if (dev->flags & IFF_UP) { | 500 | if (dev->flags & IFF_UP) { |
501 | if (change & IFF_ALLMULTI) | 501 | if (change & IFF_ALLMULTI) |
502 | dev_set_allmulti(real_dev, dev->flags & IFF_ALLMULTI ? 1 : -1); | 502 | dev_set_allmulti(real_dev, dev->flags & IFF_ALLMULTI ? 1 : -1); |
503 | if (change & IFF_PROMISC) | 503 | if (change & IFF_PROMISC) |
504 | dev_set_promiscuity(real_dev, dev->flags & IFF_PROMISC ? 1 : -1); | 504 | dev_set_promiscuity(real_dev, dev->flags & IFF_PROMISC ? 1 : -1); |
505 | } | 505 | } |
506 | } | 506 | } |
507 | 507 | ||
508 | static void vlan_dev_set_rx_mode(struct net_device *vlan_dev) | 508 | static void vlan_dev_set_rx_mode(struct net_device *vlan_dev) |
509 | { | 509 | { |
510 | dev_mc_sync(vlan_dev_priv(vlan_dev)->real_dev, vlan_dev); | 510 | dev_mc_sync(vlan_dev_priv(vlan_dev)->real_dev, vlan_dev); |
511 | dev_uc_sync(vlan_dev_priv(vlan_dev)->real_dev, vlan_dev); | 511 | dev_uc_sync(vlan_dev_priv(vlan_dev)->real_dev, vlan_dev); |
512 | } | 512 | } |
513 | 513 | ||
514 | /* | 514 | /* |
515 | * vlan network devices have devices nesting below it, and are a special | 515 | * vlan network devices have devices nesting below it, and are a special |
516 | * "super class" of normal network devices; split their locks off into a | 516 | * "super class" of normal network devices; split their locks off into a |
517 | * separate class since they always nest. | 517 | * separate class since they always nest. |
518 | */ | 518 | */ |
519 | static struct lock_class_key vlan_netdev_xmit_lock_key; | 519 | static struct lock_class_key vlan_netdev_xmit_lock_key; |
520 | static struct lock_class_key vlan_netdev_addr_lock_key; | 520 | static struct lock_class_key vlan_netdev_addr_lock_key; |
521 | 521 | ||
522 | static void vlan_dev_set_lockdep_one(struct net_device *dev, | 522 | static void vlan_dev_set_lockdep_one(struct net_device *dev, |
523 | struct netdev_queue *txq, | 523 | struct netdev_queue *txq, |
524 | void *_subclass) | 524 | void *_subclass) |
525 | { | 525 | { |
526 | lockdep_set_class_and_subclass(&txq->_xmit_lock, | 526 | lockdep_set_class_and_subclass(&txq->_xmit_lock, |
527 | &vlan_netdev_xmit_lock_key, | 527 | &vlan_netdev_xmit_lock_key, |
528 | *(int *)_subclass); | 528 | *(int *)_subclass); |
529 | } | 529 | } |
530 | 530 | ||
531 | static void vlan_dev_set_lockdep_class(struct net_device *dev, int subclass) | 531 | static void vlan_dev_set_lockdep_class(struct net_device *dev, int subclass) |
532 | { | 532 | { |
533 | lockdep_set_class_and_subclass(&dev->addr_list_lock, | 533 | lockdep_set_class_and_subclass(&dev->addr_list_lock, |
534 | &vlan_netdev_addr_lock_key, | 534 | &vlan_netdev_addr_lock_key, |
535 | subclass); | 535 | subclass); |
536 | netdev_for_each_tx_queue(dev, vlan_dev_set_lockdep_one, &subclass); | 536 | netdev_for_each_tx_queue(dev, vlan_dev_set_lockdep_one, &subclass); |
537 | } | 537 | } |
538 | 538 | ||
539 | static const struct header_ops vlan_header_ops = { | 539 | static const struct header_ops vlan_header_ops = { |
540 | .create = vlan_dev_hard_header, | 540 | .create = vlan_dev_hard_header, |
541 | .rebuild = vlan_dev_rebuild_header, | 541 | .rebuild = vlan_dev_rebuild_header, |
542 | .parse = eth_header_parse, | 542 | .parse = eth_header_parse, |
543 | }; | 543 | }; |
544 | 544 | ||
545 | static struct device_type vlan_type = { | 545 | static struct device_type vlan_type = { |
546 | .name = "vlan", | 546 | .name = "vlan", |
547 | }; | 547 | }; |
548 | 548 | ||
549 | static const struct net_device_ops vlan_netdev_ops; | 549 | static const struct net_device_ops vlan_netdev_ops; |
550 | 550 | ||
551 | static int vlan_dev_init(struct net_device *dev) | 551 | static int vlan_dev_init(struct net_device *dev) |
552 | { | 552 | { |
553 | struct net_device *real_dev = vlan_dev_priv(dev)->real_dev; | 553 | struct net_device *real_dev = vlan_dev_priv(dev)->real_dev; |
554 | int subclass = 0; | 554 | int subclass = 0; |
555 | 555 | ||
556 | netif_carrier_off(dev); | 556 | netif_carrier_off(dev); |
557 | 557 | ||
558 | /* IFF_BROADCAST|IFF_MULTICAST; ??? */ | 558 | /* IFF_BROADCAST|IFF_MULTICAST; ??? */ |
559 | dev->flags = real_dev->flags & ~(IFF_UP | IFF_PROMISC | IFF_ALLMULTI | | 559 | dev->flags = real_dev->flags & ~(IFF_UP | IFF_PROMISC | IFF_ALLMULTI | |
560 | IFF_MASTER | IFF_SLAVE); | 560 | IFF_MASTER | IFF_SLAVE); |
561 | dev->iflink = real_dev->ifindex; | 561 | dev->iflink = real_dev->ifindex; |
562 | dev->state = (real_dev->state & ((1<<__LINK_STATE_NOCARRIER) | | 562 | dev->state = (real_dev->state & ((1<<__LINK_STATE_NOCARRIER) | |
563 | (1<<__LINK_STATE_DORMANT))) | | 563 | (1<<__LINK_STATE_DORMANT))) | |
564 | (1<<__LINK_STATE_PRESENT); | 564 | (1<<__LINK_STATE_PRESENT); |
565 | 565 | ||
566 | dev->hw_features = NETIF_F_ALL_CSUM | NETIF_F_SG | | 566 | dev->hw_features = NETIF_F_ALL_CSUM | NETIF_F_SG | |
567 | NETIF_F_FRAGLIST | NETIF_F_ALL_TSO | | 567 | NETIF_F_FRAGLIST | NETIF_F_ALL_TSO | |
568 | NETIF_F_HIGHDMA | NETIF_F_SCTP_CSUM | | 568 | NETIF_F_HIGHDMA | NETIF_F_SCTP_CSUM | |
569 | NETIF_F_ALL_FCOE; | 569 | NETIF_F_ALL_FCOE; |
570 | 570 | ||
571 | dev->features |= real_dev->vlan_features | NETIF_F_LLTX; | 571 | dev->features |= real_dev->vlan_features | NETIF_F_LLTX; |
572 | dev->gso_max_size = real_dev->gso_max_size; | 572 | dev->gso_max_size = real_dev->gso_max_size; |
573 | 573 | ||
574 | /* ipv6 shared card related stuff */ | 574 | /* ipv6 shared card related stuff */ |
575 | dev->dev_id = real_dev->dev_id; | 575 | dev->dev_id = real_dev->dev_id; |
576 | 576 | ||
577 | if (is_zero_ether_addr(dev->dev_addr)) | 577 | if (is_zero_ether_addr(dev->dev_addr)) |
578 | memcpy(dev->dev_addr, real_dev->dev_addr, dev->addr_len); | 578 | memcpy(dev->dev_addr, real_dev->dev_addr, dev->addr_len); |
579 | if (is_zero_ether_addr(dev->broadcast)) | 579 | if (is_zero_ether_addr(dev->broadcast)) |
580 | memcpy(dev->broadcast, real_dev->broadcast, dev->addr_len); | 580 | memcpy(dev->broadcast, real_dev->broadcast, dev->addr_len); |
581 | 581 | ||
582 | #if IS_ENABLED(CONFIG_FCOE) | 582 | #if IS_ENABLED(CONFIG_FCOE) |
583 | dev->fcoe_ddp_xid = real_dev->fcoe_ddp_xid; | 583 | dev->fcoe_ddp_xid = real_dev->fcoe_ddp_xid; |
584 | #endif | 584 | #endif |
585 | 585 | ||
586 | dev->needed_headroom = real_dev->needed_headroom; | 586 | dev->needed_headroom = real_dev->needed_headroom; |
587 | if (real_dev->features & NETIF_F_HW_VLAN_CTAG_TX) { | 587 | if (real_dev->features & NETIF_F_HW_VLAN_CTAG_TX) { |
588 | dev->header_ops = real_dev->header_ops; | 588 | dev->header_ops = real_dev->header_ops; |
589 | dev->hard_header_len = real_dev->hard_header_len; | 589 | dev->hard_header_len = real_dev->hard_header_len; |
590 | } else { | 590 | } else { |
591 | dev->header_ops = &vlan_header_ops; | 591 | dev->header_ops = &vlan_header_ops; |
592 | dev->hard_header_len = real_dev->hard_header_len + VLAN_HLEN; | 592 | dev->hard_header_len = real_dev->hard_header_len + VLAN_HLEN; |
593 | } | 593 | } |
594 | 594 | ||
595 | dev->netdev_ops = &vlan_netdev_ops; | 595 | dev->netdev_ops = &vlan_netdev_ops; |
596 | 596 | ||
597 | SET_NETDEV_DEVTYPE(dev, &vlan_type); | 597 | SET_NETDEV_DEVTYPE(dev, &vlan_type); |
598 | 598 | ||
599 | if (is_vlan_dev(real_dev)) | 599 | if (is_vlan_dev(real_dev)) |
600 | subclass = 1; | 600 | subclass = 1; |
601 | 601 | ||
602 | vlan_dev_set_lockdep_class(dev, subclass); | 602 | vlan_dev_set_lockdep_class(dev, subclass); |
603 | 603 | ||
604 | vlan_dev_priv(dev)->vlan_pcpu_stats = alloc_percpu(struct vlan_pcpu_stats); | 604 | vlan_dev_priv(dev)->vlan_pcpu_stats = alloc_percpu(struct vlan_pcpu_stats); |
605 | if (!vlan_dev_priv(dev)->vlan_pcpu_stats) | 605 | if (!vlan_dev_priv(dev)->vlan_pcpu_stats) |
606 | return -ENOMEM; | 606 | return -ENOMEM; |
607 | 607 | ||
608 | return 0; | 608 | return 0; |
609 | } | 609 | } |
610 | 610 | ||
611 | static void vlan_dev_uninit(struct net_device *dev) | 611 | static void vlan_dev_uninit(struct net_device *dev) |
612 | { | 612 | { |
613 | struct vlan_priority_tci_mapping *pm; | 613 | struct vlan_priority_tci_mapping *pm; |
614 | struct vlan_dev_priv *vlan = vlan_dev_priv(dev); | 614 | struct vlan_dev_priv *vlan = vlan_dev_priv(dev); |
615 | int i; | 615 | int i; |
616 | 616 | ||
617 | free_percpu(vlan->vlan_pcpu_stats); | 617 | free_percpu(vlan->vlan_pcpu_stats); |
618 | vlan->vlan_pcpu_stats = NULL; | 618 | vlan->vlan_pcpu_stats = NULL; |
619 | for (i = 0; i < ARRAY_SIZE(vlan->egress_priority_map); i++) { | 619 | for (i = 0; i < ARRAY_SIZE(vlan->egress_priority_map); i++) { |
620 | while ((pm = vlan->egress_priority_map[i]) != NULL) { | 620 | while ((pm = vlan->egress_priority_map[i]) != NULL) { |
621 | vlan->egress_priority_map[i] = pm->next; | 621 | vlan->egress_priority_map[i] = pm->next; |
622 | kfree(pm); | 622 | kfree(pm); |
623 | } | 623 | } |
624 | } | 624 | } |
625 | } | 625 | } |
626 | 626 | ||
627 | static netdev_features_t vlan_dev_fix_features(struct net_device *dev, | 627 | static netdev_features_t vlan_dev_fix_features(struct net_device *dev, |
628 | netdev_features_t features) | 628 | netdev_features_t features) |
629 | { | 629 | { |
630 | struct net_device *real_dev = vlan_dev_priv(dev)->real_dev; | 630 | struct net_device *real_dev = vlan_dev_priv(dev)->real_dev; |
631 | u32 old_features = features; | 631 | netdev_features_t old_features = features; |
632 | 632 | ||
633 | features &= real_dev->vlan_features; | 633 | features &= real_dev->vlan_features; |
634 | features |= NETIF_F_RXCSUM; | 634 | features |= NETIF_F_RXCSUM; |
635 | features &= real_dev->features; | 635 | features &= real_dev->features; |
636 | 636 | ||
637 | features |= old_features & NETIF_F_SOFT_FEATURES; | 637 | features |= old_features & NETIF_F_SOFT_FEATURES; |
638 | features |= NETIF_F_LLTX; | 638 | features |= NETIF_F_LLTX; |
639 | 639 | ||
640 | return features; | 640 | return features; |
641 | } | 641 | } |
642 | 642 | ||
643 | static int vlan_ethtool_get_settings(struct net_device *dev, | 643 | static int vlan_ethtool_get_settings(struct net_device *dev, |
644 | struct ethtool_cmd *cmd) | 644 | struct ethtool_cmd *cmd) |
645 | { | 645 | { |
646 | const struct vlan_dev_priv *vlan = vlan_dev_priv(dev); | 646 | const struct vlan_dev_priv *vlan = vlan_dev_priv(dev); |
647 | 647 | ||
648 | return __ethtool_get_settings(vlan->real_dev, cmd); | 648 | return __ethtool_get_settings(vlan->real_dev, cmd); |
649 | } | 649 | } |
650 | 650 | ||
651 | static void vlan_ethtool_get_drvinfo(struct net_device *dev, | 651 | static void vlan_ethtool_get_drvinfo(struct net_device *dev, |
652 | struct ethtool_drvinfo *info) | 652 | struct ethtool_drvinfo *info) |
653 | { | 653 | { |
654 | strlcpy(info->driver, vlan_fullname, sizeof(info->driver)); | 654 | strlcpy(info->driver, vlan_fullname, sizeof(info->driver)); |
655 | strlcpy(info->version, vlan_version, sizeof(info->version)); | 655 | strlcpy(info->version, vlan_version, sizeof(info->version)); |
656 | strlcpy(info->fw_version, "N/A", sizeof(info->fw_version)); | 656 | strlcpy(info->fw_version, "N/A", sizeof(info->fw_version)); |
657 | } | 657 | } |
658 | 658 | ||
659 | static struct rtnl_link_stats64 *vlan_dev_get_stats64(struct net_device *dev, struct rtnl_link_stats64 *stats) | 659 | static struct rtnl_link_stats64 *vlan_dev_get_stats64(struct net_device *dev, struct rtnl_link_stats64 *stats) |
660 | { | 660 | { |
661 | 661 | ||
662 | if (vlan_dev_priv(dev)->vlan_pcpu_stats) { | 662 | if (vlan_dev_priv(dev)->vlan_pcpu_stats) { |
663 | struct vlan_pcpu_stats *p; | 663 | struct vlan_pcpu_stats *p; |
664 | u32 rx_errors = 0, tx_dropped = 0; | 664 | u32 rx_errors = 0, tx_dropped = 0; |
665 | int i; | 665 | int i; |
666 | 666 | ||
667 | for_each_possible_cpu(i) { | 667 | for_each_possible_cpu(i) { |
668 | u64 rxpackets, rxbytes, rxmulticast, txpackets, txbytes; | 668 | u64 rxpackets, rxbytes, rxmulticast, txpackets, txbytes; |
669 | unsigned int start; | 669 | unsigned int start; |
670 | 670 | ||
671 | p = per_cpu_ptr(vlan_dev_priv(dev)->vlan_pcpu_stats, i); | 671 | p = per_cpu_ptr(vlan_dev_priv(dev)->vlan_pcpu_stats, i); |
672 | do { | 672 | do { |
673 | start = u64_stats_fetch_begin_bh(&p->syncp); | 673 | start = u64_stats_fetch_begin_bh(&p->syncp); |
674 | rxpackets = p->rx_packets; | 674 | rxpackets = p->rx_packets; |
675 | rxbytes = p->rx_bytes; | 675 | rxbytes = p->rx_bytes; |
676 | rxmulticast = p->rx_multicast; | 676 | rxmulticast = p->rx_multicast; |
677 | txpackets = p->tx_packets; | 677 | txpackets = p->tx_packets; |
678 | txbytes = p->tx_bytes; | 678 | txbytes = p->tx_bytes; |
679 | } while (u64_stats_fetch_retry_bh(&p->syncp, start)); | 679 | } while (u64_stats_fetch_retry_bh(&p->syncp, start)); |
680 | 680 | ||
681 | stats->rx_packets += rxpackets; | 681 | stats->rx_packets += rxpackets; |
682 | stats->rx_bytes += rxbytes; | 682 | stats->rx_bytes += rxbytes; |
683 | stats->multicast += rxmulticast; | 683 | stats->multicast += rxmulticast; |
684 | stats->tx_packets += txpackets; | 684 | stats->tx_packets += txpackets; |
685 | stats->tx_bytes += txbytes; | 685 | stats->tx_bytes += txbytes; |
686 | /* rx_errors & tx_dropped are u32 */ | 686 | /* rx_errors & tx_dropped are u32 */ |
687 | rx_errors += p->rx_errors; | 687 | rx_errors += p->rx_errors; |
688 | tx_dropped += p->tx_dropped; | 688 | tx_dropped += p->tx_dropped; |
689 | } | 689 | } |
690 | stats->rx_errors = rx_errors; | 690 | stats->rx_errors = rx_errors; |
691 | stats->tx_dropped = tx_dropped; | 691 | stats->tx_dropped = tx_dropped; |
692 | } | 692 | } |
693 | return stats; | 693 | return stats; |
694 | } | 694 | } |
695 | 695 | ||
696 | #ifdef CONFIG_NET_POLL_CONTROLLER | 696 | #ifdef CONFIG_NET_POLL_CONTROLLER |
697 | static void vlan_dev_poll_controller(struct net_device *dev) | 697 | static void vlan_dev_poll_controller(struct net_device *dev) |
698 | { | 698 | { |
699 | return; | 699 | return; |
700 | } | 700 | } |
701 | 701 | ||
702 | static int vlan_dev_netpoll_setup(struct net_device *dev, struct netpoll_info *npinfo, | 702 | static int vlan_dev_netpoll_setup(struct net_device *dev, struct netpoll_info *npinfo, |
703 | gfp_t gfp) | 703 | gfp_t gfp) |
704 | { | 704 | { |
705 | struct vlan_dev_priv *vlan = vlan_dev_priv(dev); | 705 | struct vlan_dev_priv *vlan = vlan_dev_priv(dev); |
706 | struct net_device *real_dev = vlan->real_dev; | 706 | struct net_device *real_dev = vlan->real_dev; |
707 | struct netpoll *netpoll; | 707 | struct netpoll *netpoll; |
708 | int err = 0; | 708 | int err = 0; |
709 | 709 | ||
710 | netpoll = kzalloc(sizeof(*netpoll), gfp); | 710 | netpoll = kzalloc(sizeof(*netpoll), gfp); |
711 | err = -ENOMEM; | 711 | err = -ENOMEM; |
712 | if (!netpoll) | 712 | if (!netpoll) |
713 | goto out; | 713 | goto out; |
714 | 714 | ||
715 | err = __netpoll_setup(netpoll, real_dev, gfp); | 715 | err = __netpoll_setup(netpoll, real_dev, gfp); |
716 | if (err) { | 716 | if (err) { |
717 | kfree(netpoll); | 717 | kfree(netpoll); |
718 | goto out; | 718 | goto out; |
719 | } | 719 | } |
720 | 720 | ||
721 | vlan->netpoll = netpoll; | 721 | vlan->netpoll = netpoll; |
722 | 722 | ||
723 | out: | 723 | out: |
724 | return err; | 724 | return err; |
725 | } | 725 | } |
726 | 726 | ||
727 | static void vlan_dev_netpoll_cleanup(struct net_device *dev) | 727 | static void vlan_dev_netpoll_cleanup(struct net_device *dev) |
728 | { | 728 | { |
729 | struct vlan_dev_priv *vlan= vlan_dev_priv(dev); | 729 | struct vlan_dev_priv *vlan= vlan_dev_priv(dev); |
730 | struct netpoll *netpoll = vlan->netpoll; | 730 | struct netpoll *netpoll = vlan->netpoll; |
731 | 731 | ||
732 | if (!netpoll) | 732 | if (!netpoll) |
733 | return; | 733 | return; |
734 | 734 | ||
735 | vlan->netpoll = NULL; | 735 | vlan->netpoll = NULL; |
736 | 736 | ||
737 | __netpoll_free_async(netpoll); | 737 | __netpoll_free_async(netpoll); |
738 | } | 738 | } |
739 | #endif /* CONFIG_NET_POLL_CONTROLLER */ | 739 | #endif /* CONFIG_NET_POLL_CONTROLLER */ |
740 | 740 | ||
741 | static const struct ethtool_ops vlan_ethtool_ops = { | 741 | static const struct ethtool_ops vlan_ethtool_ops = { |
742 | .get_settings = vlan_ethtool_get_settings, | 742 | .get_settings = vlan_ethtool_get_settings, |
743 | .get_drvinfo = vlan_ethtool_get_drvinfo, | 743 | .get_drvinfo = vlan_ethtool_get_drvinfo, |
744 | .get_link = ethtool_op_get_link, | 744 | .get_link = ethtool_op_get_link, |
745 | }; | 745 | }; |
746 | 746 | ||
747 | static const struct net_device_ops vlan_netdev_ops = { | 747 | static const struct net_device_ops vlan_netdev_ops = { |
748 | .ndo_change_mtu = vlan_dev_change_mtu, | 748 | .ndo_change_mtu = vlan_dev_change_mtu, |
749 | .ndo_init = vlan_dev_init, | 749 | .ndo_init = vlan_dev_init, |
750 | .ndo_uninit = vlan_dev_uninit, | 750 | .ndo_uninit = vlan_dev_uninit, |
751 | .ndo_open = vlan_dev_open, | 751 | .ndo_open = vlan_dev_open, |
752 | .ndo_stop = vlan_dev_stop, | 752 | .ndo_stop = vlan_dev_stop, |
753 | .ndo_start_xmit = vlan_dev_hard_start_xmit, | 753 | .ndo_start_xmit = vlan_dev_hard_start_xmit, |
754 | .ndo_validate_addr = eth_validate_addr, | 754 | .ndo_validate_addr = eth_validate_addr, |
755 | .ndo_set_mac_address = vlan_dev_set_mac_address, | 755 | .ndo_set_mac_address = vlan_dev_set_mac_address, |
756 | .ndo_set_rx_mode = vlan_dev_set_rx_mode, | 756 | .ndo_set_rx_mode = vlan_dev_set_rx_mode, |
757 | .ndo_change_rx_flags = vlan_dev_change_rx_flags, | 757 | .ndo_change_rx_flags = vlan_dev_change_rx_flags, |
758 | .ndo_do_ioctl = vlan_dev_ioctl, | 758 | .ndo_do_ioctl = vlan_dev_ioctl, |
759 | .ndo_neigh_setup = vlan_dev_neigh_setup, | 759 | .ndo_neigh_setup = vlan_dev_neigh_setup, |
760 | .ndo_get_stats64 = vlan_dev_get_stats64, | 760 | .ndo_get_stats64 = vlan_dev_get_stats64, |
761 | #if IS_ENABLED(CONFIG_FCOE) | 761 | #if IS_ENABLED(CONFIG_FCOE) |
762 | .ndo_fcoe_ddp_setup = vlan_dev_fcoe_ddp_setup, | 762 | .ndo_fcoe_ddp_setup = vlan_dev_fcoe_ddp_setup, |
763 | .ndo_fcoe_ddp_done = vlan_dev_fcoe_ddp_done, | 763 | .ndo_fcoe_ddp_done = vlan_dev_fcoe_ddp_done, |
764 | .ndo_fcoe_enable = vlan_dev_fcoe_enable, | 764 | .ndo_fcoe_enable = vlan_dev_fcoe_enable, |
765 | .ndo_fcoe_disable = vlan_dev_fcoe_disable, | 765 | .ndo_fcoe_disable = vlan_dev_fcoe_disable, |
766 | .ndo_fcoe_get_wwn = vlan_dev_fcoe_get_wwn, | 766 | .ndo_fcoe_get_wwn = vlan_dev_fcoe_get_wwn, |
767 | .ndo_fcoe_ddp_target = vlan_dev_fcoe_ddp_target, | 767 | .ndo_fcoe_ddp_target = vlan_dev_fcoe_ddp_target, |
768 | #endif | 768 | #endif |
769 | #ifdef CONFIG_NET_POLL_CONTROLLER | 769 | #ifdef CONFIG_NET_POLL_CONTROLLER |
770 | .ndo_poll_controller = vlan_dev_poll_controller, | 770 | .ndo_poll_controller = vlan_dev_poll_controller, |
771 | .ndo_netpoll_setup = vlan_dev_netpoll_setup, | 771 | .ndo_netpoll_setup = vlan_dev_netpoll_setup, |
772 | .ndo_netpoll_cleanup = vlan_dev_netpoll_cleanup, | 772 | .ndo_netpoll_cleanup = vlan_dev_netpoll_cleanup, |
773 | #endif | 773 | #endif |
774 | .ndo_fix_features = vlan_dev_fix_features, | 774 | .ndo_fix_features = vlan_dev_fix_features, |
775 | }; | 775 | }; |
776 | 776 | ||
777 | void vlan_setup(struct net_device *dev) | 777 | void vlan_setup(struct net_device *dev) |
778 | { | 778 | { |
779 | ether_setup(dev); | 779 | ether_setup(dev); |
780 | 780 | ||
781 | dev->priv_flags |= IFF_802_1Q_VLAN; | 781 | dev->priv_flags |= IFF_802_1Q_VLAN; |
782 | dev->priv_flags &= ~(IFF_XMIT_DST_RELEASE | IFF_TX_SKB_SHARING); | 782 | dev->priv_flags &= ~(IFF_XMIT_DST_RELEASE | IFF_TX_SKB_SHARING); |
783 | dev->tx_queue_len = 0; | 783 | dev->tx_queue_len = 0; |
784 | 784 | ||
785 | dev->netdev_ops = &vlan_netdev_ops; | 785 | dev->netdev_ops = &vlan_netdev_ops; |
786 | dev->destructor = free_netdev; | 786 | dev->destructor = free_netdev; |
787 | dev->ethtool_ops = &vlan_ethtool_ops; | 787 | dev->ethtool_ops = &vlan_ethtool_ops; |
788 | 788 | ||
789 | memset(dev->broadcast, 0, ETH_ALEN); | 789 | memset(dev->broadcast, 0, ETH_ALEN); |
790 | } | 790 | } |
791 | 791 |
net/core/ethtool.c
1 | /* | 1 | /* |
2 | * net/core/ethtool.c - Ethtool ioctl handler | 2 | * net/core/ethtool.c - Ethtool ioctl handler |
3 | * Copyright (c) 2003 Matthew Wilcox <matthew@wil.cx> | 3 | * Copyright (c) 2003 Matthew Wilcox <matthew@wil.cx> |
4 | * | 4 | * |
5 | * This file is where we call all the ethtool_ops commands to get | 5 | * This file is where we call all the ethtool_ops commands to get |
6 | * the information ethtool needs. | 6 | * the information ethtool needs. |
7 | * | 7 | * |
8 | * This program is free software; you can redistribute it and/or modify | 8 | * This program is free software; you can redistribute it and/or modify |
9 | * it under the terms of the GNU General Public License as published by | 9 | * it under the terms of the GNU General Public License as published by |
10 | * the Free Software Foundation; either version 2 of the License, or | 10 | * the Free Software Foundation; either version 2 of the License, or |
11 | * (at your option) any later version. | 11 | * (at your option) any later version. |
12 | */ | 12 | */ |
13 | 13 | ||
14 | #include <linux/module.h> | 14 | #include <linux/module.h> |
15 | #include <linux/types.h> | 15 | #include <linux/types.h> |
16 | #include <linux/capability.h> | 16 | #include <linux/capability.h> |
17 | #include <linux/errno.h> | 17 | #include <linux/errno.h> |
18 | #include <linux/ethtool.h> | 18 | #include <linux/ethtool.h> |
19 | #include <linux/netdevice.h> | 19 | #include <linux/netdevice.h> |
20 | #include <linux/net_tstamp.h> | 20 | #include <linux/net_tstamp.h> |
21 | #include <linux/phy.h> | 21 | #include <linux/phy.h> |
22 | #include <linux/bitops.h> | 22 | #include <linux/bitops.h> |
23 | #include <linux/uaccess.h> | 23 | #include <linux/uaccess.h> |
24 | #include <linux/vmalloc.h> | 24 | #include <linux/vmalloc.h> |
25 | #include <linux/slab.h> | 25 | #include <linux/slab.h> |
26 | #include <linux/rtnetlink.h> | 26 | #include <linux/rtnetlink.h> |
27 | #include <linux/sched.h> | 27 | #include <linux/sched.h> |
28 | 28 | ||
29 | /* | 29 | /* |
30 | * Some useful ethtool_ops methods that're device independent. | 30 | * Some useful ethtool_ops methods that're device independent. |
31 | * If we find that all drivers want to do the same thing here, | 31 | * If we find that all drivers want to do the same thing here, |
32 | * we can turn these into dev_() function calls. | 32 | * we can turn these into dev_() function calls. |
33 | */ | 33 | */ |
34 | 34 | ||
35 | u32 ethtool_op_get_link(struct net_device *dev) | 35 | u32 ethtool_op_get_link(struct net_device *dev) |
36 | { | 36 | { |
37 | return netif_carrier_ok(dev) ? 1 : 0; | 37 | return netif_carrier_ok(dev) ? 1 : 0; |
38 | } | 38 | } |
39 | EXPORT_SYMBOL(ethtool_op_get_link); | 39 | EXPORT_SYMBOL(ethtool_op_get_link); |
40 | 40 | ||
41 | int ethtool_op_get_ts_info(struct net_device *dev, struct ethtool_ts_info *info) | 41 | int ethtool_op_get_ts_info(struct net_device *dev, struct ethtool_ts_info *info) |
42 | { | 42 | { |
43 | info->so_timestamping = | 43 | info->so_timestamping = |
44 | SOF_TIMESTAMPING_TX_SOFTWARE | | 44 | SOF_TIMESTAMPING_TX_SOFTWARE | |
45 | SOF_TIMESTAMPING_RX_SOFTWARE | | 45 | SOF_TIMESTAMPING_RX_SOFTWARE | |
46 | SOF_TIMESTAMPING_SOFTWARE; | 46 | SOF_TIMESTAMPING_SOFTWARE; |
47 | info->phc_index = -1; | 47 | info->phc_index = -1; |
48 | return 0; | 48 | return 0; |
49 | } | 49 | } |
50 | EXPORT_SYMBOL(ethtool_op_get_ts_info); | 50 | EXPORT_SYMBOL(ethtool_op_get_ts_info); |
51 | 51 | ||
52 | /* Handlers for each ethtool command */ | 52 | /* Handlers for each ethtool command */ |
53 | 53 | ||
54 | #define ETHTOOL_DEV_FEATURE_WORDS ((NETDEV_FEATURE_COUNT + 31) / 32) | 54 | #define ETHTOOL_DEV_FEATURE_WORDS ((NETDEV_FEATURE_COUNT + 31) / 32) |
55 | 55 | ||
56 | static const char netdev_features_strings[NETDEV_FEATURE_COUNT][ETH_GSTRING_LEN] = { | 56 | static const char netdev_features_strings[NETDEV_FEATURE_COUNT][ETH_GSTRING_LEN] = { |
57 | [NETIF_F_SG_BIT] = "tx-scatter-gather", | 57 | [NETIF_F_SG_BIT] = "tx-scatter-gather", |
58 | [NETIF_F_IP_CSUM_BIT] = "tx-checksum-ipv4", | 58 | [NETIF_F_IP_CSUM_BIT] = "tx-checksum-ipv4", |
59 | [NETIF_F_HW_CSUM_BIT] = "tx-checksum-ip-generic", | 59 | [NETIF_F_HW_CSUM_BIT] = "tx-checksum-ip-generic", |
60 | [NETIF_F_IPV6_CSUM_BIT] = "tx-checksum-ipv6", | 60 | [NETIF_F_IPV6_CSUM_BIT] = "tx-checksum-ipv6", |
61 | [NETIF_F_HIGHDMA_BIT] = "highdma", | 61 | [NETIF_F_HIGHDMA_BIT] = "highdma", |
62 | [NETIF_F_FRAGLIST_BIT] = "tx-scatter-gather-fraglist", | 62 | [NETIF_F_FRAGLIST_BIT] = "tx-scatter-gather-fraglist", |
63 | [NETIF_F_HW_VLAN_CTAG_TX_BIT] = "tx-vlan-ctag-hw-insert", | 63 | [NETIF_F_HW_VLAN_CTAG_TX_BIT] = "tx-vlan-ctag-hw-insert", |
64 | 64 | ||
65 | [NETIF_F_HW_VLAN_CTAG_RX_BIT] = "rx-vlan-ctag-hw-parse", | 65 | [NETIF_F_HW_VLAN_CTAG_RX_BIT] = "rx-vlan-ctag-hw-parse", |
66 | [NETIF_F_HW_VLAN_CTAG_FILTER_BIT] = "rx-vlan-ctag-filter", | 66 | [NETIF_F_HW_VLAN_CTAG_FILTER_BIT] = "rx-vlan-ctag-filter", |
67 | [NETIF_F_HW_VLAN_STAG_TX_BIT] = "tx-vlan-stag-hw-insert", | 67 | [NETIF_F_HW_VLAN_STAG_TX_BIT] = "tx-vlan-stag-hw-insert", |
68 | [NETIF_F_HW_VLAN_STAG_RX_BIT] = "rx-vlan-stag-hw-parse", | 68 | [NETIF_F_HW_VLAN_STAG_RX_BIT] = "rx-vlan-stag-hw-parse", |
69 | [NETIF_F_HW_VLAN_STAG_FILTER_BIT] = "rx-vlan-stag-filter", | 69 | [NETIF_F_HW_VLAN_STAG_FILTER_BIT] = "rx-vlan-stag-filter", |
70 | [NETIF_F_VLAN_CHALLENGED_BIT] = "vlan-challenged", | 70 | [NETIF_F_VLAN_CHALLENGED_BIT] = "vlan-challenged", |
71 | [NETIF_F_GSO_BIT] = "tx-generic-segmentation", | 71 | [NETIF_F_GSO_BIT] = "tx-generic-segmentation", |
72 | [NETIF_F_LLTX_BIT] = "tx-lockless", | 72 | [NETIF_F_LLTX_BIT] = "tx-lockless", |
73 | [NETIF_F_NETNS_LOCAL_BIT] = "netns-local", | 73 | [NETIF_F_NETNS_LOCAL_BIT] = "netns-local", |
74 | [NETIF_F_GRO_BIT] = "rx-gro", | 74 | [NETIF_F_GRO_BIT] = "rx-gro", |
75 | [NETIF_F_LRO_BIT] = "rx-lro", | 75 | [NETIF_F_LRO_BIT] = "rx-lro", |
76 | 76 | ||
77 | [NETIF_F_TSO_BIT] = "tx-tcp-segmentation", | 77 | [NETIF_F_TSO_BIT] = "tx-tcp-segmentation", |
78 | [NETIF_F_UFO_BIT] = "tx-udp-fragmentation", | 78 | [NETIF_F_UFO_BIT] = "tx-udp-fragmentation", |
79 | [NETIF_F_GSO_ROBUST_BIT] = "tx-gso-robust", | 79 | [NETIF_F_GSO_ROBUST_BIT] = "tx-gso-robust", |
80 | [NETIF_F_TSO_ECN_BIT] = "tx-tcp-ecn-segmentation", | 80 | [NETIF_F_TSO_ECN_BIT] = "tx-tcp-ecn-segmentation", |
81 | [NETIF_F_TSO6_BIT] = "tx-tcp6-segmentation", | 81 | [NETIF_F_TSO6_BIT] = "tx-tcp6-segmentation", |
82 | [NETIF_F_FSO_BIT] = "tx-fcoe-segmentation", | 82 | [NETIF_F_FSO_BIT] = "tx-fcoe-segmentation", |
83 | [NETIF_F_GSO_GRE_BIT] = "tx-gre-segmentation", | 83 | [NETIF_F_GSO_GRE_BIT] = "tx-gre-segmentation", |
84 | [NETIF_F_GSO_UDP_TUNNEL_BIT] = "tx-udp_tnl-segmentation", | 84 | [NETIF_F_GSO_UDP_TUNNEL_BIT] = "tx-udp_tnl-segmentation", |
85 | 85 | ||
86 | [NETIF_F_FCOE_CRC_BIT] = "tx-checksum-fcoe-crc", | 86 | [NETIF_F_FCOE_CRC_BIT] = "tx-checksum-fcoe-crc", |
87 | [NETIF_F_SCTP_CSUM_BIT] = "tx-checksum-sctp", | 87 | [NETIF_F_SCTP_CSUM_BIT] = "tx-checksum-sctp", |
88 | [NETIF_F_FCOE_MTU_BIT] = "fcoe-mtu", | 88 | [NETIF_F_FCOE_MTU_BIT] = "fcoe-mtu", |
89 | [NETIF_F_NTUPLE_BIT] = "rx-ntuple-filter", | 89 | [NETIF_F_NTUPLE_BIT] = "rx-ntuple-filter", |
90 | [NETIF_F_RXHASH_BIT] = "rx-hashing", | 90 | [NETIF_F_RXHASH_BIT] = "rx-hashing", |
91 | [NETIF_F_RXCSUM_BIT] = "rx-checksum", | 91 | [NETIF_F_RXCSUM_BIT] = "rx-checksum", |
92 | [NETIF_F_NOCACHE_COPY_BIT] = "tx-nocache-copy", | 92 | [NETIF_F_NOCACHE_COPY_BIT] = "tx-nocache-copy", |
93 | [NETIF_F_LOOPBACK_BIT] = "loopback", | 93 | [NETIF_F_LOOPBACK_BIT] = "loopback", |
94 | [NETIF_F_RXFCS_BIT] = "rx-fcs", | 94 | [NETIF_F_RXFCS_BIT] = "rx-fcs", |
95 | [NETIF_F_RXALL_BIT] = "rx-all", | 95 | [NETIF_F_RXALL_BIT] = "rx-all", |
96 | }; | 96 | }; |
97 | 97 | ||
98 | static int ethtool_get_features(struct net_device *dev, void __user *useraddr) | 98 | static int ethtool_get_features(struct net_device *dev, void __user *useraddr) |
99 | { | 99 | { |
100 | struct ethtool_gfeatures cmd = { | 100 | struct ethtool_gfeatures cmd = { |
101 | .cmd = ETHTOOL_GFEATURES, | 101 | .cmd = ETHTOOL_GFEATURES, |
102 | .size = ETHTOOL_DEV_FEATURE_WORDS, | 102 | .size = ETHTOOL_DEV_FEATURE_WORDS, |
103 | }; | 103 | }; |
104 | struct ethtool_get_features_block features[ETHTOOL_DEV_FEATURE_WORDS]; | 104 | struct ethtool_get_features_block features[ETHTOOL_DEV_FEATURE_WORDS]; |
105 | u32 __user *sizeaddr; | 105 | u32 __user *sizeaddr; |
106 | u32 copy_size; | 106 | u32 copy_size; |
107 | int i; | 107 | int i; |
108 | 108 | ||
109 | /* in case feature bits run out again */ | 109 | /* in case feature bits run out again */ |
110 | BUILD_BUG_ON(ETHTOOL_DEV_FEATURE_WORDS * sizeof(u32) > sizeof(netdev_features_t)); | 110 | BUILD_BUG_ON(ETHTOOL_DEV_FEATURE_WORDS * sizeof(u32) > sizeof(netdev_features_t)); |
111 | 111 | ||
112 | for (i = 0; i < ETHTOOL_DEV_FEATURE_WORDS; ++i) { | 112 | for (i = 0; i < ETHTOOL_DEV_FEATURE_WORDS; ++i) { |
113 | features[i].available = (u32)(dev->hw_features >> (32 * i)); | 113 | features[i].available = (u32)(dev->hw_features >> (32 * i)); |
114 | features[i].requested = (u32)(dev->wanted_features >> (32 * i)); | 114 | features[i].requested = (u32)(dev->wanted_features >> (32 * i)); |
115 | features[i].active = (u32)(dev->features >> (32 * i)); | 115 | features[i].active = (u32)(dev->features >> (32 * i)); |
116 | features[i].never_changed = | 116 | features[i].never_changed = |
117 | (u32)(NETIF_F_NEVER_CHANGE >> (32 * i)); | 117 | (u32)(NETIF_F_NEVER_CHANGE >> (32 * i)); |
118 | } | 118 | } |
119 | 119 | ||
120 | sizeaddr = useraddr + offsetof(struct ethtool_gfeatures, size); | 120 | sizeaddr = useraddr + offsetof(struct ethtool_gfeatures, size); |
121 | if (get_user(copy_size, sizeaddr)) | 121 | if (get_user(copy_size, sizeaddr)) |
122 | return -EFAULT; | 122 | return -EFAULT; |
123 | 123 | ||
124 | if (copy_size > ETHTOOL_DEV_FEATURE_WORDS) | 124 | if (copy_size > ETHTOOL_DEV_FEATURE_WORDS) |
125 | copy_size = ETHTOOL_DEV_FEATURE_WORDS; | 125 | copy_size = ETHTOOL_DEV_FEATURE_WORDS; |
126 | 126 | ||
127 | if (copy_to_user(useraddr, &cmd, sizeof(cmd))) | 127 | if (copy_to_user(useraddr, &cmd, sizeof(cmd))) |
128 | return -EFAULT; | 128 | return -EFAULT; |
129 | useraddr += sizeof(cmd); | 129 | useraddr += sizeof(cmd); |
130 | if (copy_to_user(useraddr, features, copy_size * sizeof(*features))) | 130 | if (copy_to_user(useraddr, features, copy_size * sizeof(*features))) |
131 | return -EFAULT; | 131 | return -EFAULT; |
132 | 132 | ||
133 | return 0; | 133 | return 0; |
134 | } | 134 | } |
135 | 135 | ||
136 | static int ethtool_set_features(struct net_device *dev, void __user *useraddr) | 136 | static int ethtool_set_features(struct net_device *dev, void __user *useraddr) |
137 | { | 137 | { |
138 | struct ethtool_sfeatures cmd; | 138 | struct ethtool_sfeatures cmd; |
139 | struct ethtool_set_features_block features[ETHTOOL_DEV_FEATURE_WORDS]; | 139 | struct ethtool_set_features_block features[ETHTOOL_DEV_FEATURE_WORDS]; |
140 | netdev_features_t wanted = 0, valid = 0; | 140 | netdev_features_t wanted = 0, valid = 0; |
141 | int i, ret = 0; | 141 | int i, ret = 0; |
142 | 142 | ||
143 | if (copy_from_user(&cmd, useraddr, sizeof(cmd))) | 143 | if (copy_from_user(&cmd, useraddr, sizeof(cmd))) |
144 | return -EFAULT; | 144 | return -EFAULT; |
145 | useraddr += sizeof(cmd); | 145 | useraddr += sizeof(cmd); |
146 | 146 | ||
147 | if (cmd.size != ETHTOOL_DEV_FEATURE_WORDS) | 147 | if (cmd.size != ETHTOOL_DEV_FEATURE_WORDS) |
148 | return -EINVAL; | 148 | return -EINVAL; |
149 | 149 | ||
150 | if (copy_from_user(features, useraddr, sizeof(features))) | 150 | if (copy_from_user(features, useraddr, sizeof(features))) |
151 | return -EFAULT; | 151 | return -EFAULT; |
152 | 152 | ||
153 | for (i = 0; i < ETHTOOL_DEV_FEATURE_WORDS; ++i) { | 153 | for (i = 0; i < ETHTOOL_DEV_FEATURE_WORDS; ++i) { |
154 | valid |= (netdev_features_t)features[i].valid << (32 * i); | 154 | valid |= (netdev_features_t)features[i].valid << (32 * i); |
155 | wanted |= (netdev_features_t)features[i].requested << (32 * i); | 155 | wanted |= (netdev_features_t)features[i].requested << (32 * i); |
156 | } | 156 | } |
157 | 157 | ||
158 | if (valid & ~NETIF_F_ETHTOOL_BITS) | 158 | if (valid & ~NETIF_F_ETHTOOL_BITS) |
159 | return -EINVAL; | 159 | return -EINVAL; |
160 | 160 | ||
161 | if (valid & ~dev->hw_features) { | 161 | if (valid & ~dev->hw_features) { |
162 | valid &= dev->hw_features; | 162 | valid &= dev->hw_features; |
163 | ret |= ETHTOOL_F_UNSUPPORTED; | 163 | ret |= ETHTOOL_F_UNSUPPORTED; |
164 | } | 164 | } |
165 | 165 | ||
166 | dev->wanted_features &= ~valid; | 166 | dev->wanted_features &= ~valid; |
167 | dev->wanted_features |= wanted & valid; | 167 | dev->wanted_features |= wanted & valid; |
168 | __netdev_update_features(dev); | 168 | __netdev_update_features(dev); |
169 | 169 | ||
170 | if ((dev->wanted_features ^ dev->features) & valid) | 170 | if ((dev->wanted_features ^ dev->features) & valid) |
171 | ret |= ETHTOOL_F_WISH; | 171 | ret |= ETHTOOL_F_WISH; |
172 | 172 | ||
173 | return ret; | 173 | return ret; |
174 | } | 174 | } |
175 | 175 | ||
176 | static int __ethtool_get_sset_count(struct net_device *dev, int sset) | 176 | static int __ethtool_get_sset_count(struct net_device *dev, int sset) |
177 | { | 177 | { |
178 | const struct ethtool_ops *ops = dev->ethtool_ops; | 178 | const struct ethtool_ops *ops = dev->ethtool_ops; |
179 | 179 | ||
180 | if (sset == ETH_SS_FEATURES) | 180 | if (sset == ETH_SS_FEATURES) |
181 | return ARRAY_SIZE(netdev_features_strings); | 181 | return ARRAY_SIZE(netdev_features_strings); |
182 | 182 | ||
183 | if (ops->get_sset_count && ops->get_strings) | 183 | if (ops->get_sset_count && ops->get_strings) |
184 | return ops->get_sset_count(dev, sset); | 184 | return ops->get_sset_count(dev, sset); |
185 | else | 185 | else |
186 | return -EOPNOTSUPP; | 186 | return -EOPNOTSUPP; |
187 | } | 187 | } |
188 | 188 | ||
189 | static void __ethtool_get_strings(struct net_device *dev, | 189 | static void __ethtool_get_strings(struct net_device *dev, |
190 | u32 stringset, u8 *data) | 190 | u32 stringset, u8 *data) |
191 | { | 191 | { |
192 | const struct ethtool_ops *ops = dev->ethtool_ops; | 192 | const struct ethtool_ops *ops = dev->ethtool_ops; |
193 | 193 | ||
194 | if (stringset == ETH_SS_FEATURES) | 194 | if (stringset == ETH_SS_FEATURES) |
195 | memcpy(data, netdev_features_strings, | 195 | memcpy(data, netdev_features_strings, |
196 | sizeof(netdev_features_strings)); | 196 | sizeof(netdev_features_strings)); |
197 | else | 197 | else |
198 | /* ops->get_strings is valid because checked earlier */ | 198 | /* ops->get_strings is valid because checked earlier */ |
199 | ops->get_strings(dev, stringset, data); | 199 | ops->get_strings(dev, stringset, data); |
200 | } | 200 | } |
201 | 201 | ||
202 | static netdev_features_t ethtool_get_feature_mask(u32 eth_cmd) | 202 | static netdev_features_t ethtool_get_feature_mask(u32 eth_cmd) |
203 | { | 203 | { |
204 | /* feature masks of legacy discrete ethtool ops */ | 204 | /* feature masks of legacy discrete ethtool ops */ |
205 | 205 | ||
206 | switch (eth_cmd) { | 206 | switch (eth_cmd) { |
207 | case ETHTOOL_GTXCSUM: | 207 | case ETHTOOL_GTXCSUM: |
208 | case ETHTOOL_STXCSUM: | 208 | case ETHTOOL_STXCSUM: |
209 | return NETIF_F_ALL_CSUM | NETIF_F_SCTP_CSUM; | 209 | return NETIF_F_ALL_CSUM | NETIF_F_SCTP_CSUM; |
210 | case ETHTOOL_GRXCSUM: | 210 | case ETHTOOL_GRXCSUM: |
211 | case ETHTOOL_SRXCSUM: | 211 | case ETHTOOL_SRXCSUM: |
212 | return NETIF_F_RXCSUM; | 212 | return NETIF_F_RXCSUM; |
213 | case ETHTOOL_GSG: | 213 | case ETHTOOL_GSG: |
214 | case ETHTOOL_SSG: | 214 | case ETHTOOL_SSG: |
215 | return NETIF_F_SG; | 215 | return NETIF_F_SG; |
216 | case ETHTOOL_GTSO: | 216 | case ETHTOOL_GTSO: |
217 | case ETHTOOL_STSO: | 217 | case ETHTOOL_STSO: |
218 | return NETIF_F_ALL_TSO; | 218 | return NETIF_F_ALL_TSO; |
219 | case ETHTOOL_GUFO: | 219 | case ETHTOOL_GUFO: |
220 | case ETHTOOL_SUFO: | 220 | case ETHTOOL_SUFO: |
221 | return NETIF_F_UFO; | 221 | return NETIF_F_UFO; |
222 | case ETHTOOL_GGSO: | 222 | case ETHTOOL_GGSO: |
223 | case ETHTOOL_SGSO: | 223 | case ETHTOOL_SGSO: |
224 | return NETIF_F_GSO; | 224 | return NETIF_F_GSO; |
225 | case ETHTOOL_GGRO: | 225 | case ETHTOOL_GGRO: |
226 | case ETHTOOL_SGRO: | 226 | case ETHTOOL_SGRO: |
227 | return NETIF_F_GRO; | 227 | return NETIF_F_GRO; |
228 | default: | 228 | default: |
229 | BUG(); | 229 | BUG(); |
230 | } | 230 | } |
231 | } | 231 | } |
232 | 232 | ||
233 | static int ethtool_get_one_feature(struct net_device *dev, | 233 | static int ethtool_get_one_feature(struct net_device *dev, |
234 | char __user *useraddr, u32 ethcmd) | 234 | char __user *useraddr, u32 ethcmd) |
235 | { | 235 | { |
236 | netdev_features_t mask = ethtool_get_feature_mask(ethcmd); | 236 | netdev_features_t mask = ethtool_get_feature_mask(ethcmd); |
237 | struct ethtool_value edata = { | 237 | struct ethtool_value edata = { |
238 | .cmd = ethcmd, | 238 | .cmd = ethcmd, |
239 | .data = !!(dev->features & mask), | 239 | .data = !!(dev->features & mask), |
240 | }; | 240 | }; |
241 | 241 | ||
242 | if (copy_to_user(useraddr, &edata, sizeof(edata))) | 242 | if (copy_to_user(useraddr, &edata, sizeof(edata))) |
243 | return -EFAULT; | 243 | return -EFAULT; |
244 | return 0; | 244 | return 0; |
245 | } | 245 | } |
246 | 246 | ||
247 | static int ethtool_set_one_feature(struct net_device *dev, | 247 | static int ethtool_set_one_feature(struct net_device *dev, |
248 | void __user *useraddr, u32 ethcmd) | 248 | void __user *useraddr, u32 ethcmd) |
249 | { | 249 | { |
250 | struct ethtool_value edata; | 250 | struct ethtool_value edata; |
251 | netdev_features_t mask; | 251 | netdev_features_t mask; |
252 | 252 | ||
253 | if (copy_from_user(&edata, useraddr, sizeof(edata))) | 253 | if (copy_from_user(&edata, useraddr, sizeof(edata))) |
254 | return -EFAULT; | 254 | return -EFAULT; |
255 | 255 | ||
256 | mask = ethtool_get_feature_mask(ethcmd); | 256 | mask = ethtool_get_feature_mask(ethcmd); |
257 | mask &= dev->hw_features; | 257 | mask &= dev->hw_features; |
258 | if (!mask) | 258 | if (!mask) |
259 | return -EOPNOTSUPP; | 259 | return -EOPNOTSUPP; |
260 | 260 | ||
261 | if (edata.data) | 261 | if (edata.data) |
262 | dev->wanted_features |= mask; | 262 | dev->wanted_features |= mask; |
263 | else | 263 | else |
264 | dev->wanted_features &= ~mask; | 264 | dev->wanted_features &= ~mask; |
265 | 265 | ||
266 | __netdev_update_features(dev); | 266 | __netdev_update_features(dev); |
267 | 267 | ||
268 | return 0; | 268 | return 0; |
269 | } | 269 | } |
270 | 270 | ||
271 | #define ETH_ALL_FLAGS (ETH_FLAG_LRO | ETH_FLAG_RXVLAN | ETH_FLAG_TXVLAN | \ | 271 | #define ETH_ALL_FLAGS (ETH_FLAG_LRO | ETH_FLAG_RXVLAN | ETH_FLAG_TXVLAN | \ |
272 | ETH_FLAG_NTUPLE | ETH_FLAG_RXHASH) | 272 | ETH_FLAG_NTUPLE | ETH_FLAG_RXHASH) |
273 | #define ETH_ALL_FEATURES (NETIF_F_LRO | NETIF_F_HW_VLAN_CTAG_RX | \ | 273 | #define ETH_ALL_FEATURES (NETIF_F_LRO | NETIF_F_HW_VLAN_CTAG_RX | \ |
274 | NETIF_F_HW_VLAN_CTAG_TX | NETIF_F_NTUPLE | \ | 274 | NETIF_F_HW_VLAN_CTAG_TX | NETIF_F_NTUPLE | \ |
275 | NETIF_F_RXHASH) | 275 | NETIF_F_RXHASH) |
276 | 276 | ||
277 | static u32 __ethtool_get_flags(struct net_device *dev) | 277 | static u32 __ethtool_get_flags(struct net_device *dev) |
278 | { | 278 | { |
279 | u32 flags = 0; | 279 | u32 flags = 0; |
280 | 280 | ||
281 | if (dev->features & NETIF_F_LRO) flags |= ETH_FLAG_LRO; | 281 | if (dev->features & NETIF_F_LRO) flags |= ETH_FLAG_LRO; |
282 | if (dev->features & NETIF_F_HW_VLAN_CTAG_RX) flags |= ETH_FLAG_RXVLAN; | 282 | if (dev->features & NETIF_F_HW_VLAN_CTAG_RX) flags |= ETH_FLAG_RXVLAN; |
283 | if (dev->features & NETIF_F_HW_VLAN_CTAG_TX) flags |= ETH_FLAG_TXVLAN; | 283 | if (dev->features & NETIF_F_HW_VLAN_CTAG_TX) flags |= ETH_FLAG_TXVLAN; |
284 | if (dev->features & NETIF_F_NTUPLE) flags |= ETH_FLAG_NTUPLE; | 284 | if (dev->features & NETIF_F_NTUPLE) flags |= ETH_FLAG_NTUPLE; |
285 | if (dev->features & NETIF_F_RXHASH) flags |= ETH_FLAG_RXHASH; | 285 | if (dev->features & NETIF_F_RXHASH) flags |= ETH_FLAG_RXHASH; |
286 | 286 | ||
287 | return flags; | 287 | return flags; |
288 | } | 288 | } |
289 | 289 | ||
290 | static int __ethtool_set_flags(struct net_device *dev, u32 data) | 290 | static int __ethtool_set_flags(struct net_device *dev, u32 data) |
291 | { | 291 | { |
292 | netdev_features_t features = 0, changed; | 292 | netdev_features_t features = 0, changed; |
293 | 293 | ||
294 | if (data & ~ETH_ALL_FLAGS) | 294 | if (data & ~ETH_ALL_FLAGS) |
295 | return -EINVAL; | 295 | return -EINVAL; |
296 | 296 | ||
297 | if (data & ETH_FLAG_LRO) features |= NETIF_F_LRO; | 297 | if (data & ETH_FLAG_LRO) features |= NETIF_F_LRO; |
298 | if (data & ETH_FLAG_RXVLAN) features |= NETIF_F_HW_VLAN_CTAG_RX; | 298 | if (data & ETH_FLAG_RXVLAN) features |= NETIF_F_HW_VLAN_CTAG_RX; |
299 | if (data & ETH_FLAG_TXVLAN) features |= NETIF_F_HW_VLAN_CTAG_TX; | 299 | if (data & ETH_FLAG_TXVLAN) features |= NETIF_F_HW_VLAN_CTAG_TX; |
300 | if (data & ETH_FLAG_NTUPLE) features |= NETIF_F_NTUPLE; | 300 | if (data & ETH_FLAG_NTUPLE) features |= NETIF_F_NTUPLE; |
301 | if (data & ETH_FLAG_RXHASH) features |= NETIF_F_RXHASH; | 301 | if (data & ETH_FLAG_RXHASH) features |= NETIF_F_RXHASH; |
302 | 302 | ||
303 | /* allow changing only bits set in hw_features */ | 303 | /* allow changing only bits set in hw_features */ |
304 | changed = (features ^ dev->features) & ETH_ALL_FEATURES; | 304 | changed = (features ^ dev->features) & ETH_ALL_FEATURES; |
305 | if (changed & ~dev->hw_features) | 305 | if (changed & ~dev->hw_features) |
306 | return (changed & dev->hw_features) ? -EINVAL : -EOPNOTSUPP; | 306 | return (changed & dev->hw_features) ? -EINVAL : -EOPNOTSUPP; |
307 | 307 | ||
308 | dev->wanted_features = | 308 | dev->wanted_features = |
309 | (dev->wanted_features & ~changed) | (features & changed); | 309 | (dev->wanted_features & ~changed) | (features & changed); |
310 | 310 | ||
311 | __netdev_update_features(dev); | 311 | __netdev_update_features(dev); |
312 | 312 | ||
313 | return 0; | 313 | return 0; |
314 | } | 314 | } |
315 | 315 | ||
316 | int __ethtool_get_settings(struct net_device *dev, struct ethtool_cmd *cmd) | 316 | int __ethtool_get_settings(struct net_device *dev, struct ethtool_cmd *cmd) |
317 | { | 317 | { |
318 | ASSERT_RTNL(); | 318 | ASSERT_RTNL(); |
319 | 319 | ||
320 | if (!dev->ethtool_ops->get_settings) | 320 | if (!dev->ethtool_ops->get_settings) |
321 | return -EOPNOTSUPP; | 321 | return -EOPNOTSUPP; |
322 | 322 | ||
323 | memset(cmd, 0, sizeof(struct ethtool_cmd)); | 323 | memset(cmd, 0, sizeof(struct ethtool_cmd)); |
324 | cmd->cmd = ETHTOOL_GSET; | 324 | cmd->cmd = ETHTOOL_GSET; |
325 | return dev->ethtool_ops->get_settings(dev, cmd); | 325 | return dev->ethtool_ops->get_settings(dev, cmd); |
326 | } | 326 | } |
327 | EXPORT_SYMBOL(__ethtool_get_settings); | 327 | EXPORT_SYMBOL(__ethtool_get_settings); |
328 | 328 | ||
329 | static int ethtool_get_settings(struct net_device *dev, void __user *useraddr) | 329 | static int ethtool_get_settings(struct net_device *dev, void __user *useraddr) |
330 | { | 330 | { |
331 | int err; | 331 | int err; |
332 | struct ethtool_cmd cmd; | 332 | struct ethtool_cmd cmd; |
333 | 333 | ||
334 | err = __ethtool_get_settings(dev, &cmd); | 334 | err = __ethtool_get_settings(dev, &cmd); |
335 | if (err < 0) | 335 | if (err < 0) |
336 | return err; | 336 | return err; |
337 | 337 | ||
338 | if (copy_to_user(useraddr, &cmd, sizeof(cmd))) | 338 | if (copy_to_user(useraddr, &cmd, sizeof(cmd))) |
339 | return -EFAULT; | 339 | return -EFAULT; |
340 | return 0; | 340 | return 0; |
341 | } | 341 | } |
342 | 342 | ||
343 | static int ethtool_set_settings(struct net_device *dev, void __user *useraddr) | 343 | static int ethtool_set_settings(struct net_device *dev, void __user *useraddr) |
344 | { | 344 | { |
345 | struct ethtool_cmd cmd; | 345 | struct ethtool_cmd cmd; |
346 | 346 | ||
347 | if (!dev->ethtool_ops->set_settings) | 347 | if (!dev->ethtool_ops->set_settings) |
348 | return -EOPNOTSUPP; | 348 | return -EOPNOTSUPP; |
349 | 349 | ||
350 | if (copy_from_user(&cmd, useraddr, sizeof(cmd))) | 350 | if (copy_from_user(&cmd, useraddr, sizeof(cmd))) |
351 | return -EFAULT; | 351 | return -EFAULT; |
352 | 352 | ||
353 | return dev->ethtool_ops->set_settings(dev, &cmd); | 353 | return dev->ethtool_ops->set_settings(dev, &cmd); |
354 | } | 354 | } |
355 | 355 | ||
356 | static noinline_for_stack int ethtool_get_drvinfo(struct net_device *dev, | 356 | static noinline_for_stack int ethtool_get_drvinfo(struct net_device *dev, |
357 | void __user *useraddr) | 357 | void __user *useraddr) |
358 | { | 358 | { |
359 | struct ethtool_drvinfo info; | 359 | struct ethtool_drvinfo info; |
360 | const struct ethtool_ops *ops = dev->ethtool_ops; | 360 | const struct ethtool_ops *ops = dev->ethtool_ops; |
361 | 361 | ||
362 | memset(&info, 0, sizeof(info)); | 362 | memset(&info, 0, sizeof(info)); |
363 | info.cmd = ETHTOOL_GDRVINFO; | 363 | info.cmd = ETHTOOL_GDRVINFO; |
364 | if (ops->get_drvinfo) { | 364 | if (ops->get_drvinfo) { |
365 | ops->get_drvinfo(dev, &info); | 365 | ops->get_drvinfo(dev, &info); |
366 | } else if (dev->dev.parent && dev->dev.parent->driver) { | 366 | } else if (dev->dev.parent && dev->dev.parent->driver) { |
367 | strlcpy(info.bus_info, dev_name(dev->dev.parent), | 367 | strlcpy(info.bus_info, dev_name(dev->dev.parent), |
368 | sizeof(info.bus_info)); | 368 | sizeof(info.bus_info)); |
369 | strlcpy(info.driver, dev->dev.parent->driver->name, | 369 | strlcpy(info.driver, dev->dev.parent->driver->name, |
370 | sizeof(info.driver)); | 370 | sizeof(info.driver)); |
371 | } else { | 371 | } else { |
372 | return -EOPNOTSUPP; | 372 | return -EOPNOTSUPP; |
373 | } | 373 | } |
374 | 374 | ||
375 | /* | 375 | /* |
376 | * this method of obtaining string set info is deprecated; | 376 | * this method of obtaining string set info is deprecated; |
377 | * Use ETHTOOL_GSSET_INFO instead. | 377 | * Use ETHTOOL_GSSET_INFO instead. |
378 | */ | 378 | */ |
379 | if (ops->get_sset_count) { | 379 | if (ops->get_sset_count) { |
380 | int rc; | 380 | int rc; |
381 | 381 | ||
382 | rc = ops->get_sset_count(dev, ETH_SS_TEST); | 382 | rc = ops->get_sset_count(dev, ETH_SS_TEST); |
383 | if (rc >= 0) | 383 | if (rc >= 0) |
384 | info.testinfo_len = rc; | 384 | info.testinfo_len = rc; |
385 | rc = ops->get_sset_count(dev, ETH_SS_STATS); | 385 | rc = ops->get_sset_count(dev, ETH_SS_STATS); |
386 | if (rc >= 0) | 386 | if (rc >= 0) |
387 | info.n_stats = rc; | 387 | info.n_stats = rc; |
388 | rc = ops->get_sset_count(dev, ETH_SS_PRIV_FLAGS); | 388 | rc = ops->get_sset_count(dev, ETH_SS_PRIV_FLAGS); |
389 | if (rc >= 0) | 389 | if (rc >= 0) |
390 | info.n_priv_flags = rc; | 390 | info.n_priv_flags = rc; |
391 | } | 391 | } |
392 | if (ops->get_regs_len) | 392 | if (ops->get_regs_len) |
393 | info.regdump_len = ops->get_regs_len(dev); | 393 | info.regdump_len = ops->get_regs_len(dev); |
394 | if (ops->get_eeprom_len) | 394 | if (ops->get_eeprom_len) |
395 | info.eedump_len = ops->get_eeprom_len(dev); | 395 | info.eedump_len = ops->get_eeprom_len(dev); |
396 | 396 | ||
397 | if (copy_to_user(useraddr, &info, sizeof(info))) | 397 | if (copy_to_user(useraddr, &info, sizeof(info))) |
398 | return -EFAULT; | 398 | return -EFAULT; |
399 | return 0; | 399 | return 0; |
400 | } | 400 | } |
401 | 401 | ||
402 | static noinline_for_stack int ethtool_get_sset_info(struct net_device *dev, | 402 | static noinline_for_stack int ethtool_get_sset_info(struct net_device *dev, |
403 | void __user *useraddr) | 403 | void __user *useraddr) |
404 | { | 404 | { |
405 | struct ethtool_sset_info info; | 405 | struct ethtool_sset_info info; |
406 | u64 sset_mask; | 406 | u64 sset_mask; |
407 | int i, idx = 0, n_bits = 0, ret, rc; | 407 | int i, idx = 0, n_bits = 0, ret, rc; |
408 | u32 *info_buf = NULL; | 408 | u32 *info_buf = NULL; |
409 | 409 | ||
410 | if (copy_from_user(&info, useraddr, sizeof(info))) | 410 | if (copy_from_user(&info, useraddr, sizeof(info))) |
411 | return -EFAULT; | 411 | return -EFAULT; |
412 | 412 | ||
413 | /* store copy of mask, because we zero struct later on */ | 413 | /* store copy of mask, because we zero struct later on */ |
414 | sset_mask = info.sset_mask; | 414 | sset_mask = info.sset_mask; |
415 | if (!sset_mask) | 415 | if (!sset_mask) |
416 | return 0; | 416 | return 0; |
417 | 417 | ||
418 | /* calculate size of return buffer */ | 418 | /* calculate size of return buffer */ |
419 | n_bits = hweight64(sset_mask); | 419 | n_bits = hweight64(sset_mask); |
420 | 420 | ||
421 | memset(&info, 0, sizeof(info)); | 421 | memset(&info, 0, sizeof(info)); |
422 | info.cmd = ETHTOOL_GSSET_INFO; | 422 | info.cmd = ETHTOOL_GSSET_INFO; |
423 | 423 | ||
424 | info_buf = kzalloc(n_bits * sizeof(u32), GFP_USER); | 424 | info_buf = kzalloc(n_bits * sizeof(u32), GFP_USER); |
425 | if (!info_buf) | 425 | if (!info_buf) |
426 | return -ENOMEM; | 426 | return -ENOMEM; |
427 | 427 | ||
428 | /* | 428 | /* |
429 | * fill return buffer based on input bitmask and successful | 429 | * fill return buffer based on input bitmask and successful |
430 | * get_sset_count return | 430 | * get_sset_count return |
431 | */ | 431 | */ |
432 | for (i = 0; i < 64; i++) { | 432 | for (i = 0; i < 64; i++) { |
433 | if (!(sset_mask & (1ULL << i))) | 433 | if (!(sset_mask & (1ULL << i))) |
434 | continue; | 434 | continue; |
435 | 435 | ||
436 | rc = __ethtool_get_sset_count(dev, i); | 436 | rc = __ethtool_get_sset_count(dev, i); |
437 | if (rc >= 0) { | 437 | if (rc >= 0) { |
438 | info.sset_mask |= (1ULL << i); | 438 | info.sset_mask |= (1ULL << i); |
439 | info_buf[idx++] = rc; | 439 | info_buf[idx++] = rc; |
440 | } | 440 | } |
441 | } | 441 | } |
442 | 442 | ||
443 | ret = -EFAULT; | 443 | ret = -EFAULT; |
444 | if (copy_to_user(useraddr, &info, sizeof(info))) | 444 | if (copy_to_user(useraddr, &info, sizeof(info))) |
445 | goto out; | 445 | goto out; |
446 | 446 | ||
447 | useraddr += offsetof(struct ethtool_sset_info, data); | 447 | useraddr += offsetof(struct ethtool_sset_info, data); |
448 | if (copy_to_user(useraddr, info_buf, idx * sizeof(u32))) | 448 | if (copy_to_user(useraddr, info_buf, idx * sizeof(u32))) |
449 | goto out; | 449 | goto out; |
450 | 450 | ||
451 | ret = 0; | 451 | ret = 0; |
452 | 452 | ||
453 | out: | 453 | out: |
454 | kfree(info_buf); | 454 | kfree(info_buf); |
455 | return ret; | 455 | return ret; |
456 | } | 456 | } |
457 | 457 | ||
458 | static noinline_for_stack int ethtool_set_rxnfc(struct net_device *dev, | 458 | static noinline_for_stack int ethtool_set_rxnfc(struct net_device *dev, |
459 | u32 cmd, void __user *useraddr) | 459 | u32 cmd, void __user *useraddr) |
460 | { | 460 | { |
461 | struct ethtool_rxnfc info; | 461 | struct ethtool_rxnfc info; |
462 | size_t info_size = sizeof(info); | 462 | size_t info_size = sizeof(info); |
463 | int rc; | 463 | int rc; |
464 | 464 | ||
465 | if (!dev->ethtool_ops->set_rxnfc) | 465 | if (!dev->ethtool_ops->set_rxnfc) |
466 | return -EOPNOTSUPP; | 466 | return -EOPNOTSUPP; |
467 | 467 | ||
468 | /* struct ethtool_rxnfc was originally defined for | 468 | /* struct ethtool_rxnfc was originally defined for |
469 | * ETHTOOL_{G,S}RXFH with only the cmd, flow_type and data | 469 | * ETHTOOL_{G,S}RXFH with only the cmd, flow_type and data |
470 | * members. User-space might still be using that | 470 | * members. User-space might still be using that |
471 | * definition. */ | 471 | * definition. */ |
472 | if (cmd == ETHTOOL_SRXFH) | 472 | if (cmd == ETHTOOL_SRXFH) |
473 | info_size = (offsetof(struct ethtool_rxnfc, data) + | 473 | info_size = (offsetof(struct ethtool_rxnfc, data) + |
474 | sizeof(info.data)); | 474 | sizeof(info.data)); |
475 | 475 | ||
476 | if (copy_from_user(&info, useraddr, info_size)) | 476 | if (copy_from_user(&info, useraddr, info_size)) |
477 | return -EFAULT; | 477 | return -EFAULT; |
478 | 478 | ||
479 | rc = dev->ethtool_ops->set_rxnfc(dev, &info); | 479 | rc = dev->ethtool_ops->set_rxnfc(dev, &info); |
480 | if (rc) | 480 | if (rc) |
481 | return rc; | 481 | return rc; |
482 | 482 | ||
483 | if (cmd == ETHTOOL_SRXCLSRLINS && | 483 | if (cmd == ETHTOOL_SRXCLSRLINS && |
484 | copy_to_user(useraddr, &info, info_size)) | 484 | copy_to_user(useraddr, &info, info_size)) |
485 | return -EFAULT; | 485 | return -EFAULT; |
486 | 486 | ||
487 | return 0; | 487 | return 0; |
488 | } | 488 | } |
489 | 489 | ||
490 | static noinline_for_stack int ethtool_get_rxnfc(struct net_device *dev, | 490 | static noinline_for_stack int ethtool_get_rxnfc(struct net_device *dev, |
491 | u32 cmd, void __user *useraddr) | 491 | u32 cmd, void __user *useraddr) |
492 | { | 492 | { |
493 | struct ethtool_rxnfc info; | 493 | struct ethtool_rxnfc info; |
494 | size_t info_size = sizeof(info); | 494 | size_t info_size = sizeof(info); |
495 | const struct ethtool_ops *ops = dev->ethtool_ops; | 495 | const struct ethtool_ops *ops = dev->ethtool_ops; |
496 | int ret; | 496 | int ret; |
497 | void *rule_buf = NULL; | 497 | void *rule_buf = NULL; |
498 | 498 | ||
499 | if (!ops->get_rxnfc) | 499 | if (!ops->get_rxnfc) |
500 | return -EOPNOTSUPP; | 500 | return -EOPNOTSUPP; |
501 | 501 | ||
502 | /* struct ethtool_rxnfc was originally defined for | 502 | /* struct ethtool_rxnfc was originally defined for |
503 | * ETHTOOL_{G,S}RXFH with only the cmd, flow_type and data | 503 | * ETHTOOL_{G,S}RXFH with only the cmd, flow_type and data |
504 | * members. User-space might still be using that | 504 | * members. User-space might still be using that |
505 | * definition. */ | 505 | * definition. */ |
506 | if (cmd == ETHTOOL_GRXFH) | 506 | if (cmd == ETHTOOL_GRXFH) |
507 | info_size = (offsetof(struct ethtool_rxnfc, data) + | 507 | info_size = (offsetof(struct ethtool_rxnfc, data) + |
508 | sizeof(info.data)); | 508 | sizeof(info.data)); |
509 | 509 | ||
510 | if (copy_from_user(&info, useraddr, info_size)) | 510 | if (copy_from_user(&info, useraddr, info_size)) |
511 | return -EFAULT; | 511 | return -EFAULT; |
512 | 512 | ||
513 | if (info.cmd == ETHTOOL_GRXCLSRLALL) { | 513 | if (info.cmd == ETHTOOL_GRXCLSRLALL) { |
514 | if (info.rule_cnt > 0) { | 514 | if (info.rule_cnt > 0) { |
515 | if (info.rule_cnt <= KMALLOC_MAX_SIZE / sizeof(u32)) | 515 | if (info.rule_cnt <= KMALLOC_MAX_SIZE / sizeof(u32)) |
516 | rule_buf = kzalloc(info.rule_cnt * sizeof(u32), | 516 | rule_buf = kzalloc(info.rule_cnt * sizeof(u32), |
517 | GFP_USER); | 517 | GFP_USER); |
518 | if (!rule_buf) | 518 | if (!rule_buf) |
519 | return -ENOMEM; | 519 | return -ENOMEM; |
520 | } | 520 | } |
521 | } | 521 | } |
522 | 522 | ||
523 | ret = ops->get_rxnfc(dev, &info, rule_buf); | 523 | ret = ops->get_rxnfc(dev, &info, rule_buf); |
524 | if (ret < 0) | 524 | if (ret < 0) |
525 | goto err_out; | 525 | goto err_out; |
526 | 526 | ||
527 | ret = -EFAULT; | 527 | ret = -EFAULT; |
528 | if (copy_to_user(useraddr, &info, info_size)) | 528 | if (copy_to_user(useraddr, &info, info_size)) |
529 | goto err_out; | 529 | goto err_out; |
530 | 530 | ||
531 | if (rule_buf) { | 531 | if (rule_buf) { |
532 | useraddr += offsetof(struct ethtool_rxnfc, rule_locs); | 532 | useraddr += offsetof(struct ethtool_rxnfc, rule_locs); |
533 | if (copy_to_user(useraddr, rule_buf, | 533 | if (copy_to_user(useraddr, rule_buf, |
534 | info.rule_cnt * sizeof(u32))) | 534 | info.rule_cnt * sizeof(u32))) |
535 | goto err_out; | 535 | goto err_out; |
536 | } | 536 | } |
537 | ret = 0; | 537 | ret = 0; |
538 | 538 | ||
539 | err_out: | 539 | err_out: |
540 | kfree(rule_buf); | 540 | kfree(rule_buf); |
541 | 541 | ||
542 | return ret; | 542 | return ret; |
543 | } | 543 | } |
544 | 544 | ||
545 | static noinline_for_stack int ethtool_get_rxfh_indir(struct net_device *dev, | 545 | static noinline_for_stack int ethtool_get_rxfh_indir(struct net_device *dev, |
546 | void __user *useraddr) | 546 | void __user *useraddr) |
547 | { | 547 | { |
548 | u32 user_size, dev_size; | 548 | u32 user_size, dev_size; |
549 | u32 *indir; | 549 | u32 *indir; |
550 | int ret; | 550 | int ret; |
551 | 551 | ||
552 | if (!dev->ethtool_ops->get_rxfh_indir_size || | 552 | if (!dev->ethtool_ops->get_rxfh_indir_size || |
553 | !dev->ethtool_ops->get_rxfh_indir) | 553 | !dev->ethtool_ops->get_rxfh_indir) |
554 | return -EOPNOTSUPP; | 554 | return -EOPNOTSUPP; |
555 | dev_size = dev->ethtool_ops->get_rxfh_indir_size(dev); | 555 | dev_size = dev->ethtool_ops->get_rxfh_indir_size(dev); |
556 | if (dev_size == 0) | 556 | if (dev_size == 0) |
557 | return -EOPNOTSUPP; | 557 | return -EOPNOTSUPP; |
558 | 558 | ||
559 | if (copy_from_user(&user_size, | 559 | if (copy_from_user(&user_size, |
560 | useraddr + offsetof(struct ethtool_rxfh_indir, size), | 560 | useraddr + offsetof(struct ethtool_rxfh_indir, size), |
561 | sizeof(user_size))) | 561 | sizeof(user_size))) |
562 | return -EFAULT; | 562 | return -EFAULT; |
563 | 563 | ||
564 | if (copy_to_user(useraddr + offsetof(struct ethtool_rxfh_indir, size), | 564 | if (copy_to_user(useraddr + offsetof(struct ethtool_rxfh_indir, size), |
565 | &dev_size, sizeof(dev_size))) | 565 | &dev_size, sizeof(dev_size))) |
566 | return -EFAULT; | 566 | return -EFAULT; |
567 | 567 | ||
568 | /* If the user buffer size is 0, this is just a query for the | 568 | /* If the user buffer size is 0, this is just a query for the |
569 | * device table size. Otherwise, if it's smaller than the | 569 | * device table size. Otherwise, if it's smaller than the |
570 | * device table size it's an error. | 570 | * device table size it's an error. |
571 | */ | 571 | */ |
572 | if (user_size < dev_size) | 572 | if (user_size < dev_size) |
573 | return user_size == 0 ? 0 : -EINVAL; | 573 | return user_size == 0 ? 0 : -EINVAL; |
574 | 574 | ||
575 | indir = kcalloc(dev_size, sizeof(indir[0]), GFP_USER); | 575 | indir = kcalloc(dev_size, sizeof(indir[0]), GFP_USER); |
576 | if (!indir) | 576 | if (!indir) |
577 | return -ENOMEM; | 577 | return -ENOMEM; |
578 | 578 | ||
579 | ret = dev->ethtool_ops->get_rxfh_indir(dev, indir); | 579 | ret = dev->ethtool_ops->get_rxfh_indir(dev, indir); |
580 | if (ret) | 580 | if (ret) |
581 | goto out; | 581 | goto out; |
582 | 582 | ||
583 | if (copy_to_user(useraddr + | 583 | if (copy_to_user(useraddr + |
584 | offsetof(struct ethtool_rxfh_indir, ring_index[0]), | 584 | offsetof(struct ethtool_rxfh_indir, ring_index[0]), |
585 | indir, dev_size * sizeof(indir[0]))) | 585 | indir, dev_size * sizeof(indir[0]))) |
586 | ret = -EFAULT; | 586 | ret = -EFAULT; |
587 | 587 | ||
588 | out: | 588 | out: |
589 | kfree(indir); | 589 | kfree(indir); |
590 | return ret; | 590 | return ret; |
591 | } | 591 | } |
592 | 592 | ||
593 | static noinline_for_stack int ethtool_set_rxfh_indir(struct net_device *dev, | 593 | static noinline_for_stack int ethtool_set_rxfh_indir(struct net_device *dev, |
594 | void __user *useraddr) | 594 | void __user *useraddr) |
595 | { | 595 | { |
596 | struct ethtool_rxnfc rx_rings; | 596 | struct ethtool_rxnfc rx_rings; |
597 | u32 user_size, dev_size, i; | 597 | u32 user_size, dev_size, i; |
598 | u32 *indir; | 598 | u32 *indir; |
599 | const struct ethtool_ops *ops = dev->ethtool_ops; | 599 | const struct ethtool_ops *ops = dev->ethtool_ops; |
600 | int ret; | 600 | int ret; |
601 | 601 | ||
602 | if (!ops->get_rxfh_indir_size || !ops->set_rxfh_indir || | 602 | if (!ops->get_rxfh_indir_size || !ops->set_rxfh_indir || |
603 | !ops->get_rxnfc) | 603 | !ops->get_rxnfc) |
604 | return -EOPNOTSUPP; | 604 | return -EOPNOTSUPP; |
605 | 605 | ||
606 | dev_size = ops->get_rxfh_indir_size(dev); | 606 | dev_size = ops->get_rxfh_indir_size(dev); |
607 | if (dev_size == 0) | 607 | if (dev_size == 0) |
608 | return -EOPNOTSUPP; | 608 | return -EOPNOTSUPP; |
609 | 609 | ||
610 | if (copy_from_user(&user_size, | 610 | if (copy_from_user(&user_size, |
611 | useraddr + offsetof(struct ethtool_rxfh_indir, size), | 611 | useraddr + offsetof(struct ethtool_rxfh_indir, size), |
612 | sizeof(user_size))) | 612 | sizeof(user_size))) |
613 | return -EFAULT; | 613 | return -EFAULT; |
614 | 614 | ||
615 | if (user_size != 0 && user_size != dev_size) | 615 | if (user_size != 0 && user_size != dev_size) |
616 | return -EINVAL; | 616 | return -EINVAL; |
617 | 617 | ||
618 | indir = kcalloc(dev_size, sizeof(indir[0]), GFP_USER); | 618 | indir = kcalloc(dev_size, sizeof(indir[0]), GFP_USER); |
619 | if (!indir) | 619 | if (!indir) |
620 | return -ENOMEM; | 620 | return -ENOMEM; |
621 | 621 | ||
622 | rx_rings.cmd = ETHTOOL_GRXRINGS; | 622 | rx_rings.cmd = ETHTOOL_GRXRINGS; |
623 | ret = ops->get_rxnfc(dev, &rx_rings, NULL); | 623 | ret = ops->get_rxnfc(dev, &rx_rings, NULL); |
624 | if (ret) | 624 | if (ret) |
625 | goto out; | 625 | goto out; |
626 | 626 | ||
627 | if (user_size == 0) { | 627 | if (user_size == 0) { |
628 | for (i = 0; i < dev_size; i++) | 628 | for (i = 0; i < dev_size; i++) |
629 | indir[i] = ethtool_rxfh_indir_default(i, rx_rings.data); | 629 | indir[i] = ethtool_rxfh_indir_default(i, rx_rings.data); |
630 | } else { | 630 | } else { |
631 | if (copy_from_user(indir, | 631 | if (copy_from_user(indir, |
632 | useraddr + | 632 | useraddr + |
633 | offsetof(struct ethtool_rxfh_indir, | 633 | offsetof(struct ethtool_rxfh_indir, |
634 | ring_index[0]), | 634 | ring_index[0]), |
635 | dev_size * sizeof(indir[0]))) { | 635 | dev_size * sizeof(indir[0]))) { |
636 | ret = -EFAULT; | 636 | ret = -EFAULT; |
637 | goto out; | 637 | goto out; |
638 | } | 638 | } |
639 | 639 | ||
640 | /* Validate ring indices */ | 640 | /* Validate ring indices */ |
641 | for (i = 0; i < dev_size; i++) { | 641 | for (i = 0; i < dev_size; i++) { |
642 | if (indir[i] >= rx_rings.data) { | 642 | if (indir[i] >= rx_rings.data) { |
643 | ret = -EINVAL; | 643 | ret = -EINVAL; |
644 | goto out; | 644 | goto out; |
645 | } | 645 | } |
646 | } | 646 | } |
647 | } | 647 | } |
648 | 648 | ||
649 | ret = ops->set_rxfh_indir(dev, indir); | 649 | ret = ops->set_rxfh_indir(dev, indir); |
650 | 650 | ||
651 | out: | 651 | out: |
652 | kfree(indir); | 652 | kfree(indir); |
653 | return ret; | 653 | return ret; |
654 | } | 654 | } |
655 | 655 | ||
656 | static int ethtool_get_regs(struct net_device *dev, char __user *useraddr) | 656 | static int ethtool_get_regs(struct net_device *dev, char __user *useraddr) |
657 | { | 657 | { |
658 | struct ethtool_regs regs; | 658 | struct ethtool_regs regs; |
659 | const struct ethtool_ops *ops = dev->ethtool_ops; | 659 | const struct ethtool_ops *ops = dev->ethtool_ops; |
660 | void *regbuf; | 660 | void *regbuf; |
661 | int reglen, ret; | 661 | int reglen, ret; |
662 | 662 | ||
663 | if (!ops->get_regs || !ops->get_regs_len) | 663 | if (!ops->get_regs || !ops->get_regs_len) |
664 | return -EOPNOTSUPP; | 664 | return -EOPNOTSUPP; |
665 | 665 | ||
666 | if (copy_from_user(®s, useraddr, sizeof(regs))) | 666 | if (copy_from_user(®s, useraddr, sizeof(regs))) |
667 | return -EFAULT; | 667 | return -EFAULT; |
668 | 668 | ||
669 | reglen = ops->get_regs_len(dev); | 669 | reglen = ops->get_regs_len(dev); |
670 | if (regs.len > reglen) | 670 | if (regs.len > reglen) |
671 | regs.len = reglen; | 671 | regs.len = reglen; |
672 | 672 | ||
673 | regbuf = vzalloc(reglen); | 673 | regbuf = vzalloc(reglen); |
674 | if (reglen && !regbuf) | 674 | if (reglen && !regbuf) |
675 | return -ENOMEM; | 675 | return -ENOMEM; |
676 | 676 | ||
677 | ops->get_regs(dev, ®s, regbuf); | 677 | ops->get_regs(dev, ®s, regbuf); |
678 | 678 | ||
679 | ret = -EFAULT; | 679 | ret = -EFAULT; |
680 | if (copy_to_user(useraddr, ®s, sizeof(regs))) | 680 | if (copy_to_user(useraddr, ®s, sizeof(regs))) |
681 | goto out; | 681 | goto out; |
682 | useraddr += offsetof(struct ethtool_regs, data); | 682 | useraddr += offsetof(struct ethtool_regs, data); |
683 | if (regbuf && copy_to_user(useraddr, regbuf, regs.len)) | 683 | if (regbuf && copy_to_user(useraddr, regbuf, regs.len)) |
684 | goto out; | 684 | goto out; |
685 | ret = 0; | 685 | ret = 0; |
686 | 686 | ||
687 | out: | 687 | out: |
688 | vfree(regbuf); | 688 | vfree(regbuf); |
689 | return ret; | 689 | return ret; |
690 | } | 690 | } |
691 | 691 | ||
692 | static int ethtool_reset(struct net_device *dev, char __user *useraddr) | 692 | static int ethtool_reset(struct net_device *dev, char __user *useraddr) |
693 | { | 693 | { |
694 | struct ethtool_value reset; | 694 | struct ethtool_value reset; |
695 | int ret; | 695 | int ret; |
696 | 696 | ||
697 | if (!dev->ethtool_ops->reset) | 697 | if (!dev->ethtool_ops->reset) |
698 | return -EOPNOTSUPP; | 698 | return -EOPNOTSUPP; |
699 | 699 | ||
700 | if (copy_from_user(&reset, useraddr, sizeof(reset))) | 700 | if (copy_from_user(&reset, useraddr, sizeof(reset))) |
701 | return -EFAULT; | 701 | return -EFAULT; |
702 | 702 | ||
703 | ret = dev->ethtool_ops->reset(dev, &reset.data); | 703 | ret = dev->ethtool_ops->reset(dev, &reset.data); |
704 | if (ret) | 704 | if (ret) |
705 | return ret; | 705 | return ret; |
706 | 706 | ||
707 | if (copy_to_user(useraddr, &reset, sizeof(reset))) | 707 | if (copy_to_user(useraddr, &reset, sizeof(reset))) |
708 | return -EFAULT; | 708 | return -EFAULT; |
709 | return 0; | 709 | return 0; |
710 | } | 710 | } |
711 | 711 | ||
712 | static int ethtool_get_wol(struct net_device *dev, char __user *useraddr) | 712 | static int ethtool_get_wol(struct net_device *dev, char __user *useraddr) |
713 | { | 713 | { |
714 | struct ethtool_wolinfo wol = { .cmd = ETHTOOL_GWOL }; | 714 | struct ethtool_wolinfo wol = { .cmd = ETHTOOL_GWOL }; |
715 | 715 | ||
716 | if (!dev->ethtool_ops->get_wol) | 716 | if (!dev->ethtool_ops->get_wol) |
717 | return -EOPNOTSUPP; | 717 | return -EOPNOTSUPP; |
718 | 718 | ||
719 | dev->ethtool_ops->get_wol(dev, &wol); | 719 | dev->ethtool_ops->get_wol(dev, &wol); |
720 | 720 | ||
721 | if (copy_to_user(useraddr, &wol, sizeof(wol))) | 721 | if (copy_to_user(useraddr, &wol, sizeof(wol))) |
722 | return -EFAULT; | 722 | return -EFAULT; |
723 | return 0; | 723 | return 0; |
724 | } | 724 | } |
725 | 725 | ||
726 | static int ethtool_set_wol(struct net_device *dev, char __user *useraddr) | 726 | static int ethtool_set_wol(struct net_device *dev, char __user *useraddr) |
727 | { | 727 | { |
728 | struct ethtool_wolinfo wol; | 728 | struct ethtool_wolinfo wol; |
729 | 729 | ||
730 | if (!dev->ethtool_ops->set_wol) | 730 | if (!dev->ethtool_ops->set_wol) |
731 | return -EOPNOTSUPP; | 731 | return -EOPNOTSUPP; |
732 | 732 | ||
733 | if (copy_from_user(&wol, useraddr, sizeof(wol))) | 733 | if (copy_from_user(&wol, useraddr, sizeof(wol))) |
734 | return -EFAULT; | 734 | return -EFAULT; |
735 | 735 | ||
736 | return dev->ethtool_ops->set_wol(dev, &wol); | 736 | return dev->ethtool_ops->set_wol(dev, &wol); |
737 | } | 737 | } |
738 | 738 | ||
739 | static int ethtool_get_eee(struct net_device *dev, char __user *useraddr) | 739 | static int ethtool_get_eee(struct net_device *dev, char __user *useraddr) |
740 | { | 740 | { |
741 | struct ethtool_eee edata; | 741 | struct ethtool_eee edata; |
742 | int rc; | 742 | int rc; |
743 | 743 | ||
744 | if (!dev->ethtool_ops->get_eee) | 744 | if (!dev->ethtool_ops->get_eee) |
745 | return -EOPNOTSUPP; | 745 | return -EOPNOTSUPP; |
746 | 746 | ||
747 | memset(&edata, 0, sizeof(struct ethtool_eee)); | 747 | memset(&edata, 0, sizeof(struct ethtool_eee)); |
748 | edata.cmd = ETHTOOL_GEEE; | 748 | edata.cmd = ETHTOOL_GEEE; |
749 | rc = dev->ethtool_ops->get_eee(dev, &edata); | 749 | rc = dev->ethtool_ops->get_eee(dev, &edata); |
750 | 750 | ||
751 | if (rc) | 751 | if (rc) |
752 | return rc; | 752 | return rc; |
753 | 753 | ||
754 | if (copy_to_user(useraddr, &edata, sizeof(edata))) | 754 | if (copy_to_user(useraddr, &edata, sizeof(edata))) |
755 | return -EFAULT; | 755 | return -EFAULT; |
756 | 756 | ||
757 | return 0; | 757 | return 0; |
758 | } | 758 | } |
759 | 759 | ||
760 | static int ethtool_set_eee(struct net_device *dev, char __user *useraddr) | 760 | static int ethtool_set_eee(struct net_device *dev, char __user *useraddr) |
761 | { | 761 | { |
762 | struct ethtool_eee edata; | 762 | struct ethtool_eee edata; |
763 | 763 | ||
764 | if (!dev->ethtool_ops->set_eee) | 764 | if (!dev->ethtool_ops->set_eee) |
765 | return -EOPNOTSUPP; | 765 | return -EOPNOTSUPP; |
766 | 766 | ||
767 | if (copy_from_user(&edata, useraddr, sizeof(edata))) | 767 | if (copy_from_user(&edata, useraddr, sizeof(edata))) |
768 | return -EFAULT; | 768 | return -EFAULT; |
769 | 769 | ||
770 | return dev->ethtool_ops->set_eee(dev, &edata); | 770 | return dev->ethtool_ops->set_eee(dev, &edata); |
771 | } | 771 | } |
772 | 772 | ||
773 | static int ethtool_nway_reset(struct net_device *dev) | 773 | static int ethtool_nway_reset(struct net_device *dev) |
774 | { | 774 | { |
775 | if (!dev->ethtool_ops->nway_reset) | 775 | if (!dev->ethtool_ops->nway_reset) |
776 | return -EOPNOTSUPP; | 776 | return -EOPNOTSUPP; |
777 | 777 | ||
778 | return dev->ethtool_ops->nway_reset(dev); | 778 | return dev->ethtool_ops->nway_reset(dev); |
779 | } | 779 | } |
780 | 780 | ||
781 | static int ethtool_get_link(struct net_device *dev, char __user *useraddr) | 781 | static int ethtool_get_link(struct net_device *dev, char __user *useraddr) |
782 | { | 782 | { |
783 | struct ethtool_value edata = { .cmd = ETHTOOL_GLINK }; | 783 | struct ethtool_value edata = { .cmd = ETHTOOL_GLINK }; |
784 | 784 | ||
785 | if (!dev->ethtool_ops->get_link) | 785 | if (!dev->ethtool_ops->get_link) |
786 | return -EOPNOTSUPP; | 786 | return -EOPNOTSUPP; |
787 | 787 | ||
788 | edata.data = netif_running(dev) && dev->ethtool_ops->get_link(dev); | 788 | edata.data = netif_running(dev) && dev->ethtool_ops->get_link(dev); |
789 | 789 | ||
790 | if (copy_to_user(useraddr, &edata, sizeof(edata))) | 790 | if (copy_to_user(useraddr, &edata, sizeof(edata))) |
791 | return -EFAULT; | 791 | return -EFAULT; |
792 | return 0; | 792 | return 0; |
793 | } | 793 | } |
794 | 794 | ||
795 | static int ethtool_get_any_eeprom(struct net_device *dev, void __user *useraddr, | 795 | static int ethtool_get_any_eeprom(struct net_device *dev, void __user *useraddr, |
796 | int (*getter)(struct net_device *, | 796 | int (*getter)(struct net_device *, |
797 | struct ethtool_eeprom *, u8 *), | 797 | struct ethtool_eeprom *, u8 *), |
798 | u32 total_len) | 798 | u32 total_len) |
799 | { | 799 | { |
800 | struct ethtool_eeprom eeprom; | 800 | struct ethtool_eeprom eeprom; |
801 | void __user *userbuf = useraddr + sizeof(eeprom); | 801 | void __user *userbuf = useraddr + sizeof(eeprom); |
802 | u32 bytes_remaining; | 802 | u32 bytes_remaining; |
803 | u8 *data; | 803 | u8 *data; |
804 | int ret = 0; | 804 | int ret = 0; |
805 | 805 | ||
806 | if (copy_from_user(&eeprom, useraddr, sizeof(eeprom))) | 806 | if (copy_from_user(&eeprom, useraddr, sizeof(eeprom))) |
807 | return -EFAULT; | 807 | return -EFAULT; |
808 | 808 | ||
809 | /* Check for wrap and zero */ | 809 | /* Check for wrap and zero */ |
810 | if (eeprom.offset + eeprom.len <= eeprom.offset) | 810 | if (eeprom.offset + eeprom.len <= eeprom.offset) |
811 | return -EINVAL; | 811 | return -EINVAL; |
812 | 812 | ||
813 | /* Check for exceeding total eeprom len */ | 813 | /* Check for exceeding total eeprom len */ |
814 | if (eeprom.offset + eeprom.len > total_len) | 814 | if (eeprom.offset + eeprom.len > total_len) |
815 | return -EINVAL; | 815 | return -EINVAL; |
816 | 816 | ||
817 | data = kmalloc(PAGE_SIZE, GFP_USER); | 817 | data = kmalloc(PAGE_SIZE, GFP_USER); |
818 | if (!data) | 818 | if (!data) |
819 | return -ENOMEM; | 819 | return -ENOMEM; |
820 | 820 | ||
821 | bytes_remaining = eeprom.len; | 821 | bytes_remaining = eeprom.len; |
822 | while (bytes_remaining > 0) { | 822 | while (bytes_remaining > 0) { |
823 | eeprom.len = min(bytes_remaining, (u32)PAGE_SIZE); | 823 | eeprom.len = min(bytes_remaining, (u32)PAGE_SIZE); |
824 | 824 | ||
825 | ret = getter(dev, &eeprom, data); | 825 | ret = getter(dev, &eeprom, data); |
826 | if (ret) | 826 | if (ret) |
827 | break; | 827 | break; |
828 | if (copy_to_user(userbuf, data, eeprom.len)) { | 828 | if (copy_to_user(userbuf, data, eeprom.len)) { |
829 | ret = -EFAULT; | 829 | ret = -EFAULT; |
830 | break; | 830 | break; |
831 | } | 831 | } |
832 | userbuf += eeprom.len; | 832 | userbuf += eeprom.len; |
833 | eeprom.offset += eeprom.len; | 833 | eeprom.offset += eeprom.len; |
834 | bytes_remaining -= eeprom.len; | 834 | bytes_remaining -= eeprom.len; |
835 | } | 835 | } |
836 | 836 | ||
837 | eeprom.len = userbuf - (useraddr + sizeof(eeprom)); | 837 | eeprom.len = userbuf - (useraddr + sizeof(eeprom)); |
838 | eeprom.offset -= eeprom.len; | 838 | eeprom.offset -= eeprom.len; |
839 | if (copy_to_user(useraddr, &eeprom, sizeof(eeprom))) | 839 | if (copy_to_user(useraddr, &eeprom, sizeof(eeprom))) |
840 | ret = -EFAULT; | 840 | ret = -EFAULT; |
841 | 841 | ||
842 | kfree(data); | 842 | kfree(data); |
843 | return ret; | 843 | return ret; |
844 | } | 844 | } |
845 | 845 | ||
846 | static int ethtool_get_eeprom(struct net_device *dev, void __user *useraddr) | 846 | static int ethtool_get_eeprom(struct net_device *dev, void __user *useraddr) |
847 | { | 847 | { |
848 | const struct ethtool_ops *ops = dev->ethtool_ops; | 848 | const struct ethtool_ops *ops = dev->ethtool_ops; |
849 | 849 | ||
850 | if (!ops->get_eeprom || !ops->get_eeprom_len) | 850 | if (!ops->get_eeprom || !ops->get_eeprom_len) |
851 | return -EOPNOTSUPP; | 851 | return -EOPNOTSUPP; |
852 | 852 | ||
853 | return ethtool_get_any_eeprom(dev, useraddr, ops->get_eeprom, | 853 | return ethtool_get_any_eeprom(dev, useraddr, ops->get_eeprom, |
854 | ops->get_eeprom_len(dev)); | 854 | ops->get_eeprom_len(dev)); |
855 | } | 855 | } |
856 | 856 | ||
857 | static int ethtool_set_eeprom(struct net_device *dev, void __user *useraddr) | 857 | static int ethtool_set_eeprom(struct net_device *dev, void __user *useraddr) |
858 | { | 858 | { |
859 | struct ethtool_eeprom eeprom; | 859 | struct ethtool_eeprom eeprom; |
860 | const struct ethtool_ops *ops = dev->ethtool_ops; | 860 | const struct ethtool_ops *ops = dev->ethtool_ops; |
861 | void __user *userbuf = useraddr + sizeof(eeprom); | 861 | void __user *userbuf = useraddr + sizeof(eeprom); |
862 | u32 bytes_remaining; | 862 | u32 bytes_remaining; |
863 | u8 *data; | 863 | u8 *data; |
864 | int ret = 0; | 864 | int ret = 0; |
865 | 865 | ||
866 | if (!ops->set_eeprom || !ops->get_eeprom_len) | 866 | if (!ops->set_eeprom || !ops->get_eeprom_len) |
867 | return -EOPNOTSUPP; | 867 | return -EOPNOTSUPP; |
868 | 868 | ||
869 | if (copy_from_user(&eeprom, useraddr, sizeof(eeprom))) | 869 | if (copy_from_user(&eeprom, useraddr, sizeof(eeprom))) |
870 | return -EFAULT; | 870 | return -EFAULT; |
871 | 871 | ||
872 | /* Check for wrap and zero */ | 872 | /* Check for wrap and zero */ |
873 | if (eeprom.offset + eeprom.len <= eeprom.offset) | 873 | if (eeprom.offset + eeprom.len <= eeprom.offset) |
874 | return -EINVAL; | 874 | return -EINVAL; |
875 | 875 | ||
876 | /* Check for exceeding total eeprom len */ | 876 | /* Check for exceeding total eeprom len */ |
877 | if (eeprom.offset + eeprom.len > ops->get_eeprom_len(dev)) | 877 | if (eeprom.offset + eeprom.len > ops->get_eeprom_len(dev)) |
878 | return -EINVAL; | 878 | return -EINVAL; |
879 | 879 | ||
880 | data = kmalloc(PAGE_SIZE, GFP_USER); | 880 | data = kmalloc(PAGE_SIZE, GFP_USER); |
881 | if (!data) | 881 | if (!data) |
882 | return -ENOMEM; | 882 | return -ENOMEM; |
883 | 883 | ||
884 | bytes_remaining = eeprom.len; | 884 | bytes_remaining = eeprom.len; |
885 | while (bytes_remaining > 0) { | 885 | while (bytes_remaining > 0) { |
886 | eeprom.len = min(bytes_remaining, (u32)PAGE_SIZE); | 886 | eeprom.len = min(bytes_remaining, (u32)PAGE_SIZE); |
887 | 887 | ||
888 | if (copy_from_user(data, userbuf, eeprom.len)) { | 888 | if (copy_from_user(data, userbuf, eeprom.len)) { |
889 | ret = -EFAULT; | 889 | ret = -EFAULT; |
890 | break; | 890 | break; |
891 | } | 891 | } |
892 | ret = ops->set_eeprom(dev, &eeprom, data); | 892 | ret = ops->set_eeprom(dev, &eeprom, data); |
893 | if (ret) | 893 | if (ret) |
894 | break; | 894 | break; |
895 | userbuf += eeprom.len; | 895 | userbuf += eeprom.len; |
896 | eeprom.offset += eeprom.len; | 896 | eeprom.offset += eeprom.len; |
897 | bytes_remaining -= eeprom.len; | 897 | bytes_remaining -= eeprom.len; |
898 | } | 898 | } |
899 | 899 | ||
900 | kfree(data); | 900 | kfree(data); |
901 | return ret; | 901 | return ret; |
902 | } | 902 | } |
903 | 903 | ||
904 | static noinline_for_stack int ethtool_get_coalesce(struct net_device *dev, | 904 | static noinline_for_stack int ethtool_get_coalesce(struct net_device *dev, |
905 | void __user *useraddr) | 905 | void __user *useraddr) |
906 | { | 906 | { |
907 | struct ethtool_coalesce coalesce = { .cmd = ETHTOOL_GCOALESCE }; | 907 | struct ethtool_coalesce coalesce = { .cmd = ETHTOOL_GCOALESCE }; |
908 | 908 | ||
909 | if (!dev->ethtool_ops->get_coalesce) | 909 | if (!dev->ethtool_ops->get_coalesce) |
910 | return -EOPNOTSUPP; | 910 | return -EOPNOTSUPP; |
911 | 911 | ||
912 | dev->ethtool_ops->get_coalesce(dev, &coalesce); | 912 | dev->ethtool_ops->get_coalesce(dev, &coalesce); |
913 | 913 | ||
914 | if (copy_to_user(useraddr, &coalesce, sizeof(coalesce))) | 914 | if (copy_to_user(useraddr, &coalesce, sizeof(coalesce))) |
915 | return -EFAULT; | 915 | return -EFAULT; |
916 | return 0; | 916 | return 0; |
917 | } | 917 | } |
918 | 918 | ||
919 | static noinline_for_stack int ethtool_set_coalesce(struct net_device *dev, | 919 | static noinline_for_stack int ethtool_set_coalesce(struct net_device *dev, |
920 | void __user *useraddr) | 920 | void __user *useraddr) |
921 | { | 921 | { |
922 | struct ethtool_coalesce coalesce; | 922 | struct ethtool_coalesce coalesce; |
923 | 923 | ||
924 | if (!dev->ethtool_ops->set_coalesce) | 924 | if (!dev->ethtool_ops->set_coalesce) |
925 | return -EOPNOTSUPP; | 925 | return -EOPNOTSUPP; |
926 | 926 | ||
927 | if (copy_from_user(&coalesce, useraddr, sizeof(coalesce))) | 927 | if (copy_from_user(&coalesce, useraddr, sizeof(coalesce))) |
928 | return -EFAULT; | 928 | return -EFAULT; |
929 | 929 | ||
930 | return dev->ethtool_ops->set_coalesce(dev, &coalesce); | 930 | return dev->ethtool_ops->set_coalesce(dev, &coalesce); |
931 | } | 931 | } |
932 | 932 | ||
933 | static int ethtool_get_ringparam(struct net_device *dev, void __user *useraddr) | 933 | static int ethtool_get_ringparam(struct net_device *dev, void __user *useraddr) |
934 | { | 934 | { |
935 | struct ethtool_ringparam ringparam = { .cmd = ETHTOOL_GRINGPARAM }; | 935 | struct ethtool_ringparam ringparam = { .cmd = ETHTOOL_GRINGPARAM }; |
936 | 936 | ||
937 | if (!dev->ethtool_ops->get_ringparam) | 937 | if (!dev->ethtool_ops->get_ringparam) |
938 | return -EOPNOTSUPP; | 938 | return -EOPNOTSUPP; |
939 | 939 | ||
940 | dev->ethtool_ops->get_ringparam(dev, &ringparam); | 940 | dev->ethtool_ops->get_ringparam(dev, &ringparam); |
941 | 941 | ||
942 | if (copy_to_user(useraddr, &ringparam, sizeof(ringparam))) | 942 | if (copy_to_user(useraddr, &ringparam, sizeof(ringparam))) |
943 | return -EFAULT; | 943 | return -EFAULT; |
944 | return 0; | 944 | return 0; |
945 | } | 945 | } |
946 | 946 | ||
947 | static int ethtool_set_ringparam(struct net_device *dev, void __user *useraddr) | 947 | static int ethtool_set_ringparam(struct net_device *dev, void __user *useraddr) |
948 | { | 948 | { |
949 | struct ethtool_ringparam ringparam; | 949 | struct ethtool_ringparam ringparam; |
950 | 950 | ||
951 | if (!dev->ethtool_ops->set_ringparam) | 951 | if (!dev->ethtool_ops->set_ringparam) |
952 | return -EOPNOTSUPP; | 952 | return -EOPNOTSUPP; |
953 | 953 | ||
954 | if (copy_from_user(&ringparam, useraddr, sizeof(ringparam))) | 954 | if (copy_from_user(&ringparam, useraddr, sizeof(ringparam))) |
955 | return -EFAULT; | 955 | return -EFAULT; |
956 | 956 | ||
957 | return dev->ethtool_ops->set_ringparam(dev, &ringparam); | 957 | return dev->ethtool_ops->set_ringparam(dev, &ringparam); |
958 | } | 958 | } |
959 | 959 | ||
960 | static noinline_for_stack int ethtool_get_channels(struct net_device *dev, | 960 | static noinline_for_stack int ethtool_get_channels(struct net_device *dev, |
961 | void __user *useraddr) | 961 | void __user *useraddr) |
962 | { | 962 | { |
963 | struct ethtool_channels channels = { .cmd = ETHTOOL_GCHANNELS }; | 963 | struct ethtool_channels channels = { .cmd = ETHTOOL_GCHANNELS }; |
964 | 964 | ||
965 | if (!dev->ethtool_ops->get_channels) | 965 | if (!dev->ethtool_ops->get_channels) |
966 | return -EOPNOTSUPP; | 966 | return -EOPNOTSUPP; |
967 | 967 | ||
968 | dev->ethtool_ops->get_channels(dev, &channels); | 968 | dev->ethtool_ops->get_channels(dev, &channels); |
969 | 969 | ||
970 | if (copy_to_user(useraddr, &channels, sizeof(channels))) | 970 | if (copy_to_user(useraddr, &channels, sizeof(channels))) |
971 | return -EFAULT; | 971 | return -EFAULT; |
972 | return 0; | 972 | return 0; |
973 | } | 973 | } |
974 | 974 | ||
975 | static noinline_for_stack int ethtool_set_channels(struct net_device *dev, | 975 | static noinline_for_stack int ethtool_set_channels(struct net_device *dev, |
976 | void __user *useraddr) | 976 | void __user *useraddr) |
977 | { | 977 | { |
978 | struct ethtool_channels channels; | 978 | struct ethtool_channels channels; |
979 | 979 | ||
980 | if (!dev->ethtool_ops->set_channels) | 980 | if (!dev->ethtool_ops->set_channels) |
981 | return -EOPNOTSUPP; | 981 | return -EOPNOTSUPP; |
982 | 982 | ||
983 | if (copy_from_user(&channels, useraddr, sizeof(channels))) | 983 | if (copy_from_user(&channels, useraddr, sizeof(channels))) |
984 | return -EFAULT; | 984 | return -EFAULT; |
985 | 985 | ||
986 | return dev->ethtool_ops->set_channels(dev, &channels); | 986 | return dev->ethtool_ops->set_channels(dev, &channels); |
987 | } | 987 | } |
988 | 988 | ||
989 | static int ethtool_get_pauseparam(struct net_device *dev, void __user *useraddr) | 989 | static int ethtool_get_pauseparam(struct net_device *dev, void __user *useraddr) |
990 | { | 990 | { |
991 | struct ethtool_pauseparam pauseparam = { ETHTOOL_GPAUSEPARAM }; | 991 | struct ethtool_pauseparam pauseparam = { ETHTOOL_GPAUSEPARAM }; |
992 | 992 | ||
993 | if (!dev->ethtool_ops->get_pauseparam) | 993 | if (!dev->ethtool_ops->get_pauseparam) |
994 | return -EOPNOTSUPP; | 994 | return -EOPNOTSUPP; |
995 | 995 | ||
996 | dev->ethtool_ops->get_pauseparam(dev, &pauseparam); | 996 | dev->ethtool_ops->get_pauseparam(dev, &pauseparam); |
997 | 997 | ||
998 | if (copy_to_user(useraddr, &pauseparam, sizeof(pauseparam))) | 998 | if (copy_to_user(useraddr, &pauseparam, sizeof(pauseparam))) |
999 | return -EFAULT; | 999 | return -EFAULT; |
1000 | return 0; | 1000 | return 0; |
1001 | } | 1001 | } |
1002 | 1002 | ||
1003 | static int ethtool_set_pauseparam(struct net_device *dev, void __user *useraddr) | 1003 | static int ethtool_set_pauseparam(struct net_device *dev, void __user *useraddr) |
1004 | { | 1004 | { |
1005 | struct ethtool_pauseparam pauseparam; | 1005 | struct ethtool_pauseparam pauseparam; |
1006 | 1006 | ||
1007 | if (!dev->ethtool_ops->set_pauseparam) | 1007 | if (!dev->ethtool_ops->set_pauseparam) |
1008 | return -EOPNOTSUPP; | 1008 | return -EOPNOTSUPP; |
1009 | 1009 | ||
1010 | if (copy_from_user(&pauseparam, useraddr, sizeof(pauseparam))) | 1010 | if (copy_from_user(&pauseparam, useraddr, sizeof(pauseparam))) |
1011 | return -EFAULT; | 1011 | return -EFAULT; |
1012 | 1012 | ||
1013 | return dev->ethtool_ops->set_pauseparam(dev, &pauseparam); | 1013 | return dev->ethtool_ops->set_pauseparam(dev, &pauseparam); |
1014 | } | 1014 | } |
1015 | 1015 | ||
1016 | static int ethtool_self_test(struct net_device *dev, char __user *useraddr) | 1016 | static int ethtool_self_test(struct net_device *dev, char __user *useraddr) |
1017 | { | 1017 | { |
1018 | struct ethtool_test test; | 1018 | struct ethtool_test test; |
1019 | const struct ethtool_ops *ops = dev->ethtool_ops; | 1019 | const struct ethtool_ops *ops = dev->ethtool_ops; |
1020 | u64 *data; | 1020 | u64 *data; |
1021 | int ret, test_len; | 1021 | int ret, test_len; |
1022 | 1022 | ||
1023 | if (!ops->self_test || !ops->get_sset_count) | 1023 | if (!ops->self_test || !ops->get_sset_count) |
1024 | return -EOPNOTSUPP; | 1024 | return -EOPNOTSUPP; |
1025 | 1025 | ||
1026 | test_len = ops->get_sset_count(dev, ETH_SS_TEST); | 1026 | test_len = ops->get_sset_count(dev, ETH_SS_TEST); |
1027 | if (test_len < 0) | 1027 | if (test_len < 0) |
1028 | return test_len; | 1028 | return test_len; |
1029 | WARN_ON(test_len == 0); | 1029 | WARN_ON(test_len == 0); |
1030 | 1030 | ||
1031 | if (copy_from_user(&test, useraddr, sizeof(test))) | 1031 | if (copy_from_user(&test, useraddr, sizeof(test))) |
1032 | return -EFAULT; | 1032 | return -EFAULT; |
1033 | 1033 | ||
1034 | test.len = test_len; | 1034 | test.len = test_len; |
1035 | data = kmalloc(test_len * sizeof(u64), GFP_USER); | 1035 | data = kmalloc(test_len * sizeof(u64), GFP_USER); |
1036 | if (!data) | 1036 | if (!data) |
1037 | return -ENOMEM; | 1037 | return -ENOMEM; |
1038 | 1038 | ||
1039 | ops->self_test(dev, &test, data); | 1039 | ops->self_test(dev, &test, data); |
1040 | 1040 | ||
1041 | ret = -EFAULT; | 1041 | ret = -EFAULT; |
1042 | if (copy_to_user(useraddr, &test, sizeof(test))) | 1042 | if (copy_to_user(useraddr, &test, sizeof(test))) |
1043 | goto out; | 1043 | goto out; |
1044 | useraddr += sizeof(test); | 1044 | useraddr += sizeof(test); |
1045 | if (copy_to_user(useraddr, data, test.len * sizeof(u64))) | 1045 | if (copy_to_user(useraddr, data, test.len * sizeof(u64))) |
1046 | goto out; | 1046 | goto out; |
1047 | ret = 0; | 1047 | ret = 0; |
1048 | 1048 | ||
1049 | out: | 1049 | out: |
1050 | kfree(data); | 1050 | kfree(data); |
1051 | return ret; | 1051 | return ret; |
1052 | } | 1052 | } |
1053 | 1053 | ||
1054 | static int ethtool_get_strings(struct net_device *dev, void __user *useraddr) | 1054 | static int ethtool_get_strings(struct net_device *dev, void __user *useraddr) |
1055 | { | 1055 | { |
1056 | struct ethtool_gstrings gstrings; | 1056 | struct ethtool_gstrings gstrings; |
1057 | u8 *data; | 1057 | u8 *data; |
1058 | int ret; | 1058 | int ret; |
1059 | 1059 | ||
1060 | if (copy_from_user(&gstrings, useraddr, sizeof(gstrings))) | 1060 | if (copy_from_user(&gstrings, useraddr, sizeof(gstrings))) |
1061 | return -EFAULT; | 1061 | return -EFAULT; |
1062 | 1062 | ||
1063 | ret = __ethtool_get_sset_count(dev, gstrings.string_set); | 1063 | ret = __ethtool_get_sset_count(dev, gstrings.string_set); |
1064 | if (ret < 0) | 1064 | if (ret < 0) |
1065 | return ret; | 1065 | return ret; |
1066 | 1066 | ||
1067 | gstrings.len = ret; | 1067 | gstrings.len = ret; |
1068 | 1068 | ||
1069 | data = kmalloc(gstrings.len * ETH_GSTRING_LEN, GFP_USER); | 1069 | data = kmalloc(gstrings.len * ETH_GSTRING_LEN, GFP_USER); |
1070 | if (!data) | 1070 | if (!data) |
1071 | return -ENOMEM; | 1071 | return -ENOMEM; |
1072 | 1072 | ||
1073 | __ethtool_get_strings(dev, gstrings.string_set, data); | 1073 | __ethtool_get_strings(dev, gstrings.string_set, data); |
1074 | 1074 | ||
1075 | ret = -EFAULT; | 1075 | ret = -EFAULT; |
1076 | if (copy_to_user(useraddr, &gstrings, sizeof(gstrings))) | 1076 | if (copy_to_user(useraddr, &gstrings, sizeof(gstrings))) |
1077 | goto out; | 1077 | goto out; |
1078 | useraddr += sizeof(gstrings); | 1078 | useraddr += sizeof(gstrings); |
1079 | if (copy_to_user(useraddr, data, gstrings.len * ETH_GSTRING_LEN)) | 1079 | if (copy_to_user(useraddr, data, gstrings.len * ETH_GSTRING_LEN)) |
1080 | goto out; | 1080 | goto out; |
1081 | ret = 0; | 1081 | ret = 0; |
1082 | 1082 | ||
1083 | out: | 1083 | out: |
1084 | kfree(data); | 1084 | kfree(data); |
1085 | return ret; | 1085 | return ret; |
1086 | } | 1086 | } |
1087 | 1087 | ||
1088 | static int ethtool_phys_id(struct net_device *dev, void __user *useraddr) | 1088 | static int ethtool_phys_id(struct net_device *dev, void __user *useraddr) |
1089 | { | 1089 | { |
1090 | struct ethtool_value id; | 1090 | struct ethtool_value id; |
1091 | static bool busy; | 1091 | static bool busy; |
1092 | const struct ethtool_ops *ops = dev->ethtool_ops; | 1092 | const struct ethtool_ops *ops = dev->ethtool_ops; |
1093 | int rc; | 1093 | int rc; |
1094 | 1094 | ||
1095 | if (!ops->set_phys_id) | 1095 | if (!ops->set_phys_id) |
1096 | return -EOPNOTSUPP; | 1096 | return -EOPNOTSUPP; |
1097 | 1097 | ||
1098 | if (busy) | 1098 | if (busy) |
1099 | return -EBUSY; | 1099 | return -EBUSY; |
1100 | 1100 | ||
1101 | if (copy_from_user(&id, useraddr, sizeof(id))) | 1101 | if (copy_from_user(&id, useraddr, sizeof(id))) |
1102 | return -EFAULT; | 1102 | return -EFAULT; |
1103 | 1103 | ||
1104 | rc = ops->set_phys_id(dev, ETHTOOL_ID_ACTIVE); | 1104 | rc = ops->set_phys_id(dev, ETHTOOL_ID_ACTIVE); |
1105 | if (rc < 0) | 1105 | if (rc < 0) |
1106 | return rc; | 1106 | return rc; |
1107 | 1107 | ||
1108 | /* Drop the RTNL lock while waiting, but prevent reentry or | 1108 | /* Drop the RTNL lock while waiting, but prevent reentry or |
1109 | * removal of the device. | 1109 | * removal of the device. |
1110 | */ | 1110 | */ |
1111 | busy = true; | 1111 | busy = true; |
1112 | dev_hold(dev); | 1112 | dev_hold(dev); |
1113 | rtnl_unlock(); | 1113 | rtnl_unlock(); |
1114 | 1114 | ||
1115 | if (rc == 0) { | 1115 | if (rc == 0) { |
1116 | /* Driver will handle this itself */ | 1116 | /* Driver will handle this itself */ |
1117 | schedule_timeout_interruptible( | 1117 | schedule_timeout_interruptible( |
1118 | id.data ? (id.data * HZ) : MAX_SCHEDULE_TIMEOUT); | 1118 | id.data ? (id.data * HZ) : MAX_SCHEDULE_TIMEOUT); |
1119 | } else { | 1119 | } else { |
1120 | /* Driver expects to be called at twice the frequency in rc */ | 1120 | /* Driver expects to be called at twice the frequency in rc */ |
1121 | int n = rc * 2, i, interval = HZ / n; | 1121 | int n = rc * 2, i, interval = HZ / n; |
1122 | 1122 | ||
1123 | /* Count down seconds */ | 1123 | /* Count down seconds */ |
1124 | do { | 1124 | do { |
1125 | /* Count down iterations per second */ | 1125 | /* Count down iterations per second */ |
1126 | i = n; | 1126 | i = n; |
1127 | do { | 1127 | do { |
1128 | rtnl_lock(); | 1128 | rtnl_lock(); |
1129 | rc = ops->set_phys_id(dev, | 1129 | rc = ops->set_phys_id(dev, |
1130 | (i & 1) ? ETHTOOL_ID_OFF : ETHTOOL_ID_ON); | 1130 | (i & 1) ? ETHTOOL_ID_OFF : ETHTOOL_ID_ON); |
1131 | rtnl_unlock(); | 1131 | rtnl_unlock(); |
1132 | if (rc) | 1132 | if (rc) |
1133 | break; | 1133 | break; |
1134 | schedule_timeout_interruptible(interval); | 1134 | schedule_timeout_interruptible(interval); |
1135 | } while (!signal_pending(current) && --i != 0); | 1135 | } while (!signal_pending(current) && --i != 0); |
1136 | } while (!signal_pending(current) && | 1136 | } while (!signal_pending(current) && |
1137 | (id.data == 0 || --id.data != 0)); | 1137 | (id.data == 0 || --id.data != 0)); |
1138 | } | 1138 | } |
1139 | 1139 | ||
1140 | rtnl_lock(); | 1140 | rtnl_lock(); |
1141 | dev_put(dev); | 1141 | dev_put(dev); |
1142 | busy = false; | 1142 | busy = false; |
1143 | 1143 | ||
1144 | (void) ops->set_phys_id(dev, ETHTOOL_ID_INACTIVE); | 1144 | (void) ops->set_phys_id(dev, ETHTOOL_ID_INACTIVE); |
1145 | return rc; | 1145 | return rc; |
1146 | } | 1146 | } |
1147 | 1147 | ||
1148 | static int ethtool_get_stats(struct net_device *dev, void __user *useraddr) | 1148 | static int ethtool_get_stats(struct net_device *dev, void __user *useraddr) |
1149 | { | 1149 | { |
1150 | struct ethtool_stats stats; | 1150 | struct ethtool_stats stats; |
1151 | const struct ethtool_ops *ops = dev->ethtool_ops; | 1151 | const struct ethtool_ops *ops = dev->ethtool_ops; |
1152 | u64 *data; | 1152 | u64 *data; |
1153 | int ret, n_stats; | 1153 | int ret, n_stats; |
1154 | 1154 | ||
1155 | if (!ops->get_ethtool_stats || !ops->get_sset_count) | 1155 | if (!ops->get_ethtool_stats || !ops->get_sset_count) |
1156 | return -EOPNOTSUPP; | 1156 | return -EOPNOTSUPP; |
1157 | 1157 | ||
1158 | n_stats = ops->get_sset_count(dev, ETH_SS_STATS); | 1158 | n_stats = ops->get_sset_count(dev, ETH_SS_STATS); |
1159 | if (n_stats < 0) | 1159 | if (n_stats < 0) |
1160 | return n_stats; | 1160 | return n_stats; |
1161 | WARN_ON(n_stats == 0); | 1161 | WARN_ON(n_stats == 0); |
1162 | 1162 | ||
1163 | if (copy_from_user(&stats, useraddr, sizeof(stats))) | 1163 | if (copy_from_user(&stats, useraddr, sizeof(stats))) |
1164 | return -EFAULT; | 1164 | return -EFAULT; |
1165 | 1165 | ||
1166 | stats.n_stats = n_stats; | 1166 | stats.n_stats = n_stats; |
1167 | data = kmalloc(n_stats * sizeof(u64), GFP_USER); | 1167 | data = kmalloc(n_stats * sizeof(u64), GFP_USER); |
1168 | if (!data) | 1168 | if (!data) |
1169 | return -ENOMEM; | 1169 | return -ENOMEM; |
1170 | 1170 | ||
1171 | ops->get_ethtool_stats(dev, &stats, data); | 1171 | ops->get_ethtool_stats(dev, &stats, data); |
1172 | 1172 | ||
1173 | ret = -EFAULT; | 1173 | ret = -EFAULT; |
1174 | if (copy_to_user(useraddr, &stats, sizeof(stats))) | 1174 | if (copy_to_user(useraddr, &stats, sizeof(stats))) |
1175 | goto out; | 1175 | goto out; |
1176 | useraddr += sizeof(stats); | 1176 | useraddr += sizeof(stats); |
1177 | if (copy_to_user(useraddr, data, stats.n_stats * sizeof(u64))) | 1177 | if (copy_to_user(useraddr, data, stats.n_stats * sizeof(u64))) |
1178 | goto out; | 1178 | goto out; |
1179 | ret = 0; | 1179 | ret = 0; |
1180 | 1180 | ||
1181 | out: | 1181 | out: |
1182 | kfree(data); | 1182 | kfree(data); |
1183 | return ret; | 1183 | return ret; |
1184 | } | 1184 | } |
1185 | 1185 | ||
1186 | static int ethtool_get_perm_addr(struct net_device *dev, void __user *useraddr) | 1186 | static int ethtool_get_perm_addr(struct net_device *dev, void __user *useraddr) |
1187 | { | 1187 | { |
1188 | struct ethtool_perm_addr epaddr; | 1188 | struct ethtool_perm_addr epaddr; |
1189 | 1189 | ||
1190 | if (copy_from_user(&epaddr, useraddr, sizeof(epaddr))) | 1190 | if (copy_from_user(&epaddr, useraddr, sizeof(epaddr))) |
1191 | return -EFAULT; | 1191 | return -EFAULT; |
1192 | 1192 | ||
1193 | if (epaddr.size < dev->addr_len) | 1193 | if (epaddr.size < dev->addr_len) |
1194 | return -ETOOSMALL; | 1194 | return -ETOOSMALL; |
1195 | epaddr.size = dev->addr_len; | 1195 | epaddr.size = dev->addr_len; |
1196 | 1196 | ||
1197 | if (copy_to_user(useraddr, &epaddr, sizeof(epaddr))) | 1197 | if (copy_to_user(useraddr, &epaddr, sizeof(epaddr))) |
1198 | return -EFAULT; | 1198 | return -EFAULT; |
1199 | useraddr += sizeof(epaddr); | 1199 | useraddr += sizeof(epaddr); |
1200 | if (copy_to_user(useraddr, dev->perm_addr, epaddr.size)) | 1200 | if (copy_to_user(useraddr, dev->perm_addr, epaddr.size)) |
1201 | return -EFAULT; | 1201 | return -EFAULT; |
1202 | return 0; | 1202 | return 0; |
1203 | } | 1203 | } |
1204 | 1204 | ||
1205 | static int ethtool_get_value(struct net_device *dev, char __user *useraddr, | 1205 | static int ethtool_get_value(struct net_device *dev, char __user *useraddr, |
1206 | u32 cmd, u32 (*actor)(struct net_device *)) | 1206 | u32 cmd, u32 (*actor)(struct net_device *)) |
1207 | { | 1207 | { |
1208 | struct ethtool_value edata = { .cmd = cmd }; | 1208 | struct ethtool_value edata = { .cmd = cmd }; |
1209 | 1209 | ||
1210 | if (!actor) | 1210 | if (!actor) |
1211 | return -EOPNOTSUPP; | 1211 | return -EOPNOTSUPP; |
1212 | 1212 | ||
1213 | edata.data = actor(dev); | 1213 | edata.data = actor(dev); |
1214 | 1214 | ||
1215 | if (copy_to_user(useraddr, &edata, sizeof(edata))) | 1215 | if (copy_to_user(useraddr, &edata, sizeof(edata))) |
1216 | return -EFAULT; | 1216 | return -EFAULT; |
1217 | return 0; | 1217 | return 0; |
1218 | } | 1218 | } |
1219 | 1219 | ||
1220 | static int ethtool_set_value_void(struct net_device *dev, char __user *useraddr, | 1220 | static int ethtool_set_value_void(struct net_device *dev, char __user *useraddr, |
1221 | void (*actor)(struct net_device *, u32)) | 1221 | void (*actor)(struct net_device *, u32)) |
1222 | { | 1222 | { |
1223 | struct ethtool_value edata; | 1223 | struct ethtool_value edata; |
1224 | 1224 | ||
1225 | if (!actor) | 1225 | if (!actor) |
1226 | return -EOPNOTSUPP; | 1226 | return -EOPNOTSUPP; |
1227 | 1227 | ||
1228 | if (copy_from_user(&edata, useraddr, sizeof(edata))) | 1228 | if (copy_from_user(&edata, useraddr, sizeof(edata))) |
1229 | return -EFAULT; | 1229 | return -EFAULT; |
1230 | 1230 | ||
1231 | actor(dev, edata.data); | 1231 | actor(dev, edata.data); |
1232 | return 0; | 1232 | return 0; |
1233 | } | 1233 | } |
1234 | 1234 | ||
1235 | static int ethtool_set_value(struct net_device *dev, char __user *useraddr, | 1235 | static int ethtool_set_value(struct net_device *dev, char __user *useraddr, |
1236 | int (*actor)(struct net_device *, u32)) | 1236 | int (*actor)(struct net_device *, u32)) |
1237 | { | 1237 | { |
1238 | struct ethtool_value edata; | 1238 | struct ethtool_value edata; |
1239 | 1239 | ||
1240 | if (!actor) | 1240 | if (!actor) |
1241 | return -EOPNOTSUPP; | 1241 | return -EOPNOTSUPP; |
1242 | 1242 | ||
1243 | if (copy_from_user(&edata, useraddr, sizeof(edata))) | 1243 | if (copy_from_user(&edata, useraddr, sizeof(edata))) |
1244 | return -EFAULT; | 1244 | return -EFAULT; |
1245 | 1245 | ||
1246 | return actor(dev, edata.data); | 1246 | return actor(dev, edata.data); |
1247 | } | 1247 | } |
1248 | 1248 | ||
1249 | static noinline_for_stack int ethtool_flash_device(struct net_device *dev, | 1249 | static noinline_for_stack int ethtool_flash_device(struct net_device *dev, |
1250 | char __user *useraddr) | 1250 | char __user *useraddr) |
1251 | { | 1251 | { |
1252 | struct ethtool_flash efl; | 1252 | struct ethtool_flash efl; |
1253 | 1253 | ||
1254 | if (copy_from_user(&efl, useraddr, sizeof(efl))) | 1254 | if (copy_from_user(&efl, useraddr, sizeof(efl))) |
1255 | return -EFAULT; | 1255 | return -EFAULT; |
1256 | 1256 | ||
1257 | if (!dev->ethtool_ops->flash_device) | 1257 | if (!dev->ethtool_ops->flash_device) |
1258 | return -EOPNOTSUPP; | 1258 | return -EOPNOTSUPP; |
1259 | 1259 | ||
1260 | efl.data[ETHTOOL_FLASH_MAX_FILENAME - 1] = 0; | 1260 | efl.data[ETHTOOL_FLASH_MAX_FILENAME - 1] = 0; |
1261 | 1261 | ||
1262 | return dev->ethtool_ops->flash_device(dev, &efl); | 1262 | return dev->ethtool_ops->flash_device(dev, &efl); |
1263 | } | 1263 | } |
1264 | 1264 | ||
1265 | static int ethtool_set_dump(struct net_device *dev, | 1265 | static int ethtool_set_dump(struct net_device *dev, |
1266 | void __user *useraddr) | 1266 | void __user *useraddr) |
1267 | { | 1267 | { |
1268 | struct ethtool_dump dump; | 1268 | struct ethtool_dump dump; |
1269 | 1269 | ||
1270 | if (!dev->ethtool_ops->set_dump) | 1270 | if (!dev->ethtool_ops->set_dump) |
1271 | return -EOPNOTSUPP; | 1271 | return -EOPNOTSUPP; |
1272 | 1272 | ||
1273 | if (copy_from_user(&dump, useraddr, sizeof(dump))) | 1273 | if (copy_from_user(&dump, useraddr, sizeof(dump))) |
1274 | return -EFAULT; | 1274 | return -EFAULT; |
1275 | 1275 | ||
1276 | return dev->ethtool_ops->set_dump(dev, &dump); | 1276 | return dev->ethtool_ops->set_dump(dev, &dump); |
1277 | } | 1277 | } |
1278 | 1278 | ||
1279 | static int ethtool_get_dump_flag(struct net_device *dev, | 1279 | static int ethtool_get_dump_flag(struct net_device *dev, |
1280 | void __user *useraddr) | 1280 | void __user *useraddr) |
1281 | { | 1281 | { |
1282 | int ret; | 1282 | int ret; |
1283 | struct ethtool_dump dump; | 1283 | struct ethtool_dump dump; |
1284 | const struct ethtool_ops *ops = dev->ethtool_ops; | 1284 | const struct ethtool_ops *ops = dev->ethtool_ops; |
1285 | 1285 | ||
1286 | if (!ops->get_dump_flag) | 1286 | if (!ops->get_dump_flag) |
1287 | return -EOPNOTSUPP; | 1287 | return -EOPNOTSUPP; |
1288 | 1288 | ||
1289 | if (copy_from_user(&dump, useraddr, sizeof(dump))) | 1289 | if (copy_from_user(&dump, useraddr, sizeof(dump))) |
1290 | return -EFAULT; | 1290 | return -EFAULT; |
1291 | 1291 | ||
1292 | ret = ops->get_dump_flag(dev, &dump); | 1292 | ret = ops->get_dump_flag(dev, &dump); |
1293 | if (ret) | 1293 | if (ret) |
1294 | return ret; | 1294 | return ret; |
1295 | 1295 | ||
1296 | if (copy_to_user(useraddr, &dump, sizeof(dump))) | 1296 | if (copy_to_user(useraddr, &dump, sizeof(dump))) |
1297 | return -EFAULT; | 1297 | return -EFAULT; |
1298 | return 0; | 1298 | return 0; |
1299 | } | 1299 | } |
1300 | 1300 | ||
1301 | static int ethtool_get_dump_data(struct net_device *dev, | 1301 | static int ethtool_get_dump_data(struct net_device *dev, |
1302 | void __user *useraddr) | 1302 | void __user *useraddr) |
1303 | { | 1303 | { |
1304 | int ret; | 1304 | int ret; |
1305 | __u32 len; | 1305 | __u32 len; |
1306 | struct ethtool_dump dump, tmp; | 1306 | struct ethtool_dump dump, tmp; |
1307 | const struct ethtool_ops *ops = dev->ethtool_ops; | 1307 | const struct ethtool_ops *ops = dev->ethtool_ops; |
1308 | void *data = NULL; | 1308 | void *data = NULL; |
1309 | 1309 | ||
1310 | if (!ops->get_dump_data || !ops->get_dump_flag) | 1310 | if (!ops->get_dump_data || !ops->get_dump_flag) |
1311 | return -EOPNOTSUPP; | 1311 | return -EOPNOTSUPP; |
1312 | 1312 | ||
1313 | if (copy_from_user(&dump, useraddr, sizeof(dump))) | 1313 | if (copy_from_user(&dump, useraddr, sizeof(dump))) |
1314 | return -EFAULT; | 1314 | return -EFAULT; |
1315 | 1315 | ||
1316 | memset(&tmp, 0, sizeof(tmp)); | 1316 | memset(&tmp, 0, sizeof(tmp)); |
1317 | tmp.cmd = ETHTOOL_GET_DUMP_FLAG; | 1317 | tmp.cmd = ETHTOOL_GET_DUMP_FLAG; |
1318 | ret = ops->get_dump_flag(dev, &tmp); | 1318 | ret = ops->get_dump_flag(dev, &tmp); |
1319 | if (ret) | 1319 | if (ret) |
1320 | return ret; | 1320 | return ret; |
1321 | 1321 | ||
1322 | len = (tmp.len > dump.len) ? dump.len : tmp.len; | 1322 | len = (tmp.len > dump.len) ? dump.len : tmp.len; |
1323 | if (!len) | 1323 | if (!len) |
1324 | return -EFAULT; | 1324 | return -EFAULT; |
1325 | 1325 | ||
1326 | data = vzalloc(tmp.len); | 1326 | data = vzalloc(tmp.len); |
1327 | if (!data) | 1327 | if (!data) |
1328 | return -ENOMEM; | 1328 | return -ENOMEM; |
1329 | ret = ops->get_dump_data(dev, &dump, data); | 1329 | ret = ops->get_dump_data(dev, &dump, data); |
1330 | if (ret) | 1330 | if (ret) |
1331 | goto out; | 1331 | goto out; |
1332 | 1332 | ||
1333 | if (copy_to_user(useraddr, &dump, sizeof(dump))) { | 1333 | if (copy_to_user(useraddr, &dump, sizeof(dump))) { |
1334 | ret = -EFAULT; | 1334 | ret = -EFAULT; |
1335 | goto out; | 1335 | goto out; |
1336 | } | 1336 | } |
1337 | useraddr += offsetof(struct ethtool_dump, data); | 1337 | useraddr += offsetof(struct ethtool_dump, data); |
1338 | if (copy_to_user(useraddr, data, len)) | 1338 | if (copy_to_user(useraddr, data, len)) |
1339 | ret = -EFAULT; | 1339 | ret = -EFAULT; |
1340 | out: | 1340 | out: |
1341 | vfree(data); | 1341 | vfree(data); |
1342 | return ret; | 1342 | return ret; |
1343 | } | 1343 | } |
1344 | 1344 | ||
1345 | static int ethtool_get_ts_info(struct net_device *dev, void __user *useraddr) | 1345 | static int ethtool_get_ts_info(struct net_device *dev, void __user *useraddr) |
1346 | { | 1346 | { |
1347 | int err = 0; | 1347 | int err = 0; |
1348 | struct ethtool_ts_info info; | 1348 | struct ethtool_ts_info info; |
1349 | const struct ethtool_ops *ops = dev->ethtool_ops; | 1349 | const struct ethtool_ops *ops = dev->ethtool_ops; |
1350 | struct phy_device *phydev = dev->phydev; | 1350 | struct phy_device *phydev = dev->phydev; |
1351 | 1351 | ||
1352 | memset(&info, 0, sizeof(info)); | 1352 | memset(&info, 0, sizeof(info)); |
1353 | info.cmd = ETHTOOL_GET_TS_INFO; | 1353 | info.cmd = ETHTOOL_GET_TS_INFO; |
1354 | 1354 | ||
1355 | if (phydev && phydev->drv && phydev->drv->ts_info) { | 1355 | if (phydev && phydev->drv && phydev->drv->ts_info) { |
1356 | err = phydev->drv->ts_info(phydev, &info); | 1356 | err = phydev->drv->ts_info(phydev, &info); |
1357 | } else if (ops->get_ts_info) { | 1357 | } else if (ops->get_ts_info) { |
1358 | err = ops->get_ts_info(dev, &info); | 1358 | err = ops->get_ts_info(dev, &info); |
1359 | } else { | 1359 | } else { |
1360 | info.so_timestamping = | 1360 | info.so_timestamping = |
1361 | SOF_TIMESTAMPING_RX_SOFTWARE | | 1361 | SOF_TIMESTAMPING_RX_SOFTWARE | |
1362 | SOF_TIMESTAMPING_SOFTWARE; | 1362 | SOF_TIMESTAMPING_SOFTWARE; |
1363 | info.phc_index = -1; | 1363 | info.phc_index = -1; |
1364 | } | 1364 | } |
1365 | 1365 | ||
1366 | if (err) | 1366 | if (err) |
1367 | return err; | 1367 | return err; |
1368 | 1368 | ||
1369 | if (copy_to_user(useraddr, &info, sizeof(info))) | 1369 | if (copy_to_user(useraddr, &info, sizeof(info))) |
1370 | err = -EFAULT; | 1370 | err = -EFAULT; |
1371 | 1371 | ||
1372 | return err; | 1372 | return err; |
1373 | } | 1373 | } |
1374 | 1374 | ||
1375 | static int ethtool_get_module_info(struct net_device *dev, | 1375 | static int ethtool_get_module_info(struct net_device *dev, |
1376 | void __user *useraddr) | 1376 | void __user *useraddr) |
1377 | { | 1377 | { |
1378 | int ret; | 1378 | int ret; |
1379 | struct ethtool_modinfo modinfo; | 1379 | struct ethtool_modinfo modinfo; |
1380 | const struct ethtool_ops *ops = dev->ethtool_ops; | 1380 | const struct ethtool_ops *ops = dev->ethtool_ops; |
1381 | 1381 | ||
1382 | if (!ops->get_module_info) | 1382 | if (!ops->get_module_info) |
1383 | return -EOPNOTSUPP; | 1383 | return -EOPNOTSUPP; |
1384 | 1384 | ||
1385 | if (copy_from_user(&modinfo, useraddr, sizeof(modinfo))) | 1385 | if (copy_from_user(&modinfo, useraddr, sizeof(modinfo))) |
1386 | return -EFAULT; | 1386 | return -EFAULT; |
1387 | 1387 | ||
1388 | ret = ops->get_module_info(dev, &modinfo); | 1388 | ret = ops->get_module_info(dev, &modinfo); |
1389 | if (ret) | 1389 | if (ret) |
1390 | return ret; | 1390 | return ret; |
1391 | 1391 | ||
1392 | if (copy_to_user(useraddr, &modinfo, sizeof(modinfo))) | 1392 | if (copy_to_user(useraddr, &modinfo, sizeof(modinfo))) |
1393 | return -EFAULT; | 1393 | return -EFAULT; |
1394 | 1394 | ||
1395 | return 0; | 1395 | return 0; |
1396 | } | 1396 | } |
1397 | 1397 | ||
1398 | static int ethtool_get_module_eeprom(struct net_device *dev, | 1398 | static int ethtool_get_module_eeprom(struct net_device *dev, |
1399 | void __user *useraddr) | 1399 | void __user *useraddr) |
1400 | { | 1400 | { |
1401 | int ret; | 1401 | int ret; |
1402 | struct ethtool_modinfo modinfo; | 1402 | struct ethtool_modinfo modinfo; |
1403 | const struct ethtool_ops *ops = dev->ethtool_ops; | 1403 | const struct ethtool_ops *ops = dev->ethtool_ops; |
1404 | 1404 | ||
1405 | if (!ops->get_module_info || !ops->get_module_eeprom) | 1405 | if (!ops->get_module_info || !ops->get_module_eeprom) |
1406 | return -EOPNOTSUPP; | 1406 | return -EOPNOTSUPP; |
1407 | 1407 | ||
1408 | ret = ops->get_module_info(dev, &modinfo); | 1408 | ret = ops->get_module_info(dev, &modinfo); |
1409 | if (ret) | 1409 | if (ret) |
1410 | return ret; | 1410 | return ret; |
1411 | 1411 | ||
1412 | return ethtool_get_any_eeprom(dev, useraddr, ops->get_module_eeprom, | 1412 | return ethtool_get_any_eeprom(dev, useraddr, ops->get_module_eeprom, |
1413 | modinfo.eeprom_len); | 1413 | modinfo.eeprom_len); |
1414 | } | 1414 | } |
1415 | 1415 | ||
1416 | /* The main entry point in this file. Called from net/core/dev.c */ | 1416 | /* The main entry point in this file. Called from net/core/dev.c */ |
1417 | 1417 | ||
1418 | int dev_ethtool(struct net *net, struct ifreq *ifr) | 1418 | int dev_ethtool(struct net *net, struct ifreq *ifr) |
1419 | { | 1419 | { |
1420 | struct net_device *dev = __dev_get_by_name(net, ifr->ifr_name); | 1420 | struct net_device *dev = __dev_get_by_name(net, ifr->ifr_name); |
1421 | void __user *useraddr = ifr->ifr_data; | 1421 | void __user *useraddr = ifr->ifr_data; |
1422 | u32 ethcmd; | 1422 | u32 ethcmd; |
1423 | int rc; | 1423 | int rc; |
1424 | u32 old_features; | 1424 | netdev_features_t old_features; |
1425 | 1425 | ||
1426 | if (!dev || !netif_device_present(dev)) | 1426 | if (!dev || !netif_device_present(dev)) |
1427 | return -ENODEV; | 1427 | return -ENODEV; |
1428 | 1428 | ||
1429 | if (copy_from_user(ðcmd, useraddr, sizeof(ethcmd))) | 1429 | if (copy_from_user(ðcmd, useraddr, sizeof(ethcmd))) |
1430 | return -EFAULT; | 1430 | return -EFAULT; |
1431 | 1431 | ||
1432 | /* Allow some commands to be done by anyone */ | 1432 | /* Allow some commands to be done by anyone */ |
1433 | switch (ethcmd) { | 1433 | switch (ethcmd) { |
1434 | case ETHTOOL_GSET: | 1434 | case ETHTOOL_GSET: |
1435 | case ETHTOOL_GDRVINFO: | 1435 | case ETHTOOL_GDRVINFO: |
1436 | case ETHTOOL_GMSGLVL: | 1436 | case ETHTOOL_GMSGLVL: |
1437 | case ETHTOOL_GLINK: | 1437 | case ETHTOOL_GLINK: |
1438 | case ETHTOOL_GCOALESCE: | 1438 | case ETHTOOL_GCOALESCE: |
1439 | case ETHTOOL_GRINGPARAM: | 1439 | case ETHTOOL_GRINGPARAM: |
1440 | case ETHTOOL_GPAUSEPARAM: | 1440 | case ETHTOOL_GPAUSEPARAM: |
1441 | case ETHTOOL_GRXCSUM: | 1441 | case ETHTOOL_GRXCSUM: |
1442 | case ETHTOOL_GTXCSUM: | 1442 | case ETHTOOL_GTXCSUM: |
1443 | case ETHTOOL_GSG: | 1443 | case ETHTOOL_GSG: |
1444 | case ETHTOOL_GSSET_INFO: | 1444 | case ETHTOOL_GSSET_INFO: |
1445 | case ETHTOOL_GSTRINGS: | 1445 | case ETHTOOL_GSTRINGS: |
1446 | case ETHTOOL_GSTATS: | 1446 | case ETHTOOL_GSTATS: |
1447 | case ETHTOOL_GTSO: | 1447 | case ETHTOOL_GTSO: |
1448 | case ETHTOOL_GPERMADDR: | 1448 | case ETHTOOL_GPERMADDR: |
1449 | case ETHTOOL_GUFO: | 1449 | case ETHTOOL_GUFO: |
1450 | case ETHTOOL_GGSO: | 1450 | case ETHTOOL_GGSO: |
1451 | case ETHTOOL_GGRO: | 1451 | case ETHTOOL_GGRO: |
1452 | case ETHTOOL_GFLAGS: | 1452 | case ETHTOOL_GFLAGS: |
1453 | case ETHTOOL_GPFLAGS: | 1453 | case ETHTOOL_GPFLAGS: |
1454 | case ETHTOOL_GRXFH: | 1454 | case ETHTOOL_GRXFH: |
1455 | case ETHTOOL_GRXRINGS: | 1455 | case ETHTOOL_GRXRINGS: |
1456 | case ETHTOOL_GRXCLSRLCNT: | 1456 | case ETHTOOL_GRXCLSRLCNT: |
1457 | case ETHTOOL_GRXCLSRULE: | 1457 | case ETHTOOL_GRXCLSRULE: |
1458 | case ETHTOOL_GRXCLSRLALL: | 1458 | case ETHTOOL_GRXCLSRLALL: |
1459 | case ETHTOOL_GRXFHINDIR: | 1459 | case ETHTOOL_GRXFHINDIR: |
1460 | case ETHTOOL_GFEATURES: | 1460 | case ETHTOOL_GFEATURES: |
1461 | case ETHTOOL_GCHANNELS: | 1461 | case ETHTOOL_GCHANNELS: |
1462 | case ETHTOOL_GET_TS_INFO: | 1462 | case ETHTOOL_GET_TS_INFO: |
1463 | case ETHTOOL_GEEE: | 1463 | case ETHTOOL_GEEE: |
1464 | break; | 1464 | break; |
1465 | default: | 1465 | default: |
1466 | if (!ns_capable(net->user_ns, CAP_NET_ADMIN)) | 1466 | if (!ns_capable(net->user_ns, CAP_NET_ADMIN)) |
1467 | return -EPERM; | 1467 | return -EPERM; |
1468 | } | 1468 | } |
1469 | 1469 | ||
1470 | if (dev->ethtool_ops->begin) { | 1470 | if (dev->ethtool_ops->begin) { |
1471 | rc = dev->ethtool_ops->begin(dev); | 1471 | rc = dev->ethtool_ops->begin(dev); |
1472 | if (rc < 0) | 1472 | if (rc < 0) |
1473 | return rc; | 1473 | return rc; |
1474 | } | 1474 | } |
1475 | old_features = dev->features; | 1475 | old_features = dev->features; |
1476 | 1476 | ||
1477 | switch (ethcmd) { | 1477 | switch (ethcmd) { |
1478 | case ETHTOOL_GSET: | 1478 | case ETHTOOL_GSET: |
1479 | rc = ethtool_get_settings(dev, useraddr); | 1479 | rc = ethtool_get_settings(dev, useraddr); |
1480 | break; | 1480 | break; |
1481 | case ETHTOOL_SSET: | 1481 | case ETHTOOL_SSET: |
1482 | rc = ethtool_set_settings(dev, useraddr); | 1482 | rc = ethtool_set_settings(dev, useraddr); |
1483 | break; | 1483 | break; |
1484 | case ETHTOOL_GDRVINFO: | 1484 | case ETHTOOL_GDRVINFO: |
1485 | rc = ethtool_get_drvinfo(dev, useraddr); | 1485 | rc = ethtool_get_drvinfo(dev, useraddr); |
1486 | break; | 1486 | break; |
1487 | case ETHTOOL_GREGS: | 1487 | case ETHTOOL_GREGS: |
1488 | rc = ethtool_get_regs(dev, useraddr); | 1488 | rc = ethtool_get_regs(dev, useraddr); |
1489 | break; | 1489 | break; |
1490 | case ETHTOOL_GWOL: | 1490 | case ETHTOOL_GWOL: |
1491 | rc = ethtool_get_wol(dev, useraddr); | 1491 | rc = ethtool_get_wol(dev, useraddr); |
1492 | break; | 1492 | break; |
1493 | case ETHTOOL_SWOL: | 1493 | case ETHTOOL_SWOL: |
1494 | rc = ethtool_set_wol(dev, useraddr); | 1494 | rc = ethtool_set_wol(dev, useraddr); |
1495 | break; | 1495 | break; |
1496 | case ETHTOOL_GMSGLVL: | 1496 | case ETHTOOL_GMSGLVL: |
1497 | rc = ethtool_get_value(dev, useraddr, ethcmd, | 1497 | rc = ethtool_get_value(dev, useraddr, ethcmd, |
1498 | dev->ethtool_ops->get_msglevel); | 1498 | dev->ethtool_ops->get_msglevel); |
1499 | break; | 1499 | break; |
1500 | case ETHTOOL_SMSGLVL: | 1500 | case ETHTOOL_SMSGLVL: |
1501 | rc = ethtool_set_value_void(dev, useraddr, | 1501 | rc = ethtool_set_value_void(dev, useraddr, |
1502 | dev->ethtool_ops->set_msglevel); | 1502 | dev->ethtool_ops->set_msglevel); |
1503 | break; | 1503 | break; |
1504 | case ETHTOOL_GEEE: | 1504 | case ETHTOOL_GEEE: |
1505 | rc = ethtool_get_eee(dev, useraddr); | 1505 | rc = ethtool_get_eee(dev, useraddr); |
1506 | break; | 1506 | break; |
1507 | case ETHTOOL_SEEE: | 1507 | case ETHTOOL_SEEE: |
1508 | rc = ethtool_set_eee(dev, useraddr); | 1508 | rc = ethtool_set_eee(dev, useraddr); |
1509 | break; | 1509 | break; |
1510 | case ETHTOOL_NWAY_RST: | 1510 | case ETHTOOL_NWAY_RST: |
1511 | rc = ethtool_nway_reset(dev); | 1511 | rc = ethtool_nway_reset(dev); |
1512 | break; | 1512 | break; |
1513 | case ETHTOOL_GLINK: | 1513 | case ETHTOOL_GLINK: |
1514 | rc = ethtool_get_link(dev, useraddr); | 1514 | rc = ethtool_get_link(dev, useraddr); |
1515 | break; | 1515 | break; |
1516 | case ETHTOOL_GEEPROM: | 1516 | case ETHTOOL_GEEPROM: |
1517 | rc = ethtool_get_eeprom(dev, useraddr); | 1517 | rc = ethtool_get_eeprom(dev, useraddr); |
1518 | break; | 1518 | break; |
1519 | case ETHTOOL_SEEPROM: | 1519 | case ETHTOOL_SEEPROM: |
1520 | rc = ethtool_set_eeprom(dev, useraddr); | 1520 | rc = ethtool_set_eeprom(dev, useraddr); |
1521 | break; | 1521 | break; |
1522 | case ETHTOOL_GCOALESCE: | 1522 | case ETHTOOL_GCOALESCE: |
1523 | rc = ethtool_get_coalesce(dev, useraddr); | 1523 | rc = ethtool_get_coalesce(dev, useraddr); |
1524 | break; | 1524 | break; |
1525 | case ETHTOOL_SCOALESCE: | 1525 | case ETHTOOL_SCOALESCE: |
1526 | rc = ethtool_set_coalesce(dev, useraddr); | 1526 | rc = ethtool_set_coalesce(dev, useraddr); |
1527 | break; | 1527 | break; |
1528 | case ETHTOOL_GRINGPARAM: | 1528 | case ETHTOOL_GRINGPARAM: |
1529 | rc = ethtool_get_ringparam(dev, useraddr); | 1529 | rc = ethtool_get_ringparam(dev, useraddr); |
1530 | break; | 1530 | break; |
1531 | case ETHTOOL_SRINGPARAM: | 1531 | case ETHTOOL_SRINGPARAM: |
1532 | rc = ethtool_set_ringparam(dev, useraddr); | 1532 | rc = ethtool_set_ringparam(dev, useraddr); |
1533 | break; | 1533 | break; |
1534 | case ETHTOOL_GPAUSEPARAM: | 1534 | case ETHTOOL_GPAUSEPARAM: |
1535 | rc = ethtool_get_pauseparam(dev, useraddr); | 1535 | rc = ethtool_get_pauseparam(dev, useraddr); |
1536 | break; | 1536 | break; |
1537 | case ETHTOOL_SPAUSEPARAM: | 1537 | case ETHTOOL_SPAUSEPARAM: |
1538 | rc = ethtool_set_pauseparam(dev, useraddr); | 1538 | rc = ethtool_set_pauseparam(dev, useraddr); |
1539 | break; | 1539 | break; |
1540 | case ETHTOOL_TEST: | 1540 | case ETHTOOL_TEST: |
1541 | rc = ethtool_self_test(dev, useraddr); | 1541 | rc = ethtool_self_test(dev, useraddr); |
1542 | break; | 1542 | break; |
1543 | case ETHTOOL_GSTRINGS: | 1543 | case ETHTOOL_GSTRINGS: |
1544 | rc = ethtool_get_strings(dev, useraddr); | 1544 | rc = ethtool_get_strings(dev, useraddr); |
1545 | break; | 1545 | break; |
1546 | case ETHTOOL_PHYS_ID: | 1546 | case ETHTOOL_PHYS_ID: |
1547 | rc = ethtool_phys_id(dev, useraddr); | 1547 | rc = ethtool_phys_id(dev, useraddr); |
1548 | break; | 1548 | break; |
1549 | case ETHTOOL_GSTATS: | 1549 | case ETHTOOL_GSTATS: |
1550 | rc = ethtool_get_stats(dev, useraddr); | 1550 | rc = ethtool_get_stats(dev, useraddr); |
1551 | break; | 1551 | break; |
1552 | case ETHTOOL_GPERMADDR: | 1552 | case ETHTOOL_GPERMADDR: |
1553 | rc = ethtool_get_perm_addr(dev, useraddr); | 1553 | rc = ethtool_get_perm_addr(dev, useraddr); |
1554 | break; | 1554 | break; |
1555 | case ETHTOOL_GFLAGS: | 1555 | case ETHTOOL_GFLAGS: |
1556 | rc = ethtool_get_value(dev, useraddr, ethcmd, | 1556 | rc = ethtool_get_value(dev, useraddr, ethcmd, |
1557 | __ethtool_get_flags); | 1557 | __ethtool_get_flags); |
1558 | break; | 1558 | break; |
1559 | case ETHTOOL_SFLAGS: | 1559 | case ETHTOOL_SFLAGS: |
1560 | rc = ethtool_set_value(dev, useraddr, __ethtool_set_flags); | 1560 | rc = ethtool_set_value(dev, useraddr, __ethtool_set_flags); |
1561 | break; | 1561 | break; |
1562 | case ETHTOOL_GPFLAGS: | 1562 | case ETHTOOL_GPFLAGS: |
1563 | rc = ethtool_get_value(dev, useraddr, ethcmd, | 1563 | rc = ethtool_get_value(dev, useraddr, ethcmd, |
1564 | dev->ethtool_ops->get_priv_flags); | 1564 | dev->ethtool_ops->get_priv_flags); |
1565 | break; | 1565 | break; |
1566 | case ETHTOOL_SPFLAGS: | 1566 | case ETHTOOL_SPFLAGS: |
1567 | rc = ethtool_set_value(dev, useraddr, | 1567 | rc = ethtool_set_value(dev, useraddr, |
1568 | dev->ethtool_ops->set_priv_flags); | 1568 | dev->ethtool_ops->set_priv_flags); |
1569 | break; | 1569 | break; |
1570 | case ETHTOOL_GRXFH: | 1570 | case ETHTOOL_GRXFH: |
1571 | case ETHTOOL_GRXRINGS: | 1571 | case ETHTOOL_GRXRINGS: |
1572 | case ETHTOOL_GRXCLSRLCNT: | 1572 | case ETHTOOL_GRXCLSRLCNT: |
1573 | case ETHTOOL_GRXCLSRULE: | 1573 | case ETHTOOL_GRXCLSRULE: |
1574 | case ETHTOOL_GRXCLSRLALL: | 1574 | case ETHTOOL_GRXCLSRLALL: |
1575 | rc = ethtool_get_rxnfc(dev, ethcmd, useraddr); | 1575 | rc = ethtool_get_rxnfc(dev, ethcmd, useraddr); |
1576 | break; | 1576 | break; |
1577 | case ETHTOOL_SRXFH: | 1577 | case ETHTOOL_SRXFH: |
1578 | case ETHTOOL_SRXCLSRLDEL: | 1578 | case ETHTOOL_SRXCLSRLDEL: |
1579 | case ETHTOOL_SRXCLSRLINS: | 1579 | case ETHTOOL_SRXCLSRLINS: |
1580 | rc = ethtool_set_rxnfc(dev, ethcmd, useraddr); | 1580 | rc = ethtool_set_rxnfc(dev, ethcmd, useraddr); |
1581 | break; | 1581 | break; |
1582 | case ETHTOOL_FLASHDEV: | 1582 | case ETHTOOL_FLASHDEV: |
1583 | rc = ethtool_flash_device(dev, useraddr); | 1583 | rc = ethtool_flash_device(dev, useraddr); |
1584 | break; | 1584 | break; |
1585 | case ETHTOOL_RESET: | 1585 | case ETHTOOL_RESET: |
1586 | rc = ethtool_reset(dev, useraddr); | 1586 | rc = ethtool_reset(dev, useraddr); |
1587 | break; | 1587 | break; |
1588 | case ETHTOOL_GSSET_INFO: | 1588 | case ETHTOOL_GSSET_INFO: |
1589 | rc = ethtool_get_sset_info(dev, useraddr); | 1589 | rc = ethtool_get_sset_info(dev, useraddr); |
1590 | break; | 1590 | break; |
1591 | case ETHTOOL_GRXFHINDIR: | 1591 | case ETHTOOL_GRXFHINDIR: |
1592 | rc = ethtool_get_rxfh_indir(dev, useraddr); | 1592 | rc = ethtool_get_rxfh_indir(dev, useraddr); |
1593 | break; | 1593 | break; |
1594 | case ETHTOOL_SRXFHINDIR: | 1594 | case ETHTOOL_SRXFHINDIR: |
1595 | rc = ethtool_set_rxfh_indir(dev, useraddr); | 1595 | rc = ethtool_set_rxfh_indir(dev, useraddr); |
1596 | break; | 1596 | break; |
1597 | case ETHTOOL_GFEATURES: | 1597 | case ETHTOOL_GFEATURES: |
1598 | rc = ethtool_get_features(dev, useraddr); | 1598 | rc = ethtool_get_features(dev, useraddr); |
1599 | break; | 1599 | break; |
1600 | case ETHTOOL_SFEATURES: | 1600 | case ETHTOOL_SFEATURES: |
1601 | rc = ethtool_set_features(dev, useraddr); | 1601 | rc = ethtool_set_features(dev, useraddr); |
1602 | break; | 1602 | break; |
1603 | case ETHTOOL_GTXCSUM: | 1603 | case ETHTOOL_GTXCSUM: |
1604 | case ETHTOOL_GRXCSUM: | 1604 | case ETHTOOL_GRXCSUM: |
1605 | case ETHTOOL_GSG: | 1605 | case ETHTOOL_GSG: |
1606 | case ETHTOOL_GTSO: | 1606 | case ETHTOOL_GTSO: |
1607 | case ETHTOOL_GUFO: | 1607 | case ETHTOOL_GUFO: |
1608 | case ETHTOOL_GGSO: | 1608 | case ETHTOOL_GGSO: |
1609 | case ETHTOOL_GGRO: | 1609 | case ETHTOOL_GGRO: |
1610 | rc = ethtool_get_one_feature(dev, useraddr, ethcmd); | 1610 | rc = ethtool_get_one_feature(dev, useraddr, ethcmd); |
1611 | break; | 1611 | break; |
1612 | case ETHTOOL_STXCSUM: | 1612 | case ETHTOOL_STXCSUM: |
1613 | case ETHTOOL_SRXCSUM: | 1613 | case ETHTOOL_SRXCSUM: |
1614 | case ETHTOOL_SSG: | 1614 | case ETHTOOL_SSG: |
1615 | case ETHTOOL_STSO: | 1615 | case ETHTOOL_STSO: |
1616 | case ETHTOOL_SUFO: | 1616 | case ETHTOOL_SUFO: |
1617 | case ETHTOOL_SGSO: | 1617 | case ETHTOOL_SGSO: |
1618 | case ETHTOOL_SGRO: | 1618 | case ETHTOOL_SGRO: |
1619 | rc = ethtool_set_one_feature(dev, useraddr, ethcmd); | 1619 | rc = ethtool_set_one_feature(dev, useraddr, ethcmd); |
1620 | break; | 1620 | break; |
1621 | case ETHTOOL_GCHANNELS: | 1621 | case ETHTOOL_GCHANNELS: |
1622 | rc = ethtool_get_channels(dev, useraddr); | 1622 | rc = ethtool_get_channels(dev, useraddr); |
1623 | break; | 1623 | break; |
1624 | case ETHTOOL_SCHANNELS: | 1624 | case ETHTOOL_SCHANNELS: |
1625 | rc = ethtool_set_channels(dev, useraddr); | 1625 | rc = ethtool_set_channels(dev, useraddr); |
1626 | break; | 1626 | break; |
1627 | case ETHTOOL_SET_DUMP: | 1627 | case ETHTOOL_SET_DUMP: |
1628 | rc = ethtool_set_dump(dev, useraddr); | 1628 | rc = ethtool_set_dump(dev, useraddr); |
1629 | break; | 1629 | break; |
1630 | case ETHTOOL_GET_DUMP_FLAG: | 1630 | case ETHTOOL_GET_DUMP_FLAG: |
1631 | rc = ethtool_get_dump_flag(dev, useraddr); | 1631 | rc = ethtool_get_dump_flag(dev, useraddr); |
1632 | break; | 1632 | break; |
1633 | case ETHTOOL_GET_DUMP_DATA: | 1633 | case ETHTOOL_GET_DUMP_DATA: |
1634 | rc = ethtool_get_dump_data(dev, useraddr); | 1634 | rc = ethtool_get_dump_data(dev, useraddr); |
1635 | break; | 1635 | break; |
1636 | case ETHTOOL_GET_TS_INFO: | 1636 | case ETHTOOL_GET_TS_INFO: |
1637 | rc = ethtool_get_ts_info(dev, useraddr); | 1637 | rc = ethtool_get_ts_info(dev, useraddr); |
1638 | break; | 1638 | break; |
1639 | case ETHTOOL_GMODULEINFO: | 1639 | case ETHTOOL_GMODULEINFO: |
1640 | rc = ethtool_get_module_info(dev, useraddr); | 1640 | rc = ethtool_get_module_info(dev, useraddr); |
1641 | break; | 1641 | break; |
1642 | case ETHTOOL_GMODULEEEPROM: | 1642 | case ETHTOOL_GMODULEEEPROM: |
1643 | rc = ethtool_get_module_eeprom(dev, useraddr); | 1643 | rc = ethtool_get_module_eeprom(dev, useraddr); |
1644 | break; | 1644 | break; |
1645 | default: | 1645 | default: |
1646 | rc = -EOPNOTSUPP; | 1646 | rc = -EOPNOTSUPP; |
1647 | } | 1647 | } |
1648 | 1648 | ||
1649 | if (dev->ethtool_ops->complete) | 1649 | if (dev->ethtool_ops->complete) |
1650 | dev->ethtool_ops->complete(dev); | 1650 | dev->ethtool_ops->complete(dev); |
1651 | 1651 | ||
1652 | if (old_features != dev->features) | 1652 | if (old_features != dev->features) |
1653 | netdev_features_change(dev); | 1653 | netdev_features_change(dev); |
1654 | 1654 | ||
1655 | return rc; | 1655 | return rc; |
1656 | } | 1656 | } |
1657 | 1657 |