summaryrefslogtreecommitdiff
path: root/net/core
diff options
context:
space:
mode:
authorStephen Hemminger <shemminger@osdl.org>2004-02-05 22:24:10 -0800
committerStephen Hemminger <shemminger@osdl.org>2004-02-05 22:24:10 -0800
commitf8c11435c19d5cee964370d8fd62d397f2b4c212 (patch)
treedd6c5b2d5ef8dc67fc3cf06eedea9416a51121e5 /net/core
parent6434c04175e058429430e2064d3a0ce112ce45ad (diff)
[NET]: Support for lots of netdevs -- faster dev_alloc_name
Convert dev_alloc_name from O(n^2) lookup to O(n) by using a page as bitmap to figure out how many devices of that pattern have been allocated. This works for up to 32k devices (PAGE_SIZE*8) on i386, more on other platforms. Correctly handles the boundary cases where number of devices won't fit because name length is limited. Adds strnchr to the string libraries since we need to find the % format character, but only care if it is in the first 15 bytes.
Diffstat (limited to 'net/core')
-rw-r--r--net/core/dev.c67
1 files changed, 46 insertions, 21 deletions
diff --git a/net/core/dev.c b/net/core/dev.c
index 17e1fa1b1815..d1dfcef63c5a 100644
--- a/net/core/dev.c
+++ b/net/core/dev.c
@@ -720,30 +720,55 @@ int dev_valid_name(const char *name)
int dev_alloc_name(struct net_device *dev, const char *name)
{
- int i;
- char buf[32];
- char *p;
-
- /*
- * Verify the string as this thing may have come from
- * the user. There must be either one "%d" and no other "%"
- * characters, or no "%" characters at all.
- */
- p = strchr(name, '%');
- if (p && (p[1] != 'd' || strchr(p + 2, '%')))
- return -EINVAL;
+ int i = 0;
+ char buf[IFNAMSIZ];
+ const char *p;
+ const int max_netdevices = 8*PAGE_SIZE;
+ long *inuse;
+ struct net_device *d;
+
+ p = strnchr(name, IFNAMSIZ-1, '%');
+ if (p) {
+ /*
+ * Verify the string as this thing may have come from
+ * the user. There must be either one "%d" and no other "%"
+ * characters.
+ */
+ if (p[1] != 'd' || strchr(p + 2, '%'))
+ return -EINVAL;
- /*
- * If you need over 100 please also fix the algorithm...
- */
- for (i = 0; i < 100; i++) {
- snprintf(buf, sizeof(buf), name, i);
- if (!__dev_get_by_name(buf)) {
- strcpy(dev->name, buf);
- return i;
+ /* Use one page as a bit array of possible slots */
+ inuse = (long *) get_zeroed_page(GFP_ATOMIC);
+ if (!inuse)
+ return -ENOMEM;
+
+ for (d = dev_base; d; d = d->next) {
+ if (!sscanf(d->name, name, &i))
+ continue;
+ if (i < 0 || i >= max_netdevices)
+ continue;
+
+ /* avoid cases where sscanf is not exact inverse of printf */
+ snprintf(buf, sizeof(buf), name, i);
+ if (!strncmp(buf, d->name, IFNAMSIZ))
+ set_bit(i, inuse);
}
+
+ i = find_first_zero_bit(inuse, max_netdevices);
+ free_page((unsigned long) inuse);
}
- return -ENFILE; /* Over 100 of the things .. bail out! */
+
+ snprintf(buf, sizeof(buf), name, i);
+ if (!__dev_get_by_name(buf)) {
+ strlcpy(dev->name, buf, IFNAMSIZ);
+ return i;
+ }
+
+ /* It is possible to run out of possible slots
+ * when the name is long and there isn't enough space left
+ * for the digits, or if all bits are used.
+ */
+ return -ENFILE;
}