/* $NetBSD$ */ /*- * Copyright (c) 2014 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Taylor R Campbell. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /* * XXX NOTE NOTE NOTE XXX * * This code does not actually work. It is a draft of an idea. It * probably won't even compile, even if you make it include the right * header files. */ /* inode->vnode table */ /* * Hash tables are a bad idea, but they're what we've been using all * along, so we might as well take advantage of them for pserialized * access until we get a better pserializable data structure. */ LIST_HEAD(vinohead, vinoent); struct vinotab { kmutex_t vit_lock; kcondvar_t vit_cv; pserialize_t vit_psz; struct vinohashtab { struct vinohead *vih_buckets; unsigned long vih_mask; } *vit_hashtab; uint8_t vit_seed[SIPHASH_KEYBYTES]; }; static size_t vinohash(struct vinotab *vinotab, struct vinohashtab *hashtab, const void *key, unsigned int keysize) { return siphash_2_4(vinotab->vit_seed, key, keysize) & hashtab->vit_mask; } struct vinotab * vinotab_create(void) { struct vinotab *const vinotab = kmem_alloc(sizeof(*vinotab), KM_SLEEP); mutex_init(&vinotab->vit_lock, MUTEX_DEFAULT, IPL_NONE); cv_init(&vinotab->vit_cv, "virehash"); vinotab->vit_psz = pserialize_create(); vinotab->vit_hashtab = kmem_alloc(sizeof(*vinotab->vit_hashtab), KM_SLEEP); /* XXX desiredvnodes strikes me as overkill for one fs. */ vinotab->vit_hashtab->vih_buckets = hashinit(desiredvnodes, HASH_LIST, true, &vinotab->vit_hashtab->vih_mask); cprng_strong(kern_cprng, vinotab->vit_seed, sizeof(vinotab->vit_seed), 0); } void vinotab_destroy(struct vinotab *vinotab) { KASSERT(vinotab->vit_hashtab != NULL); #if DIAGNOSTIC { size_t i; for (i = 0; i <= vinotab->vit_hashtab->vih_mask; i++) KASSERT(LIST_EMPTY(&inotab->vit_hashtab->vih_buckets[i])); } #endif (void)explicit_memset(vinotab->vit_seed, 0, sizeof(vinotab->vit_seed)); hashdone(vinotab->vit_hashtab->vih_buckets, HASH_LIST, vinotab->vit_hashtab->vih_mask); kmem_free(vinotab->vit_hashtab, sizeof(*vinotab->vit_hashtab)); pserialize_destroy(vinotab->vit_psz); cv_destroy(&vinotab->vit_cv); mutex_destroy(&vinotab->vit_lock); kmem_free(vinotab, sizeof(*vinotab)); } void vinotab_reinit(struct vinotab *vinotab) { struct vinohashtab *oldhashtab, *newhashtab; struct vinohead *oldbuckets, *newbuckets; struct vinoent *vinoent, *next; size_t hash; size_t i; newhashtab = kmem_alloc(sizeof(*newhashtab), KM_SLEEP); newhashtab->vih_buckets = hashinit(desiredvnodes, HASH_LIST, true, &newhashtab->vih_mask); newbuckets = newhashtab->vih_buckets; /* Detach the old hash table. */ mutex_enter(&vinotab->vit_lock); (void)vinotab_wait(vinotab, 0); oldhashtab = vinotab->vit_hashtab; KASSERT(oldhashtab != NULL); oldbuckets = oldhashtab->vih_buckets; vinotab->vit_hashtab = NULL; pserialize_perform(vinotab->vit_psz); mutex_exit(&vinotab->vit_lock); /* Rehash it. */ for (i = 0; i <= oldhashtab->vih_mask; i++) { LIST_FOREACH_SAFE(vinoent, &oldbuckets[i], vie_entry, next) { LIST_REMOVE(vinoent, vie_entry); hash = vinohash(vinotab, newhashtab, vinoent->vie_key); LIST_INSERT_HEAD(&newbuckets[hash], vinoent, vie_entry); } } /* Attach the new hash table. */ mutex_enter(&vinotab->vit_lock); KASSERT(vinotab->vit_hashtab == NULL); membar_producer(); vinotab->vit_hashtab = newhashtab; cv_broadcast(&vinotab->vit_cv, &vinotab->vit_lock); mutex_exit(&vinotab->vit_lock); hashdone(oldbuckets, HASH_LIST, oldhashtab->vih_mask); kmem_free(oldhashtab, sizeof(*oldhashtab)); } static int vinotab_wait(struct vinotab *vinotab, int flags) { int error = 0; KASSERT(mutex_owned(&vinotab->vit_lock)); while (vinotab->vit_hashtab == NULL) { if (flags == VGET_NONBLOCK) { return EWOULDBLOCK; } else if (flags == VGET_INTR) { error = cv_wait_sig(&vinotab->vit_cv, &vinotab->vit_lock); if (error) return error; } else { cv_wait(&vinotab->vit_cv, &vinotab->vit_lock); } } return 0; } int vinotab_get(struct vinotab *vinotab, const void *key, unsigned int keysize, int flags, struct vnode **vpp) { struct vinohashtab *hashtab; struct vinohead *head; struct vinoent *vinoent; struct vnode *vp = NULL; int s, error; s = pserialize_read_enter(); /* Get the current hash table. Wait if someone's rehashing. */ hashtab = vinotab->vit_hashtab; if (hashtab == NULL) { pserialize_read_exit(s); mutex_enter(&vinotab->vit_lock); error = vinotab_wait(vinotab); if (error) { mutex_exit(&vinotab->vit_lock); return error; } hashtab = vinotab->vit_hashtab; KASSERT(hashtab != NULL); s = pserialize_read_enter(s); mutex_exit(&vinotab->vit_lock); } else { /* * If it's not being rehashed, wait for its guts to * make their way over to this CPU. */ membar_consumer(); /* XXX membar_datadep_consumer */ } head = &hashtab->vih_buckets[vinohash(vinotab, hashtab, key)]; LIST_FOREACH(vinoent, head, vie_entry) { membar_consumer(); /* XXX membar_datadep_consumer */ if (keysize != vinotab->vit_keysize) continue; if (memcmp(key, vinoent->vie_key, keysize) != 0) continue; vp = vinoent->vie_vnode; vpreget(vp); pserialize_read_exit(s); error = vget(vp, flags); if (error) goto fail; *vpp = vp; return 0; } pserialize_read_exit(s); error = ENOENT; fail: KASSERT(error); *vpp = NULL; return error; } int vinotab_intern(struct vinotab *inotab, struct vinoent *new_vinoent, const void *key, unsigned int keysize, struct vnode *vp, int flags, struct vnode **vpp) { struct vinohashtab *hashtab; struct vinohead *head; struct vinoent *vinoent; struct vnode *vp0; int error; retry: mutex_enter(&vinotab->vit_lock); error = vinotab_wait(vinotab, flags); if (error) { mutex_exit(&vinotab->vit_lock); vp0 = NULL; goto out; } hashtab = vinotab->vit_hashtab; head = &hashtab->vih_buckets[vinohash(vinotab, hashtab, key)]; LIST_FOREACH(vinoent, head, vie_entry) { if (keysize != vinoent->vie_keysize) continue; if (memcmp(key, vinoent->vie_key, keysize) != 0) continue; vp0 = vinoent->vie_vnode; vpreget(vp0); mutex_exit(&inotab->vit_lock); error = vget(vp0, flags); if (error) { if (error == ENOENT) goto retry; vp0 = NULL; goto out; } goto out; } vp0 = new_vinoent->vie_vnode = vp; membar_producer(); LIST_INSERT_HEAD(head, new_vinoent, vie_entry); mutex_exit(&vinotab->vit_lock); error = 0; out: *vpp = vp0; return error; } void vinotab_remove(struct vinotab *vinotab, struct vinoent *vinoent) { mutex_enter(&vinotab->vit_lock); LIST_REMOVE(vinoent, vie_entry); pserialize_perform(vinotab->vit_psz); mutex_exit(&vinotab->vit_lock); }