For modules.dep depmod -a also creates modules.depcache which is just a simple hash table with chains and a dumb fast compression of strings, so that modules.depcache is roughly 3 times smaller than modules.dep. The hash function already makes no difference between _ and -, so for dep lookups all it needs is compute the hash, walk the chain, comparing full 32-bit hash value and if that hits, compare also modname string, on success just return that and its dependencies. For modules.alias it creates modules.aliascache, which contains a tree. Each tree node has 0 or more associated fnmatch wildcards that need to be fnmatched at that level unconditionally, then some fixed number of characters and prefixes of up to that length can be binary searched to find further node. The further node can be of 3 types - either again a normal range node, or just a pointer to a result (if all chars have been already compared), or an entry containing remaining chars to strncmp and pointer to result. Guess better is just to read the algorithm in modprobe.c for details. When reading modules.alias, modprobe always calls fnmatch on all patterns in there and fnmatch is quite expensive. With modules.aliascache, for some strings which don't have any wildcards in it, fnmatch isn't called at all, otherwise it is called only on wildcards where its prefix consisting of non-wildcard chars matches the input. Both modules.depcache and modules.aliascache contain a file header, which embeds a version number, endianity and mtime/ino/size of the corresponding modules.{dep,alias} file - in case it is hand edited later, modprobe won't use the cache which will be stale in that case, until regenerated. In addition to these changes there are some small cleanups here and there, e.g. as modprobe and depmod aren't threaded we can speed things up quite a lot by using _unlocked functions, or there is no point for every getline_wrapped to malloc new chunk of memory and let the caller free it (almost) immediately again. vanilla, modules.dep (time to handle ~ 1600 dep searches): real 0m6.554s user 0m6.424s sys 0m0.128s patched + modules.depcache real 0m0.042s user 0m0.008s sys 0m0.034s vanilla, modules.alias (time to handle ~ 6400 alias searches): real 1m22.548s user 1m21.554s sys 0m0.965s patched + modules.aliascache real 0m0.552s user 0m0.438s sys 0m0.114s time of modprobe pci:v0000EA60d00009897svAsdBbcCscDiE real 0m0.034s user 0m0.028s sys 0m0.006s vs. real 0m0.007s user 0m0.004s sys 0m0.003s --- module-init-tools-3.4.1/depmod.c.jj 2008-10-01 17:31:35.000000000 +0200 +++ module-init-tools-3.4.1/depmod.c 2008-10-01 17:32:14.000000000 +0200 @@ -24,6 +24,7 @@ #include "depmod.h" #include "moduleops.h" #include "tables.h" +#include "cache.h" #include "testing.h" @@ -503,6 +504,443 @@ static void output_deps(struct module *m } } +static const char *base_dirname; + +struct depcache_dir +{ + const char *name; + struct depcache_dir *next, *same_dir; + unsigned int count, len; + int idx; +}; + +struct depcache_string +{ + struct depcache_string *next; + unsigned int hash; + unsigned int offset; + unsigned int size; + struct depcache_dir dir; + char name[]; +}; + +struct depcache_strings +{ + struct depcache_string *hash[251]; + struct depcache_dir *dirs[256]; + struct depcache_dir dir0; + unsigned int dircount; + unsigned int count; + unsigned int offset; +}; + +static void +depcache_add_string(struct depcache_strings *strings, const char *name) +{ + size_t len = strlen(name); + unsigned int hash = dep_cache_hash(name, len); + struct depcache_string **string = &strings->hash[hash % 251], *s; + + while (*string != NULL) { + if ((*string)->hash == hash + && strcmp((*string)->name, name) == 0) + return; + string = &(*string)->next; + } + + s = NOFAIL(malloc(sizeof(struct depcache_string) + len + 1)); + s->next = NULL; + s->hash = hash; + s->offset = -1; + s->size = len + 1; + memcpy(s->name, name, len + 1); + strings->count++; + *string = s; +} + +static int +depcache_dir_cmp(const void *a, const void *b) +{ + const struct depcache_dir *dira = a; + const struct depcache_dir *dirb = b; + + if (dira->count > dirb->count) + return -1; + if (dira->count < dirb->count) + return 1; + if (dira->len > dirb->len) + return -1; + if (dira->len < dirb->len) + return 1; + return 0; +} + +static void +depcache_finalize_strings(struct depcache_strings *strings) +{ + unsigned int hash, dhash, offset, k, l; + size_t dircount = 0; + struct depcache_dir *dirhash[251], **dh, **dhs, *d; + int count; + + memset (dirhash, '\0', sizeof (dirhash)); + for (hash = 0; hash < 251; hash++) { + struct depcache_string *string = strings->hash[hash]; + + while (string != NULL) { + char *p = strrchr(string->name, '/'); + if (p != NULL) { + dhash = dep_cache_hash(string->name, + p - string->name + 1); + for (dh = &dirhash[dhash % 251]; + *dh; dh = &(*dh)->next) { + if ((*dh)->len != p - string->name + 1) + continue; + if (memcmp(string->name, (*dh)->name, + (*dh)->len) != 0) + continue; + string->dir.same_dir = (*dh)->same_dir; + (*dh)->same_dir = &string->dir; + ++(*dh)->count; + break; + } + if (*dh == NULL) { + string->dir.name = string->name; + string->dir.next = *dh; + string->dir.count = 1; + string->dir.len = p - string->name + 1; + string->dir.idx = -1; + string->dir.same_dir = NULL; + *dh = &string->dir; + ++dircount; + } + } + string = string->next; + } + } + + /* Compute the length of directory prefix common to + all entries. */ + strings->dircount = 1; + strings->dirs[0] = &strings->dir0; + strings->dir0.len = -1; + for (dhash = 0, k = 0; dhash < 251; dhash++) { + d = dirhash[dhash]; + while (d != NULL) { + if (strings->dir0.len == -1) { + strings->dir0.name = d->name; + strings->dir0.len = d->len; + } else { + int i; + for (i = 0; i < d->len + && i < strings->dir0.len; ++i) { + if (strings->dir0.name[i] + != d->name[i]) + break; + } + if (i < strings->dir0.len) { + while (i > 0 + && strings->dir0.name[i - 1] + != '/') + --i; + strings->dir0.len = i; + if (i == 0) + goto no_dir0; + } + } + d = d->next; + } + } +no_dir0:; + /* Assign the remaining up to 255 directory table + entries, starting with most often used ones. */ + dhs = NOFAIL(malloc(sizeof(*dhs) * dircount)); + count = 128; + while (strings->dircount < 256) { + for (dhash = 0, k = 0; dhash < 251; dhash++) { + d = dirhash[dhash]; + while (d != NULL) { + if (d->idx == -1) + dhs[k++] = d; + d = d->next; + } + } + + qsort(dhs, k, sizeof(*dhs), depcache_dir_cmp); + + for (l = 0; l < count && l < k; ++l) { + strings->dirs[strings->dircount] = dhs[l]; + dhs[l]->idx = strings->dircount++; + for (d = dhs[l]->same_dir; d; d = d->same_dir) { + d->len = dhs[l]->len; + d->idx = dhs[l]->idx; + } + if (strings->dircount == 256) + goto idx_done; + } + if (l == k) + goto idx_done; + count /= 2; + memset(dirhash, '\0', sizeof(dirhash)); + for (; l < k; ++l) { + unsigned m = dhs[l]->len; + if (m != 0) { + --m; + while (m > 0) + if (dhs[l]->name[m - 1] == '/') + break; + else + --m; + } + if (m == 0) { + dhs[l]->len = 0; + dhs[l]->idx = 0; + for (d = dhs[l]->same_dir; d; + d = d->same_dir) { + d->len = dhs[l]->len; + d->idx = dhs[l]->idx; + } + continue; + } else { + dhs[l]->len = m; + dhash = dep_cache_hash(dhs[l]->name, + dhs[l]->len); + for (dh = &dirhash[dhash % 251]; + *dh; dh = &(*dh)->next) { + if ((*dh)->len != dhs[l]->len) + continue; + if (memcmp((*dh)->name, dhs[l]->name, + (*dh)->len) != 0) + continue; + (*dh)->count += dhs[l]->count; + for (dh = &(*dh)->same_dir; + *dh; dh = &(*dh)->same_dir); + *dh = dhs[l]; + break; + } + if (*dh == NULL) { + *dh = dhs[l]; + dhs[l]->next = NULL; + } + } + } + } +idx_done:; + free(dhs); + offset = strings->dircount * sizeof(unsigned int) * 2; + offset += strings->dir0.len; + for (k = 1; k < strings->dircount; ++k) + offset += strings->dirs[k]->len - strings->dir0.len; + for (hash = 0; hash < 251; hash++) { + struct depcache_string *string = strings->hash[hash]; + + while (string != NULL) { + if (string->dir.idx == -1) + string->dir.idx = 0; + string->size -= string->dir.len - 1; + string->offset = offset; + offset += string->size; + string = string->next; + } + } +} + +static void +depcache_output_strings(struct depcache_strings *strings, unsigned int offset, + FILE *out) +{ + unsigned int hash, k; + + offset += strings->dircount * sizeof(unsigned int) * 2; + fwrite_unlocked(&offset, 1, sizeof(offset), out); + fwrite_unlocked(&strings->dir0.len, 1, sizeof(strings->dir0.len), out); + offset += strings->dir0.len; + for (k = 1; k < strings->dircount; ++k) { + fwrite_unlocked(&offset, 1, sizeof(offset), out); + fwrite_unlocked(&strings->dirs[k]->len, 1, + sizeof(strings->dirs[k]->len), out); + offset += strings->dirs[k]->len - strings->dir0.len; + } + fwrite_unlocked(strings->dir0.name, strings->dir0.len, 1, out); + for (k = 1; k < strings->dircount; ++k) + fwrite_unlocked(strings->dirs[k]->name + strings->dir0.len, + strings->dirs[k]->len - strings->dir0.len, + 1, out); + for (hash = 0; hash < 251; hash++) { + struct depcache_string *string = strings->hash[hash]; + + while (string != NULL) { + unsigned int len = strings->dirs[string->dir.idx]->len; + fputc_unlocked(string->dir.idx, out); + fwrite_unlocked(string->name + len, string->size - 1, + 1, out); + string = string->next; + } + } +} + +static unsigned int +depcache_string_offset(struct depcache_strings *strings, const char *name, + unsigned int *end) +{ + size_t len = strlen(name); + unsigned int hash = dep_cache_hash(name, len); + struct depcache_string *string = strings->hash[hash % 251]; + + while (string != NULL) { + if (string->hash == hash + && strcmp(string->name, name) == 0) { + if (end) + *end = string->offset + string->size - 1; + return string->offset; + } + string = string->next; + } + abort(); +} + +static int primes[] = { 61, 127, 251, 509, 1021, 2039, 4093, 8191, + 16381, 32749, 65521, 131071 }; + +static void output_depcache(struct module *modules, FILE *out) +{ + struct module *i; + unsigned int count, n; + struct dep_cache_entry **dce; + struct dep_cache header; + unsigned int *dce_offsets, offset; + unsigned int *hashtab, *hashtab_idx; + struct depcache_strings strings; + size_t base_dirname_len = strlen(base_dirname); + char dep_filename[base_dirname_len + sizeof "/modules.dep"]; + struct stat st; + + /* Don't output a binary file to stdout. */ + if (out == stdout) + return; + + memcpy(dep_filename, base_dirname, base_dirname_len); + strcpy(dep_filename + base_dirname_len, "/modules.dep"); + if (stat(dep_filename, &st) < 0) + return; + + memset(&strings, 0, sizeof(strings)); + + for (i = modules, count = 0; i; i = i->next) { + depcache_add_string(&strings, i->pathname + skipchars); + count++; + } + depcache_finalize_strings(&strings); + + dce = NOFAIL(malloc((sizeof(*dce) + sizeof(*dce_offsets)) * count)); + dce_offsets = (unsigned int *) (dce + count); + + for (i = modules, n = 0; i; i = i->next, n++) { + unsigned int ndependencies = 0, k, end; + struct list_head *j, *tmp; + const char *modname, *p; + + order_dep_list(i, i); + + list_for_each_safe(j, tmp, &i->dep_list) + ndependencies++; + + dce[n] = NOFAIL(malloc(sizeof(struct dep_cache_entry) + + ndependencies * sizeof(int))); + modname = strrchr(i->pathname + skipchars, '/'); + if (modname == NULL) + modname = i->pathname + skipchars; + else + modname++; + p = strchr(modname, '.'); + dce[n]->modname_len = p ? p - modname : strlen(modname); + dce[n]->hash = dep_cache_hash(modname, dce[n]->modname_len); + dce[n]->next = 0xffffffff; + dce[n]->fullname + = depcache_string_offset(&strings, i->pathname + skipchars, + &end); + dce[n]->modname = end - strlen(modname); + k = 0; + list_for_each_safe(j, tmp, &i->dep_list) { + struct module *dep + = list_entry(j, struct module, dep_list); + dce[n]->dependencies[k++] + = depcache_string_offset(&strings, + dep->pathname + skipchars, + NULL); + list_del_init(j); + } + dce[n]->num_dependencies = k; + } + + memset(&header, 0, sizeof(header)); + memcpy(header.magic, "mod-dep-cache", sizeof(header.magic)); + header.version = 1; + header.endian = 0x12345678; + for (n = 0; n < (sizeof(primes) / sizeof(primes[0])) - 1; n++) + if (count < primes[n]) + break; + header.hash_size = primes[n]; + header.hash_offset = sizeof(header); + header.moddep_mtime_high = (st.st_mtime >> 16) >> 16; + header.moddep_mtime_low = st.st_mtime; + header.moddep_size = st.st_size; + header.moddep_ino = st.st_ino; + hashtab = NOFAIL(malloc(2 * header.hash_size * sizeof(*hashtab))); + hashtab_idx = hashtab + header.hash_size; + memset(hashtab_idx, 0xff, header.hash_size * sizeof(*hashtab)); + for (n = 0; n < count; n++) { + unsigned int *p + = &hashtab_idx[dce[n]->hash % header.hash_size]; + + while (*p != 0xffffffff) + p = &dce[*p]->next; + *p = n; + } + + offset = sizeof(header) + header.hash_size * sizeof(*hashtab); + memset(dce_offsets, 0xff, sizeof(*dce_offsets) * count); + for (n = 0; n < header.hash_size; n++) { + unsigned int p = hashtab_idx[n]; + if (p == 0xffffffff) + hashtab[n] = p; + else + hashtab[n] = offset; + while (p != 0xffffffff) { + dce_offsets[p] = offset; + offset += sizeof(**dce) + + dce[p]->num_dependencies + * sizeof(dce[p]->dependencies[0]); + p = dce[p]->next; + } + } + + header.dir_table_offset = offset; + fwrite_unlocked(&header, sizeof(header), 1, out); + fwrite_unlocked(hashtab, sizeof(*hashtab), header.hash_size, out); + + for (n = 0; n < header.hash_size; n++) { + unsigned int p = hashtab_idx[n]; + while (p != 0xffffffff) { + unsigned int nextp = dce[p]->next, k; + if (nextp != 0xffffffff) + dce[p]->next = dce_offsets[dce[p]->next]; + dce[p]->modname += offset; + dce[p]->fullname += offset; + for (k = 0; k < dce[p]->num_dependencies; k++) + dce[p]->dependencies[k] += offset; + fwrite_unlocked(dce[p], + sizeof(**dce) + + dce[p]->num_dependencies + * sizeof(dce[p]->dependencies[0]), + 1, out); + p = nextp; + } + } + + depcache_output_strings(&strings, header.dir_table_offset, out); +} + static int smells_like_module(const char *name) { return ends_in(name,".ko") || ends_in(name, ".ko.gz"); @@ -744,11 +1182,65 @@ static const char *next_string(const cha return string; } +struct aliascache_ent +{ + const char *wildcard; + unsigned int modname_idx; +}; + +struct aliascache_ent *aliascache; +size_t aliascache_allocated; +size_t aliascache_count; + +/* Careful! Don't munge - in [ ] as per Debian Bug#350915 */ +static char *underscores(char *string) +{ + if (string) { + unsigned int i; + int inbracket = 0; + for (i = 0; string[i]; i++) { + switch (string[i]) { + case '[': + inbracket++; + break; + case ']': + inbracket--; + break; + case '-': + if (!inbracket) + string[i] = '_'; + } + } + if (inbracket) + warn("Unmatched bracket in %s\n", string); + } + return string; +} + +static void aliascache_add(const char *wildcard, unsigned int modname_idx) +{ + char *p; + if (aliascache_allocated == aliascache_count) { + if (aliascache_allocated) + aliascache_allocated *= 2; + else + aliascache_allocated = 128; + aliascache = NOFAIL(realloc(aliascache, + aliascache_allocated + * sizeof(*aliascache))); + } + p = NOFAIL(strdup(wildcard)); + underscores(p); + aliascache[aliascache_count].wildcard = p; + aliascache[aliascache_count++].modname_idx = modname_idx; +} + static void output_aliases(struct module *modules, FILE *out) { struct module *i; const char *p; unsigned long size; + unsigned int modname_idx = 0; fprintf(out, "# Aliases extracted from modules themselves.\n"); for (i = modules; i; i = i->next) { @@ -759,17 +1251,401 @@ static void output_aliases(struct module /* Grab from old-style .modalias section. */ for (p = i->ops->get_aliases(i, &size); p; - p = next_string(p, &size)) + p = next_string(p, &size)) { fprintf(out, "alias %s %s\n", p, modname); + aliascache_add(p, modname_idx); + } /* Grab form new-style .modinfo section. */ for (p = i->ops->get_modinfo(i, &size); p; p = next_string(p, &size)) { - if (strncmp(p, "alias=", strlen("alias=")) == 0) + if (strncmp(p, "alias=", strlen("alias=")) == 0) { fprintf(out, "alias %s %s\n", p + strlen("alias="), modname); + aliascache_add(p + strlen("alias="), + modname_idx); + } + } + + modname_idx += strlen(modname) + 1; + } +} + +static int aliascache_cmp(const void *p1, const void *p2) +{ + const struct aliascache_ent *ac1 = p1; + const struct aliascache_ent *ac2 = p2; + return strcmp(ac1->wildcard, ac2->wildcard); +} + +struct aliascache_range +{ + struct alias_cache_range header; + unsigned int first, last, offset, depth; + unsigned int wild_size; + struct aliascache_range *array[]; +}; + +static struct aliascache_range *aliascache_create_range(unsigned int depth, + unsigned int first, + unsigned int last, + unsigned int *offset, + unsigned int *singles) +{ + unsigned int wild[10] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + unsigned int ends[10] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + unsigned int dups[10] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + unsigned int i, j, k, l, wildc, endc, size, normc, nodups; + struct aliascache_range *ret; + + for (i = first; i <= last; i++) { + for (j = 0; j < 10; j++) { + switch (aliascache[i].wildcard[depth + j]) { + case '\0': + ends[j]++; + if (i != first + && strcmp(&aliascache[i].wildcard[depth], + &aliascache[i - 1].wildcard[depth]) + == 0) + dups[j]++; + j = 51; + break; + case '\\': + case '?': + case '*': + case '[': + wild[j]++; + j = 52; + break; + default: + break; + } + if (i != first && j < 51 + && strncmp(&aliascache[i].wildcard[depth], + &aliascache[i - 1].wildcard[depth], + j + 1) == 0) + dups[j]++; + } + } + + if (ends[0] == last + 1 - first) { + nodups = 1; + normc = last + 1 - first; + wildc = 0; + i = 0; + } else { + nodups = 0; + for (j = 0; j < 10; j++) + if (wild[j] != 0 || ends[j] != 0) + break; + wildc = 0; + endc = 0; + for (i = j; i < 10; i++) { + endc += ends[i]; + if (wildc + wild[i] > 5 + wild[j]) + break; + if (last + 1 - first - wildc < endc * 2) + break; + wildc += wild[i]; + } + if (i == 0) { + i = 1; + wildc += wild[0]; + } + normc = last + 1 - first - dups[i - 1] - wildc; + } + if (i == 10 && wildc == 0 && normc == 1) { + for (; i < 50; i++) { + if (aliascache[first].wildcard[depth + i] + != aliascache[last].wildcard[depth + i]) + break; + switch (aliascache[first].wildcard[depth + i]) { + case '\0': + case '\\': + case '?': + case '*': + case '[': + break; + default: + continue; + } + break; + } + } + size = sizeof(struct alias_cache_range); + size += (normc + wildc) * sizeof(unsigned int); + size += normc * i; + ret = NOFAIL(calloc(sizeof(*ret) + + normc * sizeof(ret->array[0]), 1)); + ret->header.nchars = i; + ret->header.count_normal = normc; + ret->header.count_wild = wildc; + ret->first = first; + ret->last = last; + ret->offset = *offset; + ret->depth = depth; + *offset += size; + for (i = first; i <= last; i++) { + for (j = 0; j < ret->header.nchars; j++) { + switch (aliascache[i].wildcard[depth + j]) { + case '\0': + j = 51; + break; + case '\\': + case '?': + case '*': + case '[': + j = 52; + ret->wild_size += strlen(aliascache[i].wildcard + + depth) + 1; + break; + default: + break; + } + } + } + *offset = (*offset + ret->wild_size + 3) & ~3; + for (i = first, k = 0; i <= last; i++) { + for (j = 0; j < ret->header.nchars; j++) { + switch (aliascache[i].wildcard[depth + j]) { + case '\0': + j = 51; + break; + case '\\': + case '?': + case '*': + case '[': + j = 52; + break; + default: + break; + } + } + if (j == 52 + 1) + continue; + for (l = i; l < last; l++) + if (strncmp(&aliascache[i].wildcard[depth], + &aliascache[l + 1].wildcard[depth], + ret->header.nchars) != 0) + break; + if ((j == 51 + 1 && i == l) || j == 0) { + ret->array[k] = (void *) 1UL; + l = i; + } else if (i == l + && !aliascache[l].wildcard[depth + + ret->header.nchars]) + /* One string, exactly nchars long. */ + ret->array[k] = (void *) 1UL; + else if (i == l) { + const char *p; + /* Only one entry. If it has no wildcards, + create the special singles entry. */ + for (j = ret->header.nchars; + ret->array[k] == NULL; j++) + switch (aliascache[i].wildcard[depth + j]) { + case '\0': + p = aliascache[i].wildcard; + p += depth + ret->header.nchars; + *singles += sizeof(unsigned int); + *singles += strlen(p) + 1; + *singles = (*singles + 3) & ~3; + ret->array[k] = (void *) 2UL; + break; + case '\\': + case '?': + case '*': + case '[': + goto create_range; + default: + break; + } + } else if (j == 51 + 1) { + size_t len = strlen(aliascache[i].wildcard); + ret->array[k] + = aliascache_create_range(len, i, l, offset, + singles); + } + if (ret->array[k] == NULL) { +create_range: + ret->array[k] + = aliascache_create_range(depth + ret->header.nchars, + i, l, offset, singles); + } + k++; + i = l; + } + if (k != normc) + abort(); + + return ret; +} + +struct aliascache_write_info +{ + FILE *out; + unsigned int singles, offset; + char *singles_buf; +}; + +static void aliascache_write_range(struct aliascache_write_info *info, + struct aliascache_range *range) +{ + size_t size; + char *buf, *strings, *wildcards; + unsigned int *arr, i, j, k, l, m; + + size = sizeof(range->header); + size += (range->header.count_normal + range->header.count_wild) + * sizeof(unsigned int); + size += range->header.count_normal * range->header.nchars; + size += range->wild_size; + size = (size + 3) & ~3; + buf = NOFAIL(malloc(size)); + memcpy(buf, &range->header, sizeof(range->header)); + arr = (unsigned int *) (buf + sizeof(range->header)); + strings = (char *) (arr + (range->header.count_normal + + range->header.count_wild)); + wildcards = strings + + range->header.count_normal * range->header.nchars; + memset (wildcards + range->wild_size, '\0', + (buf + size) - (wildcards + range->wild_size)); + + m = range->header.count_normal; + for (i = range->first, k = 0; i <= range->last; i++) { + const char *p = &aliascache[i].wildcard[range->depth]; + for (j = 0; j < range->header.nchars; j++) { + switch (p[j]) { + case '\0': + j = 51; + break; + case '\\': + case '?': + case '*': + case '[': + j = 52; + break; + default: + break; + } } + if (j == 52 + 1) { + size_t len = strlen(p); + memcpy(wildcards, p, len + 1); + wildcards += len + 1; + arr[m++] = aliascache[i].modname_idx + info->offset; + } else if (j == 0) { + if (range->array[k] != (void *) 1UL) + abort(); + arr[k++] = aliascache[i].modname_idx + info->offset; + } else { + strncpy(strings, p, range->header.nchars); + strings += range->header.nchars; + for (l = i; l < range->last; l++) + if (strncmp(p, + &aliascache[l + 1].wildcard[range->depth], + range->header.nchars) != 0) + break; + if (range->array[k] == (void *) 1UL) { + if (i != l) + abort(); + arr[k] = aliascache[i].modname_idx + + info->offset; + } else if (range->array[k] == (void *) 2UL) { + size_t len = strlen(p + range->header.nchars); + char *r; + if (i != l) + abort(); + *(unsigned int *) info->singles_buf + = aliascache[i].modname_idx + info->offset; + r = info->singles_buf + sizeof(unsigned int); + memcpy(r, p + range->header.nchars, len + 1); + r += len + 1; + arr[k] = info->singles; + len += sizeof(unsigned int) + 1; + len = (len + 3) & ~3; + memset(r, '\0', (info->singles_buf + len) - r); + info->singles_buf += len; + info->singles += len; + } else + arr[k] = range->array[k]->offset; + k++; + i = l; + } + } + + if (k != range->header.count_normal + || m != k + range->header.count_wild) + abort(); + fwrite_unlocked(buf, size, 1, info->out); + free(buf); + + for (k = 0; k < range->header.count_normal; k++) + if (range->array[k] != (void *) 1UL + && range->array[k] != (void *) 2UL) + aliascache_write_range(info, range->array[k]); +} + +static void output_aliascache(struct module *modules, FILE *out) +{ + struct module *i; + struct aliascache_range *root; + struct alias_cache header; + unsigned int offset = sizeof (header), singles = 0; + struct aliascache_write_info info; + char *singles_buf; + size_t base_dirname_len = strlen(base_dirname); + char alias_filename[base_dirname_len + sizeof "/modules.alias"]; + struct stat st; + + /* Don't output a binary file to stdout. */ + if (out == stdout || aliascache_count == 0) + return; + + memcpy(alias_filename, base_dirname, base_dirname_len); + strcpy(alias_filename + base_dirname_len, "/modules.alias"); + if (stat(alias_filename, &st) < 0) + return; + + qsort(aliascache, aliascache_count, sizeof(*aliascache), + aliascache_cmp); + + root = aliascache_create_range(0, 0, aliascache_count - 1, + &offset, &singles); + + memset(&header, 0, sizeof(header)); + memcpy(header.magic, "mod-alias-cache", sizeof(header.magic)); + header.version = 1; + header.endian = 0x12345678; + header.modalias_mtime_high = (st.st_mtime >> 16) >> 16; + header.modalias_mtime_low = st.st_mtime; + header.modalias_size = st.st_size; + header.modalias_ino = st.st_ino; + header.singles = offset; + offset += singles; + header.strings = offset; + fwrite_unlocked(&header, sizeof(header), 1, out); + + singles_buf = NOFAIL(malloc(singles)); + info.out = out; + info.singles = header.singles; + info.offset = header.strings; + info.singles_buf = singles_buf; + aliascache_write_range(&info, root); + if (info.singles != header.strings + || info.singles_buf != singles_buf + singles) + abort(); + + fwrite_unlocked(singles_buf, singles, 1, out); + + for (i = modules; i; i = i->next) { + char modname[strlen(i->pathname)+1]; + size_t len; + + filename2modname(modname, i->pathname); + underscores(modname); + len = strlen(modname); + fwrite_unlocked(modname, len + 1, 1, out); } } @@ -790,6 +1666,8 @@ static struct depfile depfiles[] = { { "modules.seriomap", output_serio_table }, { "modules.alias", output_aliases }, { "modules.symbols", output_symbols }, + { "modules.depcache", output_depcache }, + { "modules.aliascache", output_aliascache }, }; /* If we can't figure it out, it's safe to say "true". */ @@ -841,10 +1719,10 @@ static int depfile_out_of_date(const cha static int fgetc_wrapped(FILE *file, unsigned int *linenum) { for (;;) { - int ch = fgetc(file); + int ch = fgetc_unlocked(file); if (ch != '\\') return ch; - ch = fgetc(file); + ch = fgetc_unlocked(file); if (ch != '\n') return ch; if (linenum) @@ -854,24 +1732,23 @@ static int fgetc_wrapped(FILE *file, uns static char *getline_wrapped(FILE *file, unsigned int *linenum) { - int size = 1024; + static int size; int i = 0; - char *buf = NOFAIL(malloc(size)); + static char *buf; for(;;) { int ch = fgetc_wrapped(file, linenum); if (i == size) { - size *= 2; + size = size ? size * 2 : 1024; buf = NOFAIL(realloc(buf, size)); } if (ch < 0 && i == 0) { - free(buf); return NULL; } if (ch < 0 || ch == '\n') { if (linenum) (*linenum)++; buf[i] = '\0'; - return NOFAIL(realloc(buf, i+1)); + return buf; } buf[i++] = ch; } @@ -983,8 +1860,6 @@ static int read_config_file(const char * } } else grammar(cmd, filename, linenum); - - free(line); } fclose(cfile); return 1; @@ -1176,6 +2051,8 @@ int main(int argc, char *argv[]) } parse_modules(list); + base_dirname = dirname; + for (i = 0; i < sizeof(depfiles)/sizeof(depfiles[0]); i++) { FILE *out; struct depfile *d = &depfiles[i]; @@ -1201,6 +2078,7 @@ int main(int argc, char *argv[]) } } + base_dirname = NULL; free(dirname); free(version); --- module-init-tools-3.4.1/modprobe.c.jj 2008-10-01 17:31:35.000000000 +0200 +++ module-init-tools-3.4.1/modprobe.c 2008-10-01 17:32:14.000000000 +0200 @@ -38,6 +38,7 @@ #include #include #include +#include "cache.h" #define streq(a,b) (strcmp((a),(b)) == 0) #define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) @@ -66,16 +67,14 @@ static int log; static void message(const char *prefix, const char *fmt, va_list *arglist) { - char *buf, *buf2; + char *buf; vasprintf(&buf, fmt, *arglist); - asprintf(&buf2, "%s%s", prefix, buf); if (log) - syslog(LOG_NOTICE, "%s", buf2); + syslog(LOG_NOTICE, "%s%s", prefix, buf); else - fprintf(stderr, "%s", buf2); - free(buf2); + fprintf(stderr, "%s%s", prefix, buf); free(buf); } @@ -129,10 +128,10 @@ static void print_usage(const char *prog static int fgetc_wrapped(FILE *file, unsigned int *linenum) { for (;;) { - int ch = fgetc(file); + int ch = fgetc_unlocked(file); if (ch != '\\') return ch; - ch = fgetc(file); + ch = fgetc_unlocked(file); if (ch != '\n') return ch; if (linenum) @@ -142,24 +141,23 @@ static int fgetc_wrapped(FILE *file, uns static char *getline_wrapped(FILE *file, unsigned int *linenum) { - int size = 1024; + static int size; int i = 0; - char *buf = NOFAIL(malloc(size)); + static char *buf; for(;;) { int ch = fgetc_wrapped(file, linenum); if (i == size) { - size *= 2; + size = size ? size * 2 : 1024; buf = NOFAIL(realloc(buf, size)); } if (ch < 0 && i == 0) { - free(buf); return NULL; } if (ch < 0 || ch == '\n') { if (linenum) (*linenum)++; buf[i] = '\0'; - return NOFAIL(realloc(buf, i+1)); + return buf; } buf[i++] = ch; } @@ -281,9 +279,10 @@ static int add_modules_dep_line(char *li else modname = line; - len = strlen(modname); if (strchr(modname, '.')) len = strchr(modname, '.') - modname; + else + len = strlen(modname); if (!modname_equal(modname, name, len)) return 0; @@ -303,29 +302,107 @@ static int add_modules_dep_line(char *li return 1; } +static void add_depcache_module(struct dep_cache *header, const char *depcache, + unsigned int stroff, struct list_head *list) +{ + unsigned int diridx = (unsigned char) depcache[stroff]; + unsigned int *dirtab + = (unsigned int *) (depcache + header->dir_table_offset); + size_t len = strlen(depcache + stroff + 1); + char name[dirtab[2 * diridx + 1] + len + 1], *p; + memcpy(name, depcache + dirtab[0], dirtab[1]); + p = name + dirtab[1]; + if (diridx != 0) { + memcpy(p, depcache + dirtab[2 * diridx], + dirtab[2 * diridx + 1] - dirtab[1]); + p += dirtab[2 * diridx + 1] - dirtab[1]; + } + memcpy(p, depcache + stroff + 1, len + 1); + add_module(name, (p - name) + len, list); +} + static void read_depends(const char *dirname, const char *start_name, + int avoid_cache, struct list_head *list) { - char *modules_dep_name; + char *modules_dep_name, *modules_depcache_name; char *line; FILE *modules_dep; - int done = 0; + int depcache_fd; + const char *depcache = MAP_FAILED; + struct stat dep_st, depcache_st; asprintf(&modules_dep_name, "%s/%s", dirname, "modules.dep"); - modules_dep = fopen(modules_dep_name, "r"); - if (!modules_dep) - fatal("Could not load %s: %s\n", - modules_dep_name, strerror(errno)); - - /* Stop at first line, as we can have duplicates (eg. symlinks - from boot/ */ - while (!done && (line = getline_wrapped(modules_dep, NULL)) != NULL) { - done = add_modules_dep_line(line, start_name, list); - free(line); + asprintf(&modules_depcache_name, "%s/%s", dirname, "modules.depcache"); + if (avoid_cache) + depcache_fd = -1; + else + depcache_fd = open(modules_depcache_name, O_RDONLY); + if (depcache_fd != -1) { + struct dep_cache *header; + unsigned int *hashtab, hash, entoff; + struct dep_cache_entry *ent; + + if (stat(modules_dep_name, &dep_st) < 0 + || fstat(depcache_fd, &depcache_st) < 0 + || depcache_st.st_size < sizeof(struct dep_cache)) + goto use_modules_dep; + depcache = mmap(NULL, depcache_st.st_size, PROT_READ, + MAP_PRIVATE, depcache_fd, 0); + if (depcache == MAP_FAILED) + goto use_modules_dep; + header = (struct dep_cache *) depcache; + if (memcmp(header->magic, "mod-dep-cache", + sizeof(header->magic)) != 0 + || header->version != 1 + || header->endian != 0x12345678 + || header->moddep_mtime_high + != (unsigned int) ((dep_st.st_mtime >> 16) >> 16) + || header->moddep_mtime_low + != (unsigned int) dep_st.st_mtime + || header->moddep_size != dep_st.st_size + || header->moddep_ino != dep_st.st_ino) + goto use_modules_dep; + hashtab = (unsigned int *) (depcache + + header->hash_offset); + hash = dep_cache_hash(start_name, strlen(start_name)); + for (entoff = hashtab[hash % header->hash_size]; + entoff != 0xffffffff; entoff = ent->next) { + ent = (struct dep_cache_entry *) + (depcache + entoff); + if (modname_equal(depcache + ent->modname, start_name, + ent->modname_len)) { + int i; + add_depcache_module(header, depcache, + ent->fullname, list); + for (i = 0; i < ent->num_dependencies; ++i) + add_depcache_module(header, depcache, + ent->dependencies[i], + list); + break; + } + } + } else { +use_modules_dep: + modules_dep = fopen(modules_dep_name, "r"); + if (!modules_dep) + fatal("Could not load %s: %s\n", + modules_dep_name, strerror(errno)); + + /* Stop at first line, as we can have duplicates (eg. symlinks + from boot/ */ + while ((line = getline_wrapped(modules_dep, NULL)) != NULL) + if (add_modules_dep_line(line, start_name, list)) + break; + fclose(modules_dep); } - fclose(modules_dep); + if (depcache != MAP_FAILED) + munmap((char *) depcache, depcache_st.st_size); + if (depcache_fd != 0) + close(depcache_fd); free(modules_dep_name); + free(modules_depcache_name); } /* We use error numbers in a loose translation... */ @@ -391,18 +468,15 @@ again: if (streq(entry, "Loading") || streq(entry, "Unloading")) { usleep(100000); - free(line); fclose(proc_modules); goto again; } } out: - free(line); fclose(proc_modules); return 1; } - free(line); } fclose(proc_modules); return 0; @@ -1106,7 +1180,7 @@ static int do_wildcard(const char *dirna /* Ignore lines without : or which start with a # */ ptr = strchr(line, ':'); if (ptr == NULL || line[strspn(line, "\t ")] == '#') - goto next; + continue; *ptr = '\0'; /* "type" must match complete directory component(s). */ @@ -1117,8 +1191,6 @@ static int do_wildcard(const char *dirna if (fnmatch(wcard, modname, 0) == 0) printf("%s\n", line); } - next: - free(line); } free(wcard); @@ -1240,8 +1312,6 @@ static int read_config_file(const char * } } else grammar(cmd, filename, linenum); - - free(line); } fclose(cfile); return 1; @@ -1290,6 +1360,148 @@ static int read_config(const char *filen return ret; } +static int find_aliascache(const struct alias_cache *header, + const char *name, + const struct alias_cache_range *range, + struct module_alias **aliases) +{ + unsigned int i, lo, hi, mid; + int r; + const char *str, *str2, *strtab, *wild, *realname; + const unsigned int *table; + size_t len; + + table = (const unsigned int *) (range + 1); + strtab = (const char *) (table + range->count_normal + + range->count_wild); + wild = strtab + (range->count_normal * range->nchars); + + /* First, run fnmatch on all wildcards at this level. */ + for (i = 0; i < range->count_wild; i++) { + if (fnmatch(wild, name, 0) == 0) { + realname = ((const char *) header) + + table[range->count_normal + i]; + *aliases = add_alias(realname, *aliases); + } + wild = strchr(wild, '\0') + 1; + } + + /* nchars == 0 is special, there are multiple non-wildcard + symbols with the same wildcard. Either all of them + match, or none. */ + if (range->nchars == 0) { + if (name[0] != '\0') + return 0; + for (i = 0; i < range->count_normal; i++) { + realname = ((const char *) header) + table[i]; + *aliases = add_alias(realname, *aliases); + } + return 0; + } + + /* Binary search. */ + lo = 0; + str = NULL; + hi = range->count_normal; + r = 1; + mid = lo; + while (lo < hi) { + mid = (lo + hi) / 2; + str = strtab + mid * range->nchars; + r = strncmp(str, name, range->nchars); + if (r == 0) + break; + else if (r > 0) + hi = mid; + else + lo = mid + 1; + } + if (r != 0) + return 0; + + if (table[mid] >= header->strings) { + len = strlen(name); + if (len <= range->nchars) { + realname = ((const char *) header) + table[mid]; + *aliases = add_alias(realname, *aliases); + } + } else if (table[mid] >= header->singles) { + str2 = ((const char *) header) + table[mid] + + sizeof(unsigned int); + if (strcmp (str2, name + range->nchars) == 0) { + realname = ((const char *) header) + + *(const unsigned int *) + (((const char *) header) + table[mid]); + *aliases = add_alias(realname, *aliases); + } + } else { + const struct alias_cache_range *range2; + range2 = (const struct alias_cache_range *) + (((const char *) header) + table[mid]); + len = strlen(name); + return find_aliascache(header, + name + (len > range->nchars + ? range->nchars : len), + range2, aliases); + } + return 0; +} + +static int read_aliases(const char *aliasfilename, + const char *name, + int removing, + struct module_options **options, + struct module_command **commands, + struct module_alias **aliases, + struct module_blacklist **blacklist) +{ + char *aliascache_name; + int aliascache_fd; + const char *aliascache = MAP_FAILED; + struct stat alias_st, aliascache_st; + int ret; + + asprintf(&aliascache_name, "%scache", aliasfilename); + aliascache_fd = open(aliascache_name, O_RDONLY); + if (aliascache_fd != -1) { + struct alias_cache *header; + struct alias_cache_range *range; + + if (stat(aliasfilename, &alias_st) < 0 + || fstat(aliascache_fd, &aliascache_st) < 0 + || aliascache_st.st_size < sizeof(struct alias_cache)) + goto use_modules_alias; + aliascache = mmap(NULL, aliascache_st.st_size, PROT_READ, + MAP_PRIVATE, aliascache_fd, 0); + if (aliascache == MAP_FAILED) + goto use_modules_alias; + header = (struct alias_cache *) aliascache; + if (memcmp(header->magic, "mod-alias-cache", + sizeof(header->magic)) != 0 + || header->version != 1 + || header->endian != 0x12345678 + || header->modalias_mtime_high + != (unsigned int) ((alias_st.st_mtime >> 16) >> 16) + || header->modalias_mtime_low + != (unsigned int) alias_st.st_mtime + || header->modalias_size != alias_st.st_size + || header->modalias_ino != alias_st.st_ino) + goto use_modules_alias; + range = (struct alias_cache_range *) (void *) (header + 1); + ret = find_aliascache(header, name, range, aliases); + } else { +use_modules_alias: + ret = read_config(aliasfilename, name, 0, removing, options, + commands, aliases, blacklist); + } + if (aliascache != MAP_FAILED) + munmap((void *) aliascache, aliascache_st.st_size); + if (aliascache_fd != -1) + close(aliascache_fd); + free(aliascache_name); + return ret; +} + static const char *default_configs[] = { "/etc/modprobe.conf", @@ -1355,8 +1567,6 @@ static int read_kcmdline(int dump_only, opt, *options); } } - - free(line); } fclose(kcmdline); return 1; @@ -1745,14 +1955,14 @@ int main(int argc, char *argv[]) if (!aliases) { /* We only use canned aliases as last resort. */ - read_depends(dirname, modulearg, &list); + read_depends(dirname, modulearg, 0, &list); if (list_empty(&list) && !find_command(modulearg, commands)) { - read_config(aliasfilename, modulearg, 0, - remove, &modoptions, &commands, - &aliases, &blacklist); + read_aliases(aliasfilename, modulearg, + remove, &modoptions, &commands, + &aliases, &blacklist); } } @@ -1769,7 +1979,8 @@ int main(int argc, char *argv[]) opts = add_extra_options(modulearg, opts, modoptions); - read_depends(dirname, aliases->module, &list); + read_depends(dirname, aliases->module, + 0, &list); handle_module(aliases->module, &list, newname, remove, opts, first_time, err, dry_run, verbose, modoptions, --- module-init-tools-3.4.1/cache.h.jj 2008-10-01 17:32:14.000000000 +0200 +++ module-init-tools-3.4.1/cache.h 2008-10-01 17:32:14.000000000 +0200 @@ -0,0 +1,57 @@ +#ifndef MODINITTOOLS_CACHE_H +#define MODINITTOOLS_CACHE_H + +struct dep_cache { + char magic[sizeof "mod-dep-cache"]; + unsigned char version; + unsigned char pad; + unsigned int endian; + unsigned int moddep_mtime_high; + unsigned int moddep_mtime_low; + unsigned int moddep_size; + unsigned int moddep_ino; + unsigned int hash_size; + unsigned int hash_offset; + unsigned int dir_table_offset; +}; + +struct dep_cache_entry { + unsigned int hash; + unsigned int next; + unsigned int modname; + unsigned int modname_len; + unsigned int fullname; + unsigned int num_dependencies; + unsigned int dependencies[]; +}; + +static inline unsigned int dep_cache_hash (const char *str, size_t len) +{ + size_t n = 0; + unsigned int r = 0; + + for (n = 0; n < len; n++, str++) + r = r * 67 + (*str == '-' ? '_' : *str) - 113; + return r + len; +} + +struct alias_cache { + char magic[sizeof "mod-alias-cache"]; + unsigned char version; + unsigned char pad[3]; + unsigned int endian; + unsigned int modalias_mtime_high; + unsigned int modalias_mtime_low; + unsigned int modalias_size; + unsigned int modalias_ino; + unsigned int singles; + unsigned int strings; +}; + +struct alias_cache_range { + unsigned int nchars; + unsigned int count_normal; + unsigned int count_wild; +}; + +#endif /* MODINITTOOLS_CACHE_H */