diff options
author | Michał Mirosław <mirq-linux@rere.qmqm.pl> | 2011-02-15 11:59:17 -0500 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2011-02-17 17:16:33 -0500 |
commit | 0a417704777ed29d0e8c72b7274a328e61248e75 (patch) | |
tree | 2a9cd59d8a58faf0904d503e143547b8aacd63ef /net | |
parent | 340ae1654c0667e0cdd2a6d4dc16f7946e018881 (diff) |
ethtool: factorize get/set_one_feature
This allows to enable GRO even if RX csum is disabled. GRO will not
be used for packets without hardware checksum anyway.
Signed-off-by: Michał Mirosław <mirq-linux@rere.qmqm.pl>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net')
-rw-r--r-- | net/core/ethtool.c | 274 |
1 files changed, 132 insertions, 142 deletions
diff --git a/net/core/ethtool.c b/net/core/ethtool.c index 85aaeab862a8..c3fb8f90de6d 100644 --- a/net/core/ethtool.c +++ b/net/core/ethtool.c | |||
@@ -191,6 +191,109 @@ static void __ethtool_get_strings(struct net_device *dev, | |||
191 | ops->get_strings(dev, stringset, data); | 191 | ops->get_strings(dev, stringset, data); |
192 | } | 192 | } |
193 | 193 | ||
194 | static u32 ethtool_get_feature_mask(u32 eth_cmd) | ||
195 | { | ||
196 | /* feature masks of legacy discrete ethtool ops */ | ||
197 | |||
198 | switch (eth_cmd) { | ||
199 | case ETHTOOL_GTXCSUM: | ||
200 | case ETHTOOL_STXCSUM: | ||
201 | return NETIF_F_ALL_CSUM | NETIF_F_SCTP_CSUM; | ||
202 | case ETHTOOL_GSG: | ||
203 | case ETHTOOL_SSG: | ||
204 | return NETIF_F_SG; | ||
205 | case ETHTOOL_GTSO: | ||
206 | case ETHTOOL_STSO: | ||
207 | return NETIF_F_ALL_TSO; | ||
208 | case ETHTOOL_GUFO: | ||
209 | case ETHTOOL_SUFO: | ||
210 | return NETIF_F_UFO; | ||
211 | case ETHTOOL_GGSO: | ||
212 | case ETHTOOL_SGSO: | ||
213 | return NETIF_F_GSO; | ||
214 | case ETHTOOL_GGRO: | ||
215 | case ETHTOOL_SGRO: | ||
216 | return NETIF_F_GRO; | ||
217 | default: | ||
218 | BUG(); | ||
219 | } | ||
220 | } | ||
221 | |||
222 | static void *__ethtool_get_one_feature_actor(struct net_device *dev, u32 ethcmd) | ||
223 | { | ||
224 | const struct ethtool_ops *ops = dev->ethtool_ops; | ||
225 | |||
226 | if (!ops) | ||
227 | return NULL; | ||
228 | |||
229 | switch (ethcmd) { | ||
230 | case ETHTOOL_GTXCSUM: | ||
231 | return ops->get_tx_csum; | ||
232 | case ETHTOOL_SSG: | ||
233 | return ops->get_sg; | ||
234 | case ETHTOOL_STSO: | ||
235 | return ops->get_tso; | ||
236 | case ETHTOOL_SUFO: | ||
237 | return ops->get_ufo; | ||
238 | default: | ||
239 | return NULL; | ||
240 | } | ||
241 | } | ||
242 | |||
243 | static int ethtool_get_one_feature(struct net_device *dev, | ||
244 | char __user *useraddr, u32 ethcmd) | ||
245 | { | ||
246 | struct ethtool_value edata = { | ||
247 | .cmd = ethcmd, | ||
248 | .data = !!(dev->features & ethtool_get_feature_mask(ethcmd)), | ||
249 | }; | ||
250 | u32 (*actor)(struct net_device *); | ||
251 | |||
252 | actor = __ethtool_get_one_feature_actor(dev, ethcmd); | ||
253 | if (actor) | ||
254 | edata.data = actor(dev); | ||
255 | |||
256 | if (copy_to_user(useraddr, &edata, sizeof(edata))) | ||
257 | return -EFAULT; | ||
258 | return 0; | ||
259 | } | ||
260 | |||
261 | static int __ethtool_set_tx_csum(struct net_device *dev, u32 data); | ||
262 | static int __ethtool_set_sg(struct net_device *dev, u32 data); | ||
263 | static int __ethtool_set_tso(struct net_device *dev, u32 data); | ||
264 | static int __ethtool_set_ufo(struct net_device *dev, u32 data); | ||
265 | |||
266 | static int ethtool_set_one_feature(struct net_device *dev, | ||
267 | void __user *useraddr, u32 ethcmd) | ||
268 | { | ||
269 | struct ethtool_value edata; | ||
270 | u32 mask; | ||
271 | |||
272 | if (copy_from_user(&edata, useraddr, sizeof(edata))) | ||
273 | return -EFAULT; | ||
274 | |||
275 | switch (ethcmd) { | ||
276 | case ETHTOOL_STXCSUM: | ||
277 | return __ethtool_set_tx_csum(dev, edata.data); | ||
278 | case ETHTOOL_SSG: | ||
279 | return __ethtool_set_sg(dev, edata.data); | ||
280 | case ETHTOOL_STSO: | ||
281 | return __ethtool_set_tso(dev, edata.data); | ||
282 | case ETHTOOL_SUFO: | ||
283 | return __ethtool_set_ufo(dev, edata.data); | ||
284 | case ETHTOOL_SGSO: | ||
285 | case ETHTOOL_SGRO: | ||
286 | mask = ethtool_get_feature_mask(ethcmd); | ||
287 | if (edata.data) | ||
288 | dev->features |= mask; | ||
289 | else | ||
290 | dev->features &= ~mask; | ||
291 | return 0; | ||
292 | default: | ||
293 | return -EOPNOTSUPP; | ||
294 | } | ||
295 | } | ||
296 | |||
194 | static int ethtool_get_settings(struct net_device *dev, void __user *useraddr) | 297 | static int ethtool_get_settings(struct net_device *dev, void __user *useraddr) |
195 | { | 298 | { |
196 | struct ethtool_cmd cmd = { .cmd = ETHTOOL_GSET }; | 299 | struct ethtool_cmd cmd = { .cmd = ETHTOOL_GSET }; |
@@ -1107,6 +1210,9 @@ static int __ethtool_set_sg(struct net_device *dev, u32 data) | |||
1107 | { | 1210 | { |
1108 | int err; | 1211 | int err; |
1109 | 1212 | ||
1213 | if (data && !(dev->features & NETIF_F_ALL_CSUM)) | ||
1214 | return -EINVAL; | ||
1215 | |||
1110 | if (!data && dev->ethtool_ops->set_tso) { | 1216 | if (!data && dev->ethtool_ops->set_tso) { |
1111 | err = dev->ethtool_ops->set_tso(dev, 0); | 1217 | err = dev->ethtool_ops->set_tso(dev, 0); |
1112 | if (err) | 1218 | if (err) |
@@ -1121,24 +1227,20 @@ static int __ethtool_set_sg(struct net_device *dev, u32 data) | |||
1121 | return dev->ethtool_ops->set_sg(dev, data); | 1227 | return dev->ethtool_ops->set_sg(dev, data); |
1122 | } | 1228 | } |
1123 | 1229 | ||
1124 | static int ethtool_set_tx_csum(struct net_device *dev, char __user *useraddr) | 1230 | static int __ethtool_set_tx_csum(struct net_device *dev, u32 data) |
1125 | { | 1231 | { |
1126 | struct ethtool_value edata; | ||
1127 | int err; | 1232 | int err; |
1128 | 1233 | ||
1129 | if (!dev->ethtool_ops->set_tx_csum) | 1234 | if (!dev->ethtool_ops->set_tx_csum) |
1130 | return -EOPNOTSUPP; | 1235 | return -EOPNOTSUPP; |
1131 | 1236 | ||
1132 | if (copy_from_user(&edata, useraddr, sizeof(edata))) | 1237 | if (!data && dev->ethtool_ops->set_sg) { |
1133 | return -EFAULT; | ||
1134 | |||
1135 | if (!edata.data && dev->ethtool_ops->set_sg) { | ||
1136 | err = __ethtool_set_sg(dev, 0); | 1238 | err = __ethtool_set_sg(dev, 0); |
1137 | if (err) | 1239 | if (err) |
1138 | return err; | 1240 | return err; |
1139 | } | 1241 | } |
1140 | 1242 | ||
1141 | return dev->ethtool_ops->set_tx_csum(dev, edata.data); | 1243 | return dev->ethtool_ops->set_tx_csum(dev, data); |
1142 | } | 1244 | } |
1143 | 1245 | ||
1144 | static int ethtool_set_rx_csum(struct net_device *dev, char __user *useraddr) | 1246 | static int ethtool_set_rx_csum(struct net_device *dev, char __user *useraddr) |
@@ -1157,108 +1259,28 @@ static int ethtool_set_rx_csum(struct net_device *dev, char __user *useraddr) | |||
1157 | return dev->ethtool_ops->set_rx_csum(dev, edata.data); | 1259 | return dev->ethtool_ops->set_rx_csum(dev, edata.data); |
1158 | } | 1260 | } |
1159 | 1261 | ||
1160 | static int ethtool_set_sg(struct net_device *dev, char __user *useraddr) | 1262 | static int __ethtool_set_tso(struct net_device *dev, u32 data) |
1161 | { | ||
1162 | struct ethtool_value edata; | ||
1163 | |||
1164 | if (!dev->ethtool_ops->set_sg) | ||
1165 | return -EOPNOTSUPP; | ||
1166 | |||
1167 | if (copy_from_user(&edata, useraddr, sizeof(edata))) | ||
1168 | return -EFAULT; | ||
1169 | |||
1170 | if (edata.data && | ||
1171 | !(dev->features & NETIF_F_ALL_CSUM)) | ||
1172 | return -EINVAL; | ||
1173 | |||
1174 | return __ethtool_set_sg(dev, edata.data); | ||
1175 | } | ||
1176 | |||
1177 | static int ethtool_set_tso(struct net_device *dev, char __user *useraddr) | ||
1178 | { | 1263 | { |
1179 | struct ethtool_value edata; | ||
1180 | |||
1181 | if (!dev->ethtool_ops->set_tso) | 1264 | if (!dev->ethtool_ops->set_tso) |
1182 | return -EOPNOTSUPP; | 1265 | return -EOPNOTSUPP; |
1183 | 1266 | ||
1184 | if (copy_from_user(&edata, useraddr, sizeof(edata))) | 1267 | if (data && !(dev->features & NETIF_F_SG)) |
1185 | return -EFAULT; | ||
1186 | |||
1187 | if (edata.data && !(dev->features & NETIF_F_SG)) | ||
1188 | return -EINVAL; | 1268 | return -EINVAL; |
1189 | 1269 | ||
1190 | return dev->ethtool_ops->set_tso(dev, edata.data); | 1270 | return dev->ethtool_ops->set_tso(dev, data); |
1191 | } | 1271 | } |
1192 | 1272 | ||
1193 | static int ethtool_set_ufo(struct net_device *dev, char __user *useraddr) | 1273 | static int __ethtool_set_ufo(struct net_device *dev, u32 data) |
1194 | { | 1274 | { |
1195 | struct ethtool_value edata; | ||
1196 | |||
1197 | if (!dev->ethtool_ops->set_ufo) | 1275 | if (!dev->ethtool_ops->set_ufo) |
1198 | return -EOPNOTSUPP; | 1276 | return -EOPNOTSUPP; |
1199 | if (copy_from_user(&edata, useraddr, sizeof(edata))) | 1277 | if (data && !(dev->features & NETIF_F_SG)) |
1200 | return -EFAULT; | ||
1201 | if (edata.data && !(dev->features & NETIF_F_SG)) | ||
1202 | return -EINVAL; | 1278 | return -EINVAL; |
1203 | if (edata.data && !((dev->features & NETIF_F_GEN_CSUM) || | 1279 | if (data && !((dev->features & NETIF_F_GEN_CSUM) || |
1204 | (dev->features & (NETIF_F_IP_CSUM|NETIF_F_IPV6_CSUM)) | 1280 | (dev->features & (NETIF_F_IP_CSUM|NETIF_F_IPV6_CSUM)) |
1205 | == (NETIF_F_IP_CSUM|NETIF_F_IPV6_CSUM))) | 1281 | == (NETIF_F_IP_CSUM|NETIF_F_IPV6_CSUM))) |
1206 | return -EINVAL; | 1282 | return -EINVAL; |
1207 | return dev->ethtool_ops->set_ufo(dev, edata.data); | 1283 | return dev->ethtool_ops->set_ufo(dev, data); |
1208 | } | ||
1209 | |||
1210 | static int ethtool_get_gso(struct net_device *dev, char __user *useraddr) | ||
1211 | { | ||
1212 | struct ethtool_value edata = { ETHTOOL_GGSO }; | ||
1213 | |||
1214 | edata.data = dev->features & NETIF_F_GSO; | ||
1215 | if (copy_to_user(useraddr, &edata, sizeof(edata))) | ||
1216 | return -EFAULT; | ||
1217 | return 0; | ||
1218 | } | ||
1219 | |||
1220 | static int ethtool_set_gso(struct net_device *dev, char __user *useraddr) | ||
1221 | { | ||
1222 | struct ethtool_value edata; | ||
1223 | |||
1224 | if (copy_from_user(&edata, useraddr, sizeof(edata))) | ||
1225 | return -EFAULT; | ||
1226 | if (edata.data) | ||
1227 | dev->features |= NETIF_F_GSO; | ||
1228 | else | ||
1229 | dev->features &= ~NETIF_F_GSO; | ||
1230 | return 0; | ||
1231 | } | ||
1232 | |||
1233 | static int ethtool_get_gro(struct net_device *dev, char __user *useraddr) | ||
1234 | { | ||
1235 | struct ethtool_value edata = { ETHTOOL_GGRO }; | ||
1236 | |||
1237 | edata.data = dev->features & NETIF_F_GRO; | ||
1238 | if (copy_to_user(useraddr, &edata, sizeof(edata))) | ||
1239 | return -EFAULT; | ||
1240 | return 0; | ||
1241 | } | ||
1242 | |||
1243 | static int ethtool_set_gro(struct net_device *dev, char __user *useraddr) | ||
1244 | { | ||
1245 | struct ethtool_value edata; | ||
1246 | |||
1247 | if (copy_from_user(&edata, useraddr, sizeof(edata))) | ||
1248 | return -EFAULT; | ||
1249 | |||
1250 | if (edata.data) { | ||
1251 | u32 rxcsum = dev->ethtool_ops->get_rx_csum ? | ||
1252 | dev->ethtool_ops->get_rx_csum(dev) : | ||
1253 | ethtool_op_get_rx_csum(dev); | ||
1254 | |||
1255 | if (!rxcsum) | ||
1256 | return -EINVAL; | ||
1257 | dev->features |= NETIF_F_GRO; | ||
1258 | } else | ||
1259 | dev->features &= ~NETIF_F_GRO; | ||
1260 | |||
1261 | return 0; | ||
1262 | } | 1284 | } |
1263 | 1285 | ||
1264 | static int ethtool_self_test(struct net_device *dev, char __user *useraddr) | 1286 | static int ethtool_self_test(struct net_device *dev, char __user *useraddr) |
@@ -1590,33 +1612,6 @@ int dev_ethtool(struct net *net, struct ifreq *ifr) | |||
1590 | case ETHTOOL_SRXCSUM: | 1612 | case ETHTOOL_SRXCSUM: |
1591 | rc = ethtool_set_rx_csum(dev, useraddr); | 1613 | rc = ethtool_set_rx_csum(dev, useraddr); |
1592 | break; | 1614 | break; |
1593 | case ETHTOOL_GTXCSUM: | ||
1594 | rc = ethtool_get_value(dev, useraddr, ethcmd, | ||
1595 | (dev->ethtool_ops->get_tx_csum ? | ||
1596 | dev->ethtool_ops->get_tx_csum : | ||
1597 | ethtool_op_get_tx_csum)); | ||
1598 | break; | ||
1599 | case ETHTOOL_STXCSUM: | ||
1600 | rc = ethtool_set_tx_csum(dev, useraddr); | ||
1601 | break; | ||
1602 | case ETHTOOL_GSG: | ||
1603 | rc = ethtool_get_value(dev, useraddr, ethcmd, | ||
1604 | (dev->ethtool_ops->get_sg ? | ||
1605 | dev->ethtool_ops->get_sg : | ||
1606 | ethtool_op_get_sg)); | ||
1607 | break; | ||
1608 | case ETHTOOL_SSG: | ||
1609 | rc = ethtool_set_sg(dev, useraddr); | ||
1610 | break; | ||
1611 | case ETHTOOL_GTSO: | ||
1612 | rc = ethtool_get_value(dev, useraddr, ethcmd, | ||
1613 | (dev->ethtool_ops->get_tso ? | ||
1614 | dev->ethtool_ops->get_tso : | ||
1615 | ethtool_op_get_tso)); | ||
1616 | break; | ||
1617 | case ETHTOOL_STSO: | ||
1618 | rc = ethtool_set_tso(dev, useraddr); | ||
1619 | break; | ||
1620 | case ETHTOOL_TEST: | 1615 | case ETHTOOL_TEST: |
1621 | rc = ethtool_self_test(dev, useraddr); | 1616 | rc = ethtool_self_test(dev, useraddr); |
1622 | break; | 1617 | break; |
@@ -1632,21 +1627,6 @@ int dev_ethtool(struct net *net, struct ifreq *ifr) | |||
1632 | case ETHTOOL_GPERMADDR: | 1627 | case ETHTOOL_GPERMADDR: |
1633 | rc = ethtool_get_perm_addr(dev, useraddr); | 1628 | rc = ethtool_get_perm_addr(dev, useraddr); |
1634 | break; | 1629 | break; |
1635 | case ETHTOOL_GUFO: | ||
1636 | rc = ethtool_get_value(dev, useraddr, ethcmd, | ||
1637 | (dev->ethtool_ops->get_ufo ? | ||
1638 | dev->ethtool_ops->get_ufo : | ||
1639 | ethtool_op_get_ufo)); | ||
1640 | break; | ||
1641 | case ETHTOOL_SUFO: | ||
1642 | rc = ethtool_set_ufo(dev, useraddr); | ||
1643 | break; | ||
1644 | case ETHTOOL_GGSO: | ||
1645 | rc = ethtool_get_gso(dev, useraddr); | ||
1646 | break; | ||
1647 | case ETHTOOL_SGSO: | ||
1648 | rc = ethtool_set_gso(dev, useraddr); | ||
1649 | break; | ||
1650 | case ETHTOOL_GFLAGS: | 1630 | case ETHTOOL_GFLAGS: |
1651 | rc = ethtool_get_value(dev, useraddr, ethcmd, | 1631 | rc = ethtool_get_value(dev, useraddr, ethcmd, |
1652 | (dev->ethtool_ops->get_flags ? | 1632 | (dev->ethtool_ops->get_flags ? |
@@ -1677,12 +1657,6 @@ int dev_ethtool(struct net *net, struct ifreq *ifr) | |||
1677 | case ETHTOOL_SRXCLSRLINS: | 1657 | case ETHTOOL_SRXCLSRLINS: |
1678 | rc = ethtool_set_rxnfc(dev, ethcmd, useraddr); | 1658 | rc = ethtool_set_rxnfc(dev, ethcmd, useraddr); |
1679 | break; | 1659 | break; |
1680 | case ETHTOOL_GGRO: | ||
1681 | rc = ethtool_get_gro(dev, useraddr); | ||
1682 | break; | ||
1683 | case ETHTOOL_SGRO: | ||
1684 | rc = ethtool_set_gro(dev, useraddr); | ||
1685 | break; | ||
1686 | case ETHTOOL_FLASHDEV: | 1660 | case ETHTOOL_FLASHDEV: |
1687 | rc = ethtool_flash_device(dev, useraddr); | 1661 | rc = ethtool_flash_device(dev, useraddr); |
1688 | break; | 1662 | break; |
@@ -1704,6 +1678,22 @@ int dev_ethtool(struct net *net, struct ifreq *ifr) | |||
1704 | case ETHTOOL_SRXFHINDIR: | 1678 | case ETHTOOL_SRXFHINDIR: |
1705 | rc = ethtool_set_rxfh_indir(dev, useraddr); | 1679 | rc = ethtool_set_rxfh_indir(dev, useraddr); |
1706 | break; | 1680 | break; |
1681 | case ETHTOOL_GTXCSUM: | ||
1682 | case ETHTOOL_GSG: | ||
1683 | case ETHTOOL_GTSO: | ||
1684 | case ETHTOOL_GUFO: | ||
1685 | case ETHTOOL_GGSO: | ||
1686 | case ETHTOOL_GGRO: | ||
1687 | rc = ethtool_get_one_feature(dev, useraddr, ethcmd); | ||
1688 | break; | ||
1689 | case ETHTOOL_STXCSUM: | ||
1690 | case ETHTOOL_SSG: | ||
1691 | case ETHTOOL_STSO: | ||
1692 | case ETHTOOL_SUFO: | ||
1693 | case ETHTOOL_SGSO: | ||
1694 | case ETHTOOL_SGRO: | ||
1695 | rc = ethtool_set_one_feature(dev, useraddr, ethcmd); | ||
1696 | break; | ||
1707 | default: | 1697 | default: |
1708 | rc = -EOPNOTSUPP; | 1698 | rc = -EOPNOTSUPP; |
1709 | } | 1699 | } |