// SPDX-License-Identifier: GPL-2.0 /* * Implementation of the SID table type. * * Original author: Stephen Smalley, <stephen.smalley.work@gmail.com> * Author: Ondrej Mosnacek, <omosnacek@gmail.com> * * Copyright (C) 2018 Red Hat, Inc.
*/
/* * Multiple initial sids may map to the same context. Check that this * context is not already represented in the context_to_sid hashtable * to avoid duplicate entries and long linked lists upon hash * collision.
*/ if (!context_to_sid(s, context, hash)) {
isid->entry.sid = sid;
isid->entry.hash = hash;
hash_add(s->context_to_sid, &isid->entry.list, hash);
}
return 0;
}
int sidtab_hash_stats(struct sidtab *sidtab, char *page)
{ unsignedint i; int chain_len = 0; int slots_used = 0; int entries = 0; int max_chain_len = 0; unsignedint cur_bucket = 0; struct sidtab_entry *entry;
rcu_read_lock();
hash_for_each_rcu(sidtab->context_to_sid, i, entry, list) {
entries++; if (i == cur_bucket) {
chain_len++; if (chain_len == 1)
slots_used++;
} else {
cur_bucket = i; if (chain_len > max_chain_len)
max_chain_len = chain_len;
chain_len = 0;
}
}
rcu_read_unlock();
if (chain_len > max_chain_len)
max_chain_len = chain_len;
if (unlikely(s->frozen)) { /* * This sidtab is now frozen - tell the caller to abort and * get the new one.
*/
rc = -ESTALE; goto out_unlock;
}
count = s->count;
/* bail out if we already reached max entries */
rc = -EOVERFLOW; if (count >= SIDTAB_MAX) goto out_unlock;
/* insert context into new entry */
rc = -ENOMEM;
dst = sidtab_do_lookup(s, count, 1); if (!dst) goto out_unlock;
dst->sid = index_to_sid(count);
dst->hash = hash;
rc = context_cpy(&dst->context, context); if (rc) goto out_unlock;
/* * if we are building a new sidtab, we need to convert the context * and insert it there as well
*/
convert = s->convert; if (convert) { struct sidtab *target = convert->target;
/* allocate last leaf in the new sidtab (to avoid race with * live convert)
*/
rc = sidtab_do_lookup(params->target, count - 1, 1) ? 0 : -ENOMEM; if (rc) {
spin_unlock_irqrestore(&s->lock, flags); return rc;
}
/* set count in case no new entries are added during conversion */
params->target->count = count;
/* enable live convert of new entries */
s->convert = params;
/* we can safely convert the tree outside the lock */
spin_unlock_irqrestore(&s->lock, flags);
/* convert all entries not covered by live convert */
pos = 0;
rc = sidtab_convert_tree(¶ms->target->roots[level],
&s->roots[level], &pos, count, level, params); if (rc) { /* we need to keep the old table - disable live convert */
spin_lock_irqsave(&s->lock, flags);
s->convert = NULL;
spin_unlock_irqrestore(&s->lock, flags); return rc;
} /* * The hashtable can also be modified in sidtab_context_to_sid() * so we must re-acquire the lock here.
*/
spin_lock_irqsave(&s->lock, flags);
sidtab_convert_hashtable(params->target, count);
spin_unlock_irqrestore(&s->lock, flags);
if (level != 0) { struct sidtab_node_inner *node = entry.ptr_inner;
if (!node) return;
for (i = 0; i < SIDTAB_INNER_ENTRIES; i++)
sidtab_destroy_tree(node->entries[i], level - 1);
kfree(node);
} else { struct sidtab_node_leaf *node = entry.ptr_leaf;
if (!node) return;
for (i = 0; i < SIDTAB_LEAF_ENTRIES; i++)
sidtab_destroy_entry(&node->entries[i]);
kfree(node);
}
}
void sidtab_destroy(struct sidtab *s)
{
u32 i, level;
for (i = 0; i < SECINITSID_NUM; i++) if (s->isids[i].set)
sidtab_destroy_entry(&s->isids[i].entry);
level = SIDTAB_MAX_LEVEL; while (level && !s->roots[level].ptr_inner)
--level;
sidtab_destroy_tree(s->roots[level], level); /* * The context_to_sid hashtable's objects are all shared * with the isids array and context tree, and so don't need * to be cleaned up here.
*/
}
/* do not cache invalid contexts */ if (entry->context.len) return;
spin_lock_irqsave(&s->cache_lock, flags);
cache = rcu_dereference_protected(entry->cache,
lockdep_is_held(&s->cache_lock)); if (cache) { /* entry in cache - just bump to the head of LRU list */
list_move(&cache->lru_member, &s->cache_lru_list); goto out_unlock;
}
cache = kmalloc(struct_size(cache, str, str_len), GFP_ATOMIC); if (!cache) goto out_unlock;
if (s->cache_free_slots == 0) { /* pop a cache entry from the tail and free it */
victim = container_of(s->cache_lru_list.prev, struct sidtab_str_cache, lru_member);
list_del(&victim->lru_member);
rcu_assign_pointer(victim->parent->cache, NULL);
} else {
s->cache_free_slots--;
}
cache->parent = entry;
cache->len = str_len;
memcpy(cache->str, str, str_len);
list_add(&cache->lru_member, &s->cache_lru_list);
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.