/* -*- mode:c; c-file-style:"k&r"; c-basic-offset: 4; tab-width:4; indent-tabs-mode:nil; mode:auto-fill; fill-column:78; -*- */ /* vim: set ts=4 sw=4 et tw=78 fo=cqt wm=0: */ /* Copyright (C) 2014 OSCAR lab, Stony Brook University This file is part of Graphene Library OS. Graphene Library OS is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Graphene Library OS is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ /* * shim_dcache.c * * This file contains codes for maintaining directory cache in library OS. * The source codes are imported from Linux kernel, but simplified according * to the characteristic of library OS. */ #include #include #include #include #include #include static LIST_HEAD(dcache_list); static LIST_HEAD(unused); static LIST_HEAD(persistent); static struct hlist_head dcache_htable[DCACHE_HASH_SIZE] = { HLIST_HEAD_INIT }; LOCKTYPE dcache_lock; struct shim_dcache_stats { long memsize; long nentries; }; static struct shim_dcache_stats dcache_stats; long get_dcache_stats (const char * name) { if (strcmp_static(name, "memsize")) return dcache_stats.memsize; if (strcmp_static(name, "nentries")) return dcache_stats.nentries; return 0; } #define DCACHE_MGR_ALLOC 64 #define PAGE_SIZE allocsize #define OBJ_TYPE struct shim_dentry #include static MEM_MGR dentry_mgr = NULL; struct shim_dentry * dentry_root = NULL; //#define DEBUG_DCACHE //#define DEBUG_REF static struct shim_dentry * alloc_dentry (void) { struct shim_dentry * dent = get_mem_obj_from_mgr_enlarge(dentry_mgr, size_align_up(DCACHE_MGR_ALLOC)); if (!dent) return NULL; dcache_stats.memsize += sizeof(struct shim_dentry); dcache_stats.nentries++; memset(dent, 0, sizeof(struct shim_dentry)); dent->mode = NO_MODE; INIT_HLIST_NODE(&dent->hlist); INIT_LIST_HEAD(&dent->list); INIT_LIST_HEAD(&dent->children); INIT_LIST_HEAD(&dent->siblings); return dent; } DEFINE_PROFILE_CATAGORY(dcache, ); DEFINE_PROFILE_INTERVAL(total_init_dcache, dcache); DEFINE_PROFILE_CATAGORY(within_init_dcache, dcache); DEFINE_PROFILE_INTERVAL(dcache_init_memory, within_init_dcache); DEFINE_PROFILE_INTERVAL(dcache_init_lock, within_init_dcache); DEFINE_PROFILE_INTERVAL(dcache_init_root_entry, within_init_dcache); int init_dcache (void) { #ifdef PROFILE unsigned long begin_time = GET_PROFILE_INTERVAL(); BEGIN_PROFILE_INTERVAL_SET(begin_time); #endif dentry_mgr = create_mem_mgr(init_align_up(DCACHE_MGR_ALLOC)); SAVE_PROFILE_INTERVAL(dcache_init_memory); create_lock(dcache_lock); SAVE_PROFILE_INTERVAL(dcache_init_lock); dentry_root = alloc_dentry(); qstrsetstr(&dentry_root->rel_path, "", 0); qstrsetstr(&dentry_root->name, "", 0); get_dentry(dentry_root); SAVE_PROFILE_INTERVAL(dcache_init_root_entry); SAVE_PROFILE_INTERVAL_SINCE(total_init_dcache, begin_time); return 0; } int reinit_dcache (void) { create_lock(dcache_lock); return 0; } /* remove from the hash table, so that a lookup will fail. */ void __del_dcache (struct shim_dentry * dent) { if (!(dent->state & DENTRY_HASHED)) return; dent->state &= ~DENTRY_HASHED; hlist_del_init(&dent->hlist); #ifdef DEBUG_DCACHE debug("del dcache %p(%s/%s) (mount = %p)\n", dent, dent->fs ? qstrgetstr(&dent->fs->path) : "", qstrgetstr(&dent->rel_path), dent->fs); #endif } static int __internal_put_dentry (struct shim_dentry * dent); void del_dcache (struct shim_dentry * dent) { lock(dcache_lock); __del_dcache(dent); unlock(dcache_lock); } static inline void __dput_dentry (struct shim_dentry * dent) { while (1) { /* if the dentry is never in the hash table, we are happy to drop it */ if (!(dent->state & DENTRY_HASHED)) goto kill; /* move the node to unused list unless it is persistent */ if (!(dent->state & DENTRY_PERSIST)) { dent->state |= DENTRY_RECENTLY; list_del(&dent->list); INIT_LIST_HEAD(&dent->list); list_add(&dent->list, &unused); } /* we don't delete the dentry from dcache because it might be acquired and used again, unless it gets recycled due to memory pressure */ break; kill: { if (dent->fs && dent->fs->d_ops && dent->fs->d_ops->dput) dent->fs->d_ops->dput(dent); struct shim_dentry * parent = dent->parent; if (!parent) break; list_del(&dent->siblings); /* remove from parent's list of children */ dent->parent = NULL; dent = parent; if (__internal_put_dentry(dent)) break; } } } static int __internal_put_dentry (struct shim_dentry * dent) { int count = REF_DEC(dent->ref_count); #ifdef DEBUG_REF debug("put dentry %p(%s/%s) (ref_count = %d)\n", dent, dent->fs ? qstrgetstr(&dent->fs->path) : "", qstrgetstr(&dent->rel_path), count); #endif return count; } void get_dentry (struct shim_dentry * dent) { #ifdef DEBUG_REF int count = REF_INC(dent->ref_count); debug("get dentry %p(%s/%s) (ref_count = %d)\n", dent, dent->fs ? qstrgetstr(&dent->fs->path) : "", qstrgetstr(&dent->rel_path), count); #else REF_INC(dent->ref_count); #endif } void put_dentry (struct shim_dentry * dent) { if (__internal_put_dentry(dent)) return; if (locked(dcache_lock)) { __dput_dentry(dent); } else { lock(dcache_lock); __dput_dentry(dent); unlock(dcache_lock); } } struct shim_dentry * get_new_dentry (struct shim_dentry * parent, const char * name, int namelen) { struct shim_dentry * dent = alloc_dentry(); if (!dent) return NULL; REF_SET(dent->ref_count, 0); qstrsetstr(&dent->name, name, namelen); if (!parent) { qstrsetstr(&dent->rel_path, name, namelen); return dent; } if (!qstrempty(&parent->rel_path)) { const char * strs[] = { qstrgetstr(&parent->rel_path), "/", name }; size_t lens[] = { parent->rel_path.len, 1, namelen }; qstrsetstrs(&dent->rel_path, 3, strs, lens); } else qstrsetstr(&dent->rel_path, name, namelen); return dent; } void __set_parent_dentry (struct shim_dentry * child, struct shim_dentry * parent) { if (child->parent == parent) return; assert(!child->parent); get_dentry(parent); list_add_tail(&child->siblings, &parent->children); child->parent = parent; parent->nchildren++; } void __unset_parent_dentry (struct shim_dentry * child, struct shim_dentry * parent) { if (child->parent != parent) return; assert(child->parent); child->parent = NULL; list_del_init(&child->siblings); parent->nchildren--; put_dentry(parent); } static inline HASHTYPE hash_dentry (struct shim_dentry * start, const char * path, int len) { return rehash_path(start ? start->rel_path.hash : 0, path, len, NULL); } void __add_dcache (struct shim_dentry * dent, HASHTYPE * hashptr) { struct hlist_head * head; if (hashptr) { dent->rel_path.hash = *hashptr; goto add_hash; } if (!dent->parent) { dent->rel_path.hash = dent->fs ? dent->fs->path.hash : 0; goto add_hash; } dent->rel_path.hash = hash_dentry(dent->parent, dentry_get_name(dent), dent->name.len); add_hash: head = &dcache_htable[DCACHE_HASH(dent->rel_path.hash)]; hlist_add_head(&dent->hlist, head); dent->state |= DENTRY_HASHED; #ifdef DEBUG_DCACHE debug("add dcache %p(%s/%s) (mount = %p)\n", dent, dent->fs ? qstrgetstr(&dent->fs->path) : "", qstrgetstr(&dent->rel_path), dent->fs); #endif } void add_dcache (struct shim_dentry * dent, HASHTYPE * hashptr) { lock(dcache_lock); __add_dcache(dent, hashptr); unlock(dcache_lock); } struct shim_dentry * __lookup_dcache (struct shim_dentry * start, const char * name, int namelen, const char * path, int pathlen, HASHTYPE * hashptr) { HASHTYPE hash = hash_dentry(start, name, namelen); struct shim_dentry * dent, * found = NULL; struct hlist_node * node; struct hlist_head * head = &dcache_htable[DCACHE_HASH(hash)]; /* walk through all the nodes in the hash bucket, find the droids we're looking for */ hlist_for_each_entry(dent, node, head, hlist) { if ((dent->state & DENTRY_MOUNTPOINT) || dent->rel_path.hash != hash) continue; /* first we compare the filename */ const char * filename = get_file_name(name, namelen); if (memcmp(dentry_get_name(dent), filename, name + namelen - filename)) continue; if (filename == name) { struct shim_dentry * d = dent; while (d && !d->parent && d->fs) d = d->fs->mount_point; if (d && d->parent && d->parent != start) continue; } if (path && pathlen && filename != path) { const char * fullpath; int fullpathlen; fullpath = dentry_get_path(dent, true, &fullpathlen); if (pathlen > fullpathlen) continue; fullpath += fullpathlen - pathlen; if (fullpath[-1] != '/') continue; if (memcmp(fullpath, path, pathlen)) continue; } get_dentry(dent); found = dent; break; } if (hashptr) *hashptr = hash; return found; } /* after lookup_dcache, the dentry is popped to prevent recycling */ struct shim_dentry * lookup_dcache (struct shim_dentry * start, const char * name, int namelen, const char * path, int pathlen, HASHTYPE * hashptr) { lock(dcache_lock); struct shim_dentry * dent = __lookup_dcache(start, name, namelen, path, pathlen, hashptr); unlock(dcache_lock); return dent; } int __del_dentry_tree (struct shim_dentry * root) { struct shim_dentry * this_parent = root; struct list_head * next; repeat: next = this_parent->children.next; resume: while (next != &this_parent->children) { struct list_head * tmp = next; struct shim_dentry * d = list_entry(tmp, struct shim_dentry, siblings); next = tmp->next; if (d->state & DENTRY_MOUNTPOINT) { this_parent = d->mounted->root; goto repeat; } if (!list_empty(&d->children)) { this_parent = d; goto repeat; } __unset_parent_dentry(d, this_parent); __del_dcache(d); } if (this_parent != root) { struct shim_dentry * child = this_parent; if (!this_parent->parent) { this_parent = this_parent->fs->mount_point; __del_dcache(child); child = this_parent; } this_parent = this_parent->parent; next = child->siblings.next; __del_dcache(child); __unset_parent_dentry(child, this_parent); goto resume; } return 0; } BEGIN_CP_FUNC(dentry) { assert(size == sizeof(struct shim_dentry)); struct shim_dentry * dent = (struct shim_dentry *) obj; struct shim_dentry * new_dent = NULL; ptr_t off = GET_FROM_CP_MAP(obj); if (!off) { off = ADD_CP_OFFSET(sizeof(struct shim_dentry)); ADD_TO_CP_MAP(obj, off); new_dent = (struct shim_dentry *) (base + off); lock(dent->lock); *new_dent = *dent; INIT_HLIST_NODE(&new_dent->hlist); INIT_LIST_HEAD(&new_dent->list); INIT_LIST_HEAD(&new_dent->children); INIT_LIST_HEAD(&new_dent->siblings); new_dent->data = NULL; clear_lock(new_dent->lock); REF_SET(new_dent->ref_count, 0); DO_CP_IN_MEMBER(qstr, new_dent, rel_path); DO_CP_IN_MEMBER(qstr, new_dent, name); if (dent->fs) DO_CP_MEMBER(mount, dent, new_dent, fs); if (dent->parent) DO_CP_MEMBER(dentry, dent, new_dent, parent); if (dent->mounted) DO_CP_MEMBER(mount, dent, new_dent, mounted); unlock(dent->lock); ADD_CP_FUNC_ENTRY(off); } else { new_dent = (struct shim_dentry *) (base + off); } if (objp) *objp = (void *) new_dent; } END_CP_FUNC(dentry) BEGIN_RS_FUNC(dentry) { struct shim_dentry * dent = (void *) (base + GET_CP_FUNC_ENTRY()); BEGIN_PROFILE_INTERVAL(); CP_REBASE(dent->hlist); CP_REBASE(dent->list); CP_REBASE(dent->children); CP_REBASE(dent->siblings); CP_REBASE(dent->fs); CP_REBASE(dent->parent); CP_REBASE(dent->mounted); create_lock(dent->lock); if (dent->parent) __set_parent_dentry(dent, dent->parent); struct hlist_head * head = &dcache_htable[DCACHE_HASH(dent->rel_path.hash)]; hlist_add_head(&dent->hlist, head); dent->state |= DENTRY_HASHED; DEBUG_RS("hash=%08x,path=%s,fs=%s", dent->rel_path.hash, dentry_get_path(dent, true, NULL), dent->fs ? qstrgetstr(&dent->fs->path) : NULL); } END_RS_FUNC(dentry)