X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=net%2Fcore%2Fethtool.c;h=87dc556fd9d6c694f65e33c6868aebdeef8325cc;hb=97bf2856c6014879bd04983a3e9dfcdac1e7fe85;hp=9925b81609d4a1d732d01e9c1b84c5217b765724;hpb=5273a3df6485dc2ad6aa7ddd441b9a21970f003b;p=linux-2.6.git diff --git a/net/core/ethtool.c b/net/core/ethtool.c index 9925b8160..87dc556fd 100644 --- a/net/core/ethtool.c +++ b/net/core/ethtool.c @@ -11,6 +11,7 @@ #include #include +#include #include #include #include @@ -29,7 +30,7 @@ u32 ethtool_op_get_link(struct net_device *dev) u32 ethtool_op_get_tx_csum(struct net_device *dev) { - return (dev->features & NETIF_F_IP_CSUM) != 0; + return (dev->features & NETIF_F_ALL_CSUM) != 0; } int ethtool_op_set_tx_csum(struct net_device *dev, u32 data) @@ -42,6 +43,15 @@ int ethtool_op_set_tx_csum(struct net_device *dev, u32 data) return 0; } +int ethtool_op_set_tx_hw_csum(struct net_device *dev, u32 data) +{ + if (data) + dev->features |= NETIF_F_HW_CSUM; + else + dev->features &= ~NETIF_F_HW_CSUM; + + return 0; +} u32 ethtool_op_get_sg(struct net_device *dev) { return (dev->features & NETIF_F_SG) != 0; @@ -72,6 +82,32 @@ int ethtool_op_set_tso(struct net_device *dev, u32 data) return 0; } +int ethtool_op_get_perm_addr(struct net_device *dev, struct ethtool_perm_addr *addr, u8 *data) +{ + unsigned char len = dev->addr_len; + if ( addr->size < len ) + return -ETOOSMALL; + + addr->size = len; + memcpy(data, dev->perm_addr, len); + return 0; +} + + +u32 ethtool_op_get_ufo(struct net_device *dev) +{ + return (dev->features & NETIF_F_UFO) != 0; +} + +int ethtool_op_set_ufo(struct net_device *dev, u32 data) +{ + if (data) + dev->features |= NETIF_F_UFO; + else + dev->features &= ~NETIF_F_UFO; + return 0; +} + /* Handlers for each ethtool command */ static int ethtool_get_settings(struct net_device *dev, void __user *useraddr) @@ -107,7 +143,7 @@ static int ethtool_set_settings(struct net_device *dev, void __user *useraddr) static int ethtool_get_drvinfo(struct net_device *dev, void __user *useraddr) { struct ethtool_drvinfo info; - struct ethtool_ops *ops = dev->ethtool_ops; + const struct ethtool_ops *ops = dev->ethtool_ops; if (!ops->get_drvinfo) return -EOPNOTSUPP; @@ -133,7 +169,7 @@ static int ethtool_get_drvinfo(struct net_device *dev, void __user *useraddr) static int ethtool_get_regs(struct net_device *dev, char __user *useraddr) { struct ethtool_regs regs; - struct ethtool_ops *ops = dev->ethtool_ops; + const struct ethtool_ops *ops = dev->ethtool_ops; void *regbuf; int reglen, ret; @@ -157,7 +193,7 @@ static int ethtool_get_regs(struct net_device *dev, char __user *useraddr) if (copy_to_user(useraddr, ®s, sizeof(regs))) goto out; useraddr += offsetof(struct ethtool_regs, data); - if (copy_to_user(useraddr, regbuf, reglen)) + if (copy_to_user(useraddr, regbuf, regs.len)) goto out; ret = 0; @@ -246,7 +282,7 @@ static int ethtool_get_link(struct net_device *dev, void __user *useraddr) static int ethtool_get_eeprom(struct net_device *dev, void __user *useraddr) { struct ethtool_eeprom eeprom; - struct ethtool_ops *ops = dev->ethtool_ops; + const struct ethtool_ops *ops = dev->ethtool_ops; u8 *data; int ret; @@ -291,7 +327,7 @@ static int ethtool_get_eeprom(struct net_device *dev, void __user *useraddr) static int ethtool_set_eeprom(struct net_device *dev, void __user *useraddr) { struct ethtool_eeprom eeprom; - struct ethtool_ops *ops = dev->ethtool_ops; + const struct ethtool_ops *ops = dev->ethtool_ops; u8 *data; int ret; @@ -347,7 +383,7 @@ static int ethtool_set_coalesce(struct net_device *dev, void __user *useraddr) { struct ethtool_coalesce coalesce; - if (!dev->ethtool_ops->get_coalesce) + if (!dev->ethtool_ops->set_coalesce) return -EOPNOTSUPP; if (copy_from_user(&coalesce, useraddr, sizeof(coalesce))) @@ -401,7 +437,7 @@ static int ethtool_set_pauseparam(struct net_device *dev, void __user *useraddr) { struct ethtool_pauseparam pauseparam; - if (!dev->ethtool_ops->get_pauseparam) + if (!dev->ethtool_ops->set_pauseparam) return -EOPNOTSUPP; if (copy_from_user(&pauseparam, useraddr, sizeof(pauseparam))) @@ -452,9 +488,28 @@ static int ethtool_get_tx_csum(struct net_device *dev, char __user *useraddr) return 0; } +static int __ethtool_set_sg(struct net_device *dev, u32 data) +{ + int err; + + if (!data && dev->ethtool_ops->set_tso) { + err = dev->ethtool_ops->set_tso(dev, 0); + if (err) + return err; + } + + if (!data && dev->ethtool_ops->set_ufo) { + err = dev->ethtool_ops->set_ufo(dev, 0); + if (err) + return err; + } + return dev->ethtool_ops->set_sg(dev, data); +} + static int ethtool_set_tx_csum(struct net_device *dev, char __user *useraddr) { struct ethtool_value edata; + int err; if (!dev->ethtool_ops->set_tx_csum) return -EOPNOTSUPP; @@ -462,6 +517,12 @@ static int ethtool_set_tx_csum(struct net_device *dev, char __user *useraddr) if (copy_from_user(&edata, useraddr, sizeof(edata))) return -EFAULT; + if (!edata.data && dev->ethtool_ops->set_sg) { + err = __ethtool_set_sg(dev, 0); + if (err) + return err; + } + return dev->ethtool_ops->set_tx_csum(dev, edata.data); } @@ -489,7 +550,11 @@ static int ethtool_set_sg(struct net_device *dev, char __user *useraddr) if (copy_from_user(&edata, useraddr, sizeof(edata))) return -EFAULT; - return dev->ethtool_ops->set_sg(dev, edata.data); + if (edata.data && + !(dev->features & NETIF_F_ALL_CSUM)) + return -EINVAL; + + return __ethtool_set_sg(dev, edata.data); } static int ethtool_get_tso(struct net_device *dev, char __user *useraddr) @@ -516,13 +581,66 @@ static int ethtool_set_tso(struct net_device *dev, char __user *useraddr) if (copy_from_user(&edata, useraddr, sizeof(edata))) return -EFAULT; + if (edata.data && !(dev->features & NETIF_F_SG)) + return -EINVAL; + return dev->ethtool_ops->set_tso(dev, edata.data); } +static int ethtool_get_ufo(struct net_device *dev, char __user *useraddr) +{ + struct ethtool_value edata = { ETHTOOL_GUFO }; + + if (!dev->ethtool_ops->get_ufo) + return -EOPNOTSUPP; + edata.data = dev->ethtool_ops->get_ufo(dev); + if (copy_to_user(useraddr, &edata, sizeof(edata))) + return -EFAULT; + return 0; +} + +static int ethtool_set_ufo(struct net_device *dev, char __user *useraddr) +{ + struct ethtool_value edata; + + if (!dev->ethtool_ops->set_ufo) + return -EOPNOTSUPP; + if (copy_from_user(&edata, useraddr, sizeof(edata))) + return -EFAULT; + if (edata.data && !(dev->features & NETIF_F_SG)) + return -EINVAL; + if (edata.data && !(dev->features & NETIF_F_HW_CSUM)) + return -EINVAL; + return dev->ethtool_ops->set_ufo(dev, edata.data); +} + +static int ethtool_get_gso(struct net_device *dev, char __user *useraddr) +{ + struct ethtool_value edata = { ETHTOOL_GGSO }; + + edata.data = dev->features & NETIF_F_GSO; + if (copy_to_user(useraddr, &edata, sizeof(edata))) + return -EFAULT; + return 0; +} + +static int ethtool_set_gso(struct net_device *dev, char __user *useraddr) +{ + struct ethtool_value edata; + + if (copy_from_user(&edata, useraddr, sizeof(edata))) + return -EFAULT; + if (edata.data) + dev->features |= NETIF_F_GSO; + else + dev->features &= ~NETIF_F_GSO; + return 0; +} + static int ethtool_self_test(struct net_device *dev, char __user *useraddr) { struct ethtool_test test; - struct ethtool_ops *ops = dev->ethtool_ops; + const struct ethtool_ops *ops = dev->ethtool_ops; u64 *data; int ret; @@ -555,7 +673,7 @@ static int ethtool_self_test(struct net_device *dev, char __user *useraddr) static int ethtool_get_strings(struct net_device *dev, void __user *useraddr) { struct ethtool_gstrings gstrings; - struct ethtool_ops *ops = dev->ethtool_ops; + const struct ethtool_ops *ops = dev->ethtool_ops; u8 *data; int ret; @@ -615,7 +733,7 @@ static int ethtool_phys_id(struct net_device *dev, void __user *useraddr) static int ethtool_get_stats(struct net_device *dev, void __user *useraddr) { struct ethtool_stats stats; - struct ethtool_ops *ops = dev->ethtool_ops; + const struct ethtool_ops *ops = dev->ethtool_ops; u64 *data; int ret; @@ -645,20 +763,48 @@ static int ethtool_get_stats(struct net_device *dev, void __user *useraddr) return ret; } +static int ethtool_get_perm_addr(struct net_device *dev, void __user *useraddr) +{ + struct ethtool_perm_addr epaddr; + u8 *data; + int ret; + + if (!dev->ethtool_ops->get_perm_addr) + return -EOPNOTSUPP; + + if (copy_from_user(&epaddr,useraddr,sizeof(epaddr))) + return -EFAULT; + + data = kmalloc(epaddr.size, GFP_USER); + if (!data) + return -ENOMEM; + + ret = dev->ethtool_ops->get_perm_addr(dev,&epaddr,data); + if (ret) + return ret; + + ret = -EFAULT; + if (copy_to_user(useraddr, &epaddr, sizeof(epaddr))) + goto out; + useraddr += sizeof(epaddr); + if (copy_to_user(useraddr, data, epaddr.size)) + goto out; + ret = 0; + + out: + kfree(data); + return ret; +} + /* The main entry point in this file. Called from net/core/dev.c */ int dev_ethtool(struct ifreq *ifr) { struct net_device *dev = __dev_get_by_name(ifr->ifr_name); - void __user *useraddr = (void __user *) ifr->ifr_data; + void __user *useraddr = ifr->ifr_data; u32 ethcmd; - - /* - * XXX: This can be pushed down into the ethtool_* handlers that - * need it. Keep existing behaviour for the moment. - */ - if (!capable(CAP_NET_ADMIN)) - return -EPERM; + int rc; + unsigned long old_features; if (!dev || !netif_device_present(dev)) return -ENODEV; @@ -669,72 +815,156 @@ int dev_ethtool(struct ifreq *ifr) if (copy_from_user(ðcmd, useraddr, sizeof (ethcmd))) return -EFAULT; + /* Allow some commands to be done by anyone */ + switch(ethcmd) { + case ETHTOOL_GDRVINFO: + case ETHTOOL_GMSGLVL: + case ETHTOOL_GCOALESCE: + case ETHTOOL_GRINGPARAM: + case ETHTOOL_GPAUSEPARAM: + case ETHTOOL_GRXCSUM: + case ETHTOOL_GTXCSUM: + case ETHTOOL_GSG: + case ETHTOOL_GSTRINGS: + case ETHTOOL_GTSO: + case ETHTOOL_GPERMADDR: + case ETHTOOL_GUFO: + case ETHTOOL_GGSO: + break; + default: + if (!capable(CAP_NET_ADMIN)) + return -EPERM; + } + + if(dev->ethtool_ops->begin) + if ((rc = dev->ethtool_ops->begin(dev)) < 0) + return rc; + + old_features = dev->features; + switch (ethcmd) { case ETHTOOL_GSET: - return ethtool_get_settings(dev, useraddr); + rc = ethtool_get_settings(dev, useraddr); + break; case ETHTOOL_SSET: - return ethtool_set_settings(dev, useraddr); + rc = ethtool_set_settings(dev, useraddr); + break; case ETHTOOL_GDRVINFO: - return ethtool_get_drvinfo(dev, useraddr); + rc = ethtool_get_drvinfo(dev, useraddr); + break; case ETHTOOL_GREGS: - return ethtool_get_regs(dev, useraddr); + rc = ethtool_get_regs(dev, useraddr); + break; case ETHTOOL_GWOL: - return ethtool_get_wol(dev, useraddr); + rc = ethtool_get_wol(dev, useraddr); + break; case ETHTOOL_SWOL: - return ethtool_set_wol(dev, useraddr); + rc = ethtool_set_wol(dev, useraddr); + break; case ETHTOOL_GMSGLVL: - return ethtool_get_msglevel(dev, useraddr); + rc = ethtool_get_msglevel(dev, useraddr); + break; case ETHTOOL_SMSGLVL: - return ethtool_set_msglevel(dev, useraddr); + rc = ethtool_set_msglevel(dev, useraddr); + break; case ETHTOOL_NWAY_RST: - return ethtool_nway_reset(dev); + rc = ethtool_nway_reset(dev); + break; case ETHTOOL_GLINK: - return ethtool_get_link(dev, useraddr); + rc = ethtool_get_link(dev, useraddr); + break; case ETHTOOL_GEEPROM: - return ethtool_get_eeprom(dev, useraddr); + rc = ethtool_get_eeprom(dev, useraddr); + break; case ETHTOOL_SEEPROM: - return ethtool_set_eeprom(dev, useraddr); + rc = ethtool_set_eeprom(dev, useraddr); + break; case ETHTOOL_GCOALESCE: - return ethtool_get_coalesce(dev, useraddr); + rc = ethtool_get_coalesce(dev, useraddr); + break; case ETHTOOL_SCOALESCE: - return ethtool_set_coalesce(dev, useraddr); + rc = ethtool_set_coalesce(dev, useraddr); + break; case ETHTOOL_GRINGPARAM: - return ethtool_get_ringparam(dev, useraddr); + rc = ethtool_get_ringparam(dev, useraddr); + break; case ETHTOOL_SRINGPARAM: - return ethtool_set_ringparam(dev, useraddr); + rc = ethtool_set_ringparam(dev, useraddr); + break; case ETHTOOL_GPAUSEPARAM: - return ethtool_get_pauseparam(dev, useraddr); + rc = ethtool_get_pauseparam(dev, useraddr); + break; case ETHTOOL_SPAUSEPARAM: - return ethtool_set_pauseparam(dev, useraddr); + rc = ethtool_set_pauseparam(dev, useraddr); + break; case ETHTOOL_GRXCSUM: - return ethtool_get_rx_csum(dev, useraddr); + rc = ethtool_get_rx_csum(dev, useraddr); + break; case ETHTOOL_SRXCSUM: - return ethtool_set_rx_csum(dev, useraddr); + rc = ethtool_set_rx_csum(dev, useraddr); + break; case ETHTOOL_GTXCSUM: - return ethtool_get_tx_csum(dev, useraddr); + rc = ethtool_get_tx_csum(dev, useraddr); + break; case ETHTOOL_STXCSUM: - return ethtool_set_tx_csum(dev, useraddr); + rc = ethtool_set_tx_csum(dev, useraddr); + break; case ETHTOOL_GSG: - return ethtool_get_sg(dev, useraddr); + rc = ethtool_get_sg(dev, useraddr); + break; case ETHTOOL_SSG: - return ethtool_set_sg(dev, useraddr); + rc = ethtool_set_sg(dev, useraddr); + break; case ETHTOOL_GTSO: - return ethtool_get_tso(dev, useraddr); + rc = ethtool_get_tso(dev, useraddr); + break; case ETHTOOL_STSO: - return ethtool_set_tso(dev, useraddr); + rc = ethtool_set_tso(dev, useraddr); + break; case ETHTOOL_TEST: - return ethtool_self_test(dev, useraddr); + rc = ethtool_self_test(dev, useraddr); + break; case ETHTOOL_GSTRINGS: - return ethtool_get_strings(dev, useraddr); + rc = ethtool_get_strings(dev, useraddr); + break; case ETHTOOL_PHYS_ID: - return ethtool_phys_id(dev, useraddr); + rc = ethtool_phys_id(dev, useraddr); + break; case ETHTOOL_GSTATS: - return ethtool_get_stats(dev, useraddr); + rc = ethtool_get_stats(dev, useraddr); + break; + case ETHTOOL_GPERMADDR: + rc = ethtool_get_perm_addr(dev, useraddr); + break; + case ETHTOOL_GUFO: + rc = ethtool_get_ufo(dev, useraddr); + break; + case ETHTOOL_SUFO: + rc = ethtool_set_ufo(dev, useraddr); + break; + case ETHTOOL_GGSO: + rc = ethtool_get_gso(dev, useraddr); + break; + case ETHTOOL_SGSO: + rc = ethtool_set_gso(dev, useraddr); + break; default: - return -EOPNOTSUPP; + rc = -EOPNOTSUPP; } + + if(dev->ethtool_ops->complete) + dev->ethtool_ops->complete(dev); + + if (old_features != dev->features) + netdev_features_change(dev); + + return rc; ioctl: + /* Keep existing behaviour for the moment. */ + if (!capable(CAP_NET_ADMIN)) + return -EPERM; + if (dev->do_ioctl) return dev->do_ioctl(dev, ifr, SIOCETHTOOL); return -EOPNOTSUPP; @@ -742,9 +972,13 @@ int dev_ethtool(struct ifreq *ifr) EXPORT_SYMBOL(dev_ethtool); EXPORT_SYMBOL(ethtool_op_get_link); +EXPORT_SYMBOL_GPL(ethtool_op_get_perm_addr); EXPORT_SYMBOL(ethtool_op_get_sg); EXPORT_SYMBOL(ethtool_op_get_tso); EXPORT_SYMBOL(ethtool_op_get_tx_csum); EXPORT_SYMBOL(ethtool_op_set_sg); EXPORT_SYMBOL(ethtool_op_set_tso); EXPORT_SYMBOL(ethtool_op_set_tx_csum); +EXPORT_SYMBOL(ethtool_op_set_tx_hw_csum); +EXPORT_SYMBOL(ethtool_op_set_ufo); +EXPORT_SYMBOL(ethtool_op_get_ufo);