Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

selinux: prepare for inlining of hashtab functions

Refactor searching and inserting into hashtabs to pave the way for
converting hashtab_search() and hashtab_insert() to inline functions in
the next patch. This will avoid indirect calls and allow the compiler to
better optimize individual callers, leading to a significant performance
improvement.

In order to avoid the indirect calls, the key hashing and comparison
callbacks need to be extracted from the hashtab struct and passed
directly to hashtab_search()/_insert() by the callers so that the
callback address is always known at compile time. The kernel's
rhashtable library (<linux/rhashtable*.h>) does the same thing.

This of course makes the hashtab functions slightly easier to misuse by
passing a wrong callback set, but unfortunately there is no better way
to implement a hash table that is both generic and efficient in C. This
patch tries to somewhat mitigate this by only calling the hashtab
functions in the same file where the corresponding callbacks are
defined (wrapping them into more specialized functions as needed).

Note that this patch doesn't bring any benefit without also moving the
definitions of hashtab_search() and -_insert() to the header file, which
is done in a follow-up patch for easier review of the hashtab.c changes
in this patch.

Signed-off-by: Ondrej Mosnacek <omosnace@redhat.com>
Acked-by: Stephen Smalley <stephen.smalley.work@gmail.com>
Signed-off-by: Paul Moore <paul@paul-moore.com>

authored by

Ondrej Mosnacek and committed by
Paul Moore
24def7bb 237389e3

+110 -63
+23 -21
security/selinux/ss/hashtab.c
··· 29 29 return nel == 0 ? 0 : roundup_pow_of_two(nel); 30 30 } 31 31 32 - int hashtab_init(struct hashtab *h, 33 - u32 (*hash_value)(struct hashtab *h, const void *key), 34 - int (*keycmp)(struct hashtab *h, const void *key1, 35 - const void *key2), 36 - u32 nel_hint) 32 + int hashtab_init(struct hashtab *h, u32 nel_hint) 37 33 { 38 34 h->size = hashtab_compute_size(nel_hint); 39 35 h->nel = 0; 40 - h->hash_value = hash_value; 41 - h->keycmp = keycmp; 42 36 if (!h->size) 43 37 return 0; 44 38 ··· 40 46 return h->htable ? 0 : -ENOMEM; 41 47 } 42 48 43 - int hashtab_insert(struct hashtab *h, void *key, void *datum) 49 + int hashtab_insert(struct hashtab *h, void *key, void *datum, 50 + struct hashtab_key_params key_params) 44 51 { 45 52 u32 hvalue; 46 53 struct hashtab_node *prev, *cur, *newnode; ··· 51 56 if (!h->size || h->nel == HASHTAB_MAX_NODES) 52 57 return -EINVAL; 53 58 54 - hvalue = h->hash_value(h, key); 59 + hvalue = key_params.hash(key) & (h->size - 1); 55 60 prev = NULL; 56 61 cur = h->htable[hvalue]; 57 - while (cur && h->keycmp(h, key, cur->key) > 0) { 62 + while (cur) { 63 + int cmp = key_params.cmp(key, cur->key); 64 + 65 + if (cmp == 0) 66 + return -EEXIST; 67 + if (cmp < 0) 68 + break; 58 69 prev = cur; 59 70 cur = cur->next; 60 71 } 61 - 62 - if (cur && (h->keycmp(h, key, cur->key) == 0)) 63 - return -EEXIST; 64 72 65 73 newnode = kmem_cache_zalloc(hashtab_node_cachep, GFP_KERNEL); 66 74 if (!newnode) ··· 82 84 return 0; 83 85 } 84 86 85 - void *hashtab_search(struct hashtab *h, const void *key) 87 + void *hashtab_search(struct hashtab *h, const void *key, 88 + struct hashtab_key_params key_params) 86 89 { 87 90 u32 hvalue; 88 91 struct hashtab_node *cur; ··· 91 92 if (!h->size) 92 93 return NULL; 93 94 94 - hvalue = h->hash_value(h, key); 95 + hvalue = key_params.hash(key) & (h->size - 1); 95 96 cur = h->htable[hvalue]; 96 - while (cur && h->keycmp(h, key, cur->key) > 0) 97 + while (cur) { 98 + int cmp = key_params.cmp(key, cur->key); 99 + 100 + if (cmp == 0) 101 + return cur->datum; 102 + if (cmp < 0) 103 + break; 97 104 cur = cur->next; 98 - 99 - if (!cur || (h->keycmp(h, key, cur->key) != 0)) 100 - return NULL; 101 - 102 - return cur->datum; 105 + } 106 + return NULL; 103 107 } 104 108 105 109 void hashtab_destroy(struct hashtab *h)
+11 -11
security/selinux/ss/hashtab.h
··· 13 13 14 14 #define HASHTAB_MAX_NODES 0xffffffff 15 15 16 + struct hashtab_key_params { 17 + u32 (*hash)(const void *key); /* hash function */ 18 + int (*cmp)(const void *key1, const void *key2); 19 + /* key comparison function */ 20 + }; 21 + 16 22 struct hashtab_node { 17 23 void *key; 18 24 void *datum; ··· 29 23 struct hashtab_node **htable; /* hash table */ 30 24 u32 size; /* number of slots in hash table */ 31 25 u32 nel; /* number of elements in hash table */ 32 - u32 (*hash_value)(struct hashtab *h, const void *key); 33 - /* hash function */ 34 - int (*keycmp)(struct hashtab *h, const void *key1, const void *key2); 35 - /* key comparison function */ 36 26 }; 37 27 38 28 struct hashtab_info { ··· 41 39 * 42 40 * Returns -ENOMEM if insufficient space is available or 0 otherwise. 43 41 */ 44 - int hashtab_init(struct hashtab *h, 45 - u32 (*hash_value)(struct hashtab *h, const void *key), 46 - int (*keycmp)(struct hashtab *h, const void *key1, 47 - const void *key2), 48 - u32 nel_hint); 42 + int hashtab_init(struct hashtab *h, u32 nel_hint); 49 43 50 44 /* 51 45 * Inserts the specified (key, datum) pair into the specified hash table. ··· 51 53 * -EINVAL for general errors or 52 54 0 otherwise. 53 55 */ 54 - int hashtab_insert(struct hashtab *h, void *k, void *d); 56 + int hashtab_insert(struct hashtab *h, void *k, void *d, 57 + struct hashtab_key_params key_params); 55 58 56 59 /* 57 60 * Searches for the entry with the specified key in the hash table. ··· 60 61 * Returns NULL if no entry has the specified key or 61 62 * the datum of the entry otherwise. 62 63 */ 63 - void *hashtab_search(struct hashtab *h, const void *k); 64 + void *hashtab_search(struct hashtab *h, const void *k, 65 + struct hashtab_key_params key_params); 64 66 65 67 /* 66 68 * Destroys the specified hash table.
+1 -1
security/selinux/ss/mls.c
··· 507 507 rtr.source_type = scontext->type; 508 508 rtr.target_type = tcontext->type; 509 509 rtr.target_class = tclass; 510 - r = hashtab_search(&p->range_tr, &rtr); 510 + r = policydb_rangetr_search(p, &rtr); 511 511 if (r) 512 512 return mls_range_set(newcontext, r); 513 513
+54 -22
security/selinux/ss/policydb.c
··· 411 411 return rc; 412 412 } 413 413 414 - static u32 filenametr_hash(struct hashtab *h, const void *k) 414 + static u32 filenametr_hash(const void *k) 415 415 { 416 416 const struct filename_trans_key *ft = k; 417 417 unsigned long hash; ··· 423 423 byte_num = 0; 424 424 while ((focus = ft->name[byte_num++])) 425 425 hash = partial_name_hash(focus, hash); 426 - return hash & (h->size - 1); 426 + return hash; 427 427 } 428 428 429 - static int filenametr_cmp(struct hashtab *h, const void *k1, const void *k2) 429 + static int filenametr_cmp(const void *k1, const void *k2) 430 430 { 431 431 const struct filename_trans_key *ft1 = k1; 432 432 const struct filename_trans_key *ft2 = k2; ··· 444 444 445 445 } 446 446 447 - static u32 rangetr_hash(struct hashtab *h, const void *k) 447 + static const struct hashtab_key_params filenametr_key_params = { 448 + .hash = filenametr_hash, 449 + .cmp = filenametr_cmp, 450 + }; 451 + 452 + struct filename_trans_datum *policydb_filenametr_search( 453 + struct policydb *p, struct filename_trans_key *key) 454 + { 455 + return hashtab_search(&p->filename_trans, key, filenametr_key_params); 456 + } 457 + 458 + static u32 rangetr_hash(const void *k) 448 459 { 449 460 const struct range_trans *key = k; 450 461 451 - return (key->source_type + (key->target_type << 3) + 452 - (key->target_class << 5)) & (h->size - 1); 462 + return key->source_type + (key->target_type << 3) + 463 + (key->target_class << 5); 453 464 } 454 465 455 - static int rangetr_cmp(struct hashtab *h, const void *k1, const void *k2) 466 + static int rangetr_cmp(const void *k1, const void *k2) 456 467 { 457 468 const struct range_trans *key1 = k1, *key2 = k2; 458 469 int v; ··· 481 470 return v; 482 471 } 483 472 484 - static u32 role_trans_hash(struct hashtab *h, const void *k) 473 + static const struct hashtab_key_params rangetr_key_params = { 474 + .hash = rangetr_hash, 475 + .cmp = rangetr_cmp, 476 + }; 477 + 478 + struct mls_range *policydb_rangetr_search(struct policydb *p, 479 + struct range_trans *key) 480 + { 481 + return hashtab_search(&p->range_tr, key, rangetr_key_params); 482 + } 483 + 484 + static u32 role_trans_hash(const void *k) 485 485 { 486 486 const struct role_trans_key *key = k; 487 487 488 - return (key->role + (key->type << 3) + (key->tclass << 5)) & 489 - (h->size - 1); 488 + return key->role + (key->type << 3) + (key->tclass << 5); 490 489 } 491 490 492 - static int role_trans_cmp(struct hashtab *h, const void *k1, const void *k2) 491 + static int role_trans_cmp(const void *k1, const void *k2) 493 492 { 494 493 const struct role_trans_key *key1 = k1, *key2 = k2; 495 494 int v; ··· 513 492 return v; 514 493 515 494 return key1->tclass - key2->tclass; 495 + } 496 + 497 + static const struct hashtab_key_params roletr_key_params = { 498 + .hash = role_trans_hash, 499 + .cmp = role_trans_cmp, 500 + }; 501 + 502 + struct role_trans_datum *policydb_roletr_search(struct policydb *p, 503 + struct role_trans_key *key) 504 + { 505 + return hashtab_search(&p->role_tr, key, roletr_key_params); 516 506 } 517 507 518 508 /* ··· 1828 1796 1829 1797 nel = le32_to_cpu(buf[0]); 1830 1798 1831 - rc = hashtab_init(&p->range_tr, rangetr_hash, rangetr_cmp, nel); 1799 + rc = hashtab_init(&p->range_tr, nel); 1832 1800 if (rc) 1833 1801 return rc; 1834 1802 ··· 1873 1841 goto out; 1874 1842 } 1875 1843 1876 - rc = hashtab_insert(&p->range_tr, rt, r); 1844 + rc = hashtab_insert(&p->range_tr, rt, r, rangetr_key_params); 1877 1845 if (rc) 1878 1846 goto out; 1879 1847 ··· 1920 1888 otype = le32_to_cpu(buf[3]); 1921 1889 1922 1890 last = NULL; 1923 - datum = hashtab_search(&p->filename_trans, &key); 1891 + datum = policydb_filenametr_search(p, &key); 1924 1892 while (datum) { 1925 1893 if (unlikely(ebitmap_get_bit(&datum->stypes, stype - 1))) { 1926 1894 /* conflicting/duplicate rules are ignored */ ··· 1950 1918 if (!ft) 1951 1919 goto out; 1952 1920 1953 - rc = hashtab_insert(&p->filename_trans, ft, datum); 1921 + rc = hashtab_insert(&p->filename_trans, ft, datum, 1922 + filenametr_key_params); 1954 1923 if (rc) 1955 1924 goto out; 1956 1925 name = NULL; ··· 2039 2006 ft->tclass = tclass; 2040 2007 ft->name = name; 2041 2008 2042 - rc = hashtab_insert(&p->filename_trans, ft, first); 2009 + rc = hashtab_insert(&p->filename_trans, ft, first, 2010 + filenametr_key_params); 2043 2011 if (rc == -EEXIST) 2044 2012 pr_err("SELinux: Duplicate filename transition key\n"); 2045 2013 if (rc) ··· 2078 2044 if (p->policyvers < POLICYDB_VERSION_COMP_FTRANS) { 2079 2045 p->compat_filename_trans_count = nel; 2080 2046 2081 - rc = hashtab_init(&p->filename_trans, filenametr_hash, 2082 - filenametr_cmp, (1 << 11)); 2047 + rc = hashtab_init(&p->filename_trans, (1 << 11)); 2083 2048 if (rc) 2084 2049 return rc; 2085 2050 ··· 2088 2055 return rc; 2089 2056 } 2090 2057 } else { 2091 - rc = hashtab_init(&p->filename_trans, filenametr_hash, 2092 - filenametr_cmp, nel); 2058 + rc = hashtab_init(&p->filename_trans, nel); 2093 2059 if (rc) 2094 2060 return rc; 2095 2061 ··· 2571 2539 goto bad; 2572 2540 nel = le32_to_cpu(buf[0]); 2573 2541 2574 - rc = hashtab_init(&p->role_tr, role_trans_hash, role_trans_cmp, nel); 2542 + rc = hashtab_init(&p->role_tr, nel); 2575 2543 if (rc) 2576 2544 goto bad; 2577 2545 for (i = 0; i < nel; i++) { ··· 2608 2576 !policydb_role_isvalid(p, rtd->new_role)) 2609 2577 goto bad; 2610 2578 2611 - rc = hashtab_insert(&p->role_tr, rtk, rtd); 2579 + rc = hashtab_insert(&p->role_tr, rtk, rtd, roletr_key_params); 2612 2580 if (rc) 2613 2581 goto bad; 2614 2582
+9
security/selinux/ss/policydb.h
··· 324 324 extern int policydb_read(struct policydb *p, void *fp); 325 325 extern int policydb_write(struct policydb *p, void *fp); 326 326 327 + extern struct filename_trans_datum *policydb_filenametr_search( 328 + struct policydb *p, struct filename_trans_key *key); 329 + 330 + extern struct mls_range *policydb_rangetr_search( 331 + struct policydb *p, struct range_trans *key); 332 + 333 + extern struct role_trans_datum *policydb_roletr_search( 334 + struct policydb *p, struct role_trans_key *key); 335 + 327 336 #define POLICYDB_CONFIG_MLS 1 328 337 329 338 /* the config flags related to unknown classes/perms are bits 2 and 3 */
+2 -2
security/selinux/ss/services.c
··· 1671 1671 ft.tclass = tclass; 1672 1672 ft.name = objname; 1673 1673 1674 - datum = hashtab_search(&policydb->filename_trans, &ft); 1674 + datum = policydb_filenametr_search(policydb, &ft); 1675 1675 while (datum) { 1676 1676 if (ebitmap_get_bit(&datum->stypes, stype - 1)) { 1677 1677 newcontext->type = datum->otype; ··· 1834 1834 .tclass = tclass, 1835 1835 }; 1836 1836 1837 - rtd = hashtab_search(&policydb->role_tr, &rtk); 1837 + rtd = policydb_roletr_search(policydb, &rtk); 1838 1838 if (rtd) 1839 1839 newcontext.role = rtd->new_role; 1840 1840 }
+10 -6
security/selinux/ss/symtab.c
··· 9 9 #include <linux/errno.h> 10 10 #include "symtab.h" 11 11 12 - static unsigned int symhash(struct hashtab *h, const void *key) 12 + static unsigned int symhash(const void *key) 13 13 { 14 14 const char *p, *keyp; 15 15 unsigned int size; ··· 20 20 size = strlen(keyp); 21 21 for (p = keyp; (p - keyp) < size; p++) 22 22 val = (val << 4 | (val >> (8*sizeof(unsigned int)-4))) ^ (*p); 23 - return val & (h->size - 1); 23 + return val; 24 24 } 25 25 26 - static int symcmp(struct hashtab *h, const void *key1, const void *key2) 26 + static int symcmp(const void *key1, const void *key2) 27 27 { 28 28 const char *keyp1, *keyp2; 29 29 ··· 32 32 return strcmp(keyp1, keyp2); 33 33 } 34 34 35 + static const struct hashtab_key_params symtab_key_params = { 36 + .hash = symhash, 37 + .cmp = symcmp, 38 + }; 35 39 36 40 int symtab_init(struct symtab *s, unsigned int size) 37 41 { 38 42 s->nprim = 0; 39 - return hashtab_init(&s->table, symhash, symcmp, size); 43 + return hashtab_init(&s->table, size); 40 44 } 41 45 42 46 int symtab_insert(struct symtab *s, char *name, void *datum) 43 47 { 44 - return hashtab_insert(&s->table, name, datum); 48 + return hashtab_insert(&s->table, name, datum, symtab_key_params); 45 49 } 46 50 47 51 void *symtab_search(struct symtab *s, const char *name) 48 52 { 49 - return hashtab_search(&s->table, name); 53 + return hashtab_search(&s->table, name, symtab_key_params); 50 54 }