[ETHTOOL]: Add support for large eeproms

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>
This commit is contained in:
Mandeep Singh Baines 2008-04-15 19:24:17 -07:00 committed by David S. Miller
parent 73e87e02ec
commit b131dd5d65

View File

@ -284,8 +284,10 @@ static int ethtool_get_eeprom(struct net_device *dev, void __user *useraddr)
{ {
struct ethtool_eeprom eeprom; struct ethtool_eeprom eeprom;
const struct ethtool_ops *ops = dev->ethtool_ops; const struct ethtool_ops *ops = dev->ethtool_ops;
void __user *userbuf = useraddr + sizeof(eeprom);
u32 bytes_remaining;
u8 *data; u8 *data;
int ret; int ret = 0;
if (!ops->get_eeprom || !ops->get_eeprom_len) if (!ops->get_eeprom || !ops->get_eeprom_len)
return -EOPNOTSUPP; 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)) if (eeprom.offset + eeprom.len > ops->get_eeprom_len(dev))
return -EINVAL; return -EINVAL;
data = kmalloc(eeprom.len, GFP_USER); data = kmalloc(PAGE_SIZE, GFP_USER);
if (!data) if (!data)
return -ENOMEM; return -ENOMEM;
ret = -EFAULT; bytes_remaining = eeprom.len;
if (copy_from_user(data, useraddr + sizeof(eeprom), eeprom.len)) while (bytes_remaining > 0) {
goto out; eeprom.len = min(bytes_remaining, (u32)PAGE_SIZE);
ret = ops->get_eeprom(dev, &eeprom, data); ret = ops->get_eeprom(dev, &eeprom, data);
if (ret) if (ret)
goto out; break;
if (copy_to_user(userbuf, data, eeprom.len)) {
ret = -EFAULT;
break;
}
userbuf += eeprom.len;
eeprom.offset += eeprom.len;
bytes_remaining -= eeprom.len;
}
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;
out:
kfree(data); kfree(data);
return ret; return ret;
} }
@ -329,8 +331,10 @@ static int ethtool_set_eeprom(struct net_device *dev, void __user *useraddr)
{ {
struct ethtool_eeprom eeprom; struct ethtool_eeprom eeprom;
const struct ethtool_ops *ops = dev->ethtool_ops; const struct ethtool_ops *ops = dev->ethtool_ops;
void __user *userbuf = useraddr + sizeof(eeprom);
u32 bytes_remaining;
u8 *data; u8 *data;
int ret; int ret = 0;
if (!ops->set_eeprom || !ops->get_eeprom_len) if (!ops->set_eeprom || !ops->get_eeprom_len)
return -EOPNOTSUPP; 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)) if (eeprom.offset + eeprom.len > ops->get_eeprom_len(dev))
return -EINVAL; return -EINVAL;
data = kmalloc(eeprom.len, GFP_USER); data = kmalloc(PAGE_SIZE, GFP_USER);
if (!data) if (!data)
return -ENOMEM; return -ENOMEM;
ret = -EFAULT; bytes_remaining = eeprom.len;
if (copy_from_user(data, useraddr + sizeof(eeprom), eeprom.len)) while (bytes_remaining > 0) {
goto out; eeprom.len = min(bytes_remaining, (u32)PAGE_SIZE);
ret = ops->set_eeprom(dev, &eeprom, data); if (copy_from_user(data, userbuf, eeprom.len)) {
if (ret) ret = -EFAULT;
goto out; break;
}
ret = ops->set_eeprom(dev, &eeprom, data);
if (ret)
break;
userbuf += eeprom.len;
eeprom.offset += eeprom.len;
bytes_remaining -= eeprom.len;
}
if (copy_to_user(useraddr + sizeof(eeprom), data, eeprom.len))
ret = -EFAULT;
out:
kfree(data); kfree(data);
return ret; return ret;
} }