/* inode->vnode table */ struct vfs_inotab; struct vfs_inoent { LIST_ENTRY(vfs_inoent) ie_entry; }; struct vfs_inotab * vfs_inotab_create(ptrdiff_t, ptrdiff_t); void vfs_inotab_destroy(struct vfs_inode_table *); struct vnode * vfs_inotab_get(struct vfs_inotab *, ino_t, int); struct vnode * vfs_inotab_put(struct vfs_inotab *, ino_t, struct vfs_inoent *, int); void vfs_inotab_remove(struct vfs_inotab *, struct vfs_inoent *); /* * 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 data structure. */ LIST_HEAD(vfs_inohead, vfs_inoent); struct vfs_inotab { kmutex_t it_lock; pserialize_t it_psz; struct vfs_inohashtab { struct vfs_inohead *ih_buckets; unsigned long ih_mask; } *it_hashtab; ptrdiff_t it_vnoff; ptrdiff_t it_inoff; }; static ino_t vfs_inoent_ino(struct vfs_inotab *inotab, const struct vfs_inoent *inoent) { const char *const p = (const void *)inoent; return *(const ino_t *)(p + inotab->it_inoff); } static struct vnode * vfs_inoent_vp(struct vfs_inotab *inotab, const struct vfs_inoent *inoent) { const char *const p = (const void *)inoent; return *(struct vnode *const *)(p + inotab->it_vnoff); } static size_t vfs_inohash(struct vfs_inohashtab *inohash, ino_t ino) { return (ino & inohash->ih_mask); } struct vfs_inotab * vfs_inotab_create(ptrdiff_t vnoff, ptrdiff_t inoff) { struct vfs_inotab *const inotab = kmem_alloc(sizeof(*inotab), KM_SLEEP); mutex_init(&inotab->it_lock, MUTEX_DEFAULT, IPL_NONE); inotab->it_psz = pserialize_create(); inotab->it_hash = kmem_alloc(sizeof(*inotab->it_hash), KM_SLEEP); /* XXX desiredvnodes strikes me as overkill for one fs. */ inotab->it_hash->ih_buckets = hashinit(desiredvnodes, HASH_LIST, true, &inotab->it_hash->ih_mask); inotab->it_vnoff = vnoff; inotab->it_inoff = inoff; } void vfs_inotab_reinit(struct vfs_inotab *inotab) { struct vfs_inohashtab *oldhashtab, *newhashtab; struct vfs_inohead *oldbuckets, *newbuckets; struct vfs_inoentry *inoent, *next; size_t hash; size_t i; newhashtab = kmem_alloc(sizeof(*newhashtab), KM_SLEEP); newhashtab->ih_buckets = hashinit(desiredvnodes, HASH_LIST, true, &newhashtab->ih_mask); newbuckets = newhashtab->ih_buckets; mutex_enter(&inotab->it_lock); oldhashtab = inotab->it_hash; oldbuckets = oldhashtab->ih_buckets; for (i = 0; i <= oldhashtab->ih_mask; i++) { LIST_FOREACH_SAFE(inoent, &oldbuckets[i], ie_entry, next) { LIST_REMOVE(inoent, ie_entry); pserialize_perform(inotab->it_psz); hash = vfs_inohash(inotab, vfs_inoent_ino(inotab, inoent)); LIST_INSERT_HEAD(&newbuckets[hash], inoent, ie_entry); } } membar_producer(); inotab->it_hash = newhashtab; pserialize_perform(inotab->it_psz); mutex_exit(&inotab->it_lock); hashdone(oldbuckets, HASH_LIST, oldhashtab->ih_mask); kmem_free(oldhashtab, sizeof(*oldhashtab)); } void vfs_inotab_destroy(struct vfs_inotab *inotab) { #if DIAGNOSTIC { size_t i; for (i = 0; i <= inotab->it_hashmask; i++) KASSERT(LIST_EMPTY(&inotab->it_buckets[i])); } #endif hashdone(inotab->it_hashtab->ih_buckets, HASH_LIST, inotab->it_hashtab->ih_mask); kmem_free(inotab->it_hashtab, sizeof(*inotab->it_hashtab)); pserialize_destroy(inotab->it_psz); mutex_destroy(&inotab->it_lock); kmem_free(inotab, sizeof(*inotab)); } struct vnode * vfs_inotab_get(struct vfs_inotab *inotab, ino_t ino, int flags) { struct vfs_inohashtab *hashtab; struct vfs_inohead *head; struct vfs_inoent *inoent; int s, error; s = pserialize_read_enter(); hashtab = inotab->it_hashtab; membar_consumer(); /* XXX membar_datadep_consumer */ head = &hashtab->ih_buckets[vfs_inohash(hashtab, ino)]; LIST_FOREACH(inoent, head, ie_entry) { membar_consumer(); /* XXX membar_datadep_consumer */ if (ino == vfs_inoent_ino(inotab, inoent)) { struct vnode *const vp = vfs_inoent_vp(inotab, inoent); membar_consumer(); /* XXX membar_datadep_consumer */ pserialize_read_exit(s); mutex_enter(vp->v_interlock); error = vget(vp, flags); mutex_exit(vp->v_interlock); if (error) return NULL; return vp; } } pserialize_read_exit(s); return NULL; } struct vnode * vfs_inotab_put(struct vfs_inotab *inotab, struct vfs_inoent *new_inoent, int flags) { const ino_t ino = vfs_inoent_ino(inotab, new_inoent); struct vfs_inohashtab *hashtab; struct vfs_inohead *head; struct vfs_inoent *inoent; struct vnode *vp; int error; mutex_enter(&inotab->it_lock); hashtab = inotab->it_hashtab; head = &hashtab->ih_buckets[vfs_inohash(hashtab, ino)]; LIST_FOREACH(inoent, head, ie_entry) { if (vfs_inoent_ino(inotab, inoent) == ino) { vp = vfs_inoent_vp(inotab, inoent); mutex_enter(vp->v_interlock); mutex_exit(&inotab->it_lock); error = vget(vp, flags); mutex_exit(vp->v_interlock); if (error) return NULL; return vp; } } vp = vfs_inoent_vp(inotab, inoent); vnode_ready(vp); membar_producer(); LIST_INSERT_HEAD(head, new_inoent, ie_entry); mutex_exit(&inotab->it_lock); return vp; } void vfs_inotab_remove(struct vfs_inotab *inotab, struct vfs_inoent *inoent) { mutex_enter(&inotab->it_lock); LIST_REMOVE(inoent, ie_entry); pserialize_perform(inotab->it_psz); mutex_exit(&inotab->it_lock); }