[ETHTOOL]: Add support for large eeproms
authorMandeep Singh Baines <msb@google.com>
Wed, 16 Apr 2008 02:24:17 +0000 (19:24 -0700)
committerDavid S. Miller <davem@davemloft.net>
Wed, 16 Apr 2008 02:29:17 +0000 (19:29 -0700)
Currently, it is not possible to read/write to an eeprom larger than
128k in size because the buffer used for temporarily storing the
eeprom contents is allocated using kmalloc. kmalloc can only allocate
a maximum of 128k depending on architecture.

Modified ethtool_get/set_eeprom to only allocate a page of memory and
then copy the eeprom a page at a time.

Updated original patch as per suggestions from Joe Perches.

Signed-off-by: Mandeep Singh Baines <msb@google.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
net/core/ethtool.c

index 1163eb2256d0560b1fb3b4a71db5df5d4a40a072..a29b43d0b450a5e1a265375bd4f77797f0b23d79 100644 (file)
@@ -284,8 +284,10 @@ static int ethtool_get_eeprom(struct net_device *dev, void __user *useraddr)
 {
        struct ethtool_eeprom eeprom;
        const struct ethtool_ops *ops = dev->ethtool_ops;
+       void __user *userbuf = useraddr + sizeof(eeprom);
+       u32 bytes_remaining;
        u8 *data;
-       int ret;
+       int ret = 0;
 
        if (!ops->get_eeprom || !ops->get_eeprom_len)
                return -EOPNOTSUPP;
@@ -301,26 +303,26 @@ static int ethtool_get_eeprom(struct net_device *dev, void __user *useraddr)
        if (eeprom.offset + eeprom.len > ops->get_eeprom_len(dev))
                return -EINVAL;
 
-       data = kmalloc(eeprom.len, GFP_USER);
+       data = kmalloc(PAGE_SIZE, GFP_USER);
        if (!data)
                return -ENOMEM;
 
-       ret = -EFAULT;
-       if (copy_from_user(data, useraddr + sizeof(eeprom), eeprom.len))
-               goto out;
-
-       ret = ops->get_eeprom(dev, &eeprom, data);
-       if (ret)
-               goto out;
+       bytes_remaining = eeprom.len;
+       while (bytes_remaining > 0) {
+               eeprom.len = min(bytes_remaining, (u32)PAGE_SIZE);
 
-       ret = -EFAULT;
-       if (copy_to_user(useraddr, &eeprom, sizeof(eeprom)))
-               goto out;
-       if (copy_to_user(useraddr + sizeof(eeprom), data, eeprom.len))
-               goto out;
-       ret = 0;
+               ret = ops->get_eeprom(dev, &eeprom, data);
+               if (ret)
+                       break;
+               if (copy_to_user(userbuf, data, eeprom.len)) {
+                       ret = -EFAULT;
+                       break;
+               }
+               userbuf += eeprom.len;
+               eeprom.offset += eeprom.len;
+               bytes_remaining -= eeprom.len;
+       }
 
- out:
        kfree(data);
        return ret;
 }
@@ -329,8 +331,10 @@ static int ethtool_set_eeprom(struct net_device *dev, void __user *useraddr)
 {
        struct ethtool_eeprom eeprom;
        const struct ethtool_ops *ops = dev->ethtool_ops;
+       void __user *userbuf = useraddr + sizeof(eeprom);
+       u32 bytes_remaining;
        u8 *data;
-       int ret;
+       int ret = 0;
 
        if (!ops->set_eeprom || !ops->get_eeprom_len)
                return -EOPNOTSUPP;
@@ -346,22 +350,26 @@ static int ethtool_set_eeprom(struct net_device *dev, void __user *useraddr)
        if (eeprom.offset + eeprom.len > ops->get_eeprom_len(dev))
                return -EINVAL;
 
-       data = kmalloc(eeprom.len, GFP_USER);
+       data = kmalloc(PAGE_SIZE, GFP_USER);
        if (!data)
                return -ENOMEM;
 
-       ret = -EFAULT;
-       if (copy_from_user(data, useraddr + sizeof(eeprom), eeprom.len))
-               goto out;
-
-       ret = ops->set_eeprom(dev, &eeprom, data);
-       if (ret)
-               goto out;
+       bytes_remaining = eeprom.len;
+       while (bytes_remaining > 0) {
+               eeprom.len = min(bytes_remaining, (u32)PAGE_SIZE);
 
-       if (copy_to_user(useraddr + sizeof(eeprom), data, eeprom.len))
-               ret = -EFAULT;
+               if (copy_from_user(data, userbuf, eeprom.len)) {
+                       ret = -EFAULT;
+                       break;
+               }
+               ret = ops->set_eeprom(dev, &eeprom, data);
+               if (ret)
+                       break;
+               userbuf += eeprom.len;
+               eeprom.offset += eeprom.len;
+               bytes_remaining -= eeprom.len;
+       }
 
- out:
        kfree(data);
        return ret;
 }