📄 dcache.c
字号:
int have_submounts(struct dentry *parent){ struct dentry *this_parent = parent; struct list_head *next; spin_lock(&dcache_lock); if (d_mountpoint(parent)) goto positive;repeat: next = this_parent->d_subdirs.next;resume: while (next != &this_parent->d_subdirs) { struct list_head *tmp = next; struct dentry *dentry = list_entry(tmp, struct dentry, d_child); next = tmp->next; /* Have we found a mount point ? */ if (d_mountpoint(dentry)) goto positive; if (!list_empty(&dentry->d_subdirs)) { this_parent = dentry; goto repeat; } } /* * All done at this level ... ascend and resume the search. */ if (this_parent != parent) { next = this_parent->d_child.next; this_parent = this_parent->d_parent; goto resume; } spin_unlock(&dcache_lock); return 0; /* No mount points found in tree */positive: spin_unlock(&dcache_lock); return 1;}/* * Search the dentry child list for the specified parent, * and move any unused dentries to the end of the unused * list for prune_dcache(). We descend to the next level * whenever the d_subdirs list is non-empty and continue * searching. */static int select_parent(struct dentry * parent){ struct dentry *this_parent = parent; struct list_head *next; int found = 0; spin_lock(&dcache_lock);repeat: next = this_parent->d_subdirs.next;resume: while (next != &this_parent->d_subdirs) { struct list_head *tmp = next; struct dentry *dentry = list_entry(tmp, struct dentry, d_child); next = tmp->next; if (!atomic_read(&dentry->d_count)) { list_del(&dentry->d_lru); list_add(&dentry->d_lru, dentry_unused.prev); found++; } /* * Descend a level if the d_subdirs list is non-empty. */ if (!list_empty(&dentry->d_subdirs)) { this_parent = dentry;#ifdef DCACHE_DEBUGprintk(KERN_DEBUG "select_parent: descending to %s/%s, found=%d\n",dentry->d_parent->d_name.name, dentry->d_name.name, found);#endif goto repeat; } } /* * All done at this level ... ascend and resume the search. */ if (this_parent != parent) { next = this_parent->d_child.next; this_parent = this_parent->d_parent;#ifdef DCACHE_DEBUGprintk(KERN_DEBUG "select_parent: ascending to %s/%s, found=%d\n",this_parent->d_parent->d_name.name, this_parent->d_name.name, found);#endif goto resume; } spin_unlock(&dcache_lock); return found;}/** * shrink_dcache_parent - prune dcache * @parent: parent of entries to prune * * Prune the dcache to remove unused children of the parent dentry. */ void shrink_dcache_parent(struct dentry * parent){ int found; while ((found = select_parent(parent)) != 0) prune_dcache(found);}/* * This is called from kswapd when we think we need some * more memory, but aren't really sure how much. So we * carefully try to free a _bit_ of our dcache, but not * too much. * * Priority: * 0 - very urgent: shrink everything * ... * 6 - base-level: try to shrink a bit. */int shrink_dcache_memory(int priority, unsigned int gfp_mask){ int count = 0; /* * Nasty deadlock avoidance. * * ext2_new_block->getblk->GFP->shrink_dcache_memory->prune_dcache-> * prune_one_dentry->dput->dentry_iput->iput->inode->i_sb->s_op-> * put_inode->ext2_discard_prealloc->ext2_free_blocks->lock_super-> * DEADLOCK. * * We should make sure we don't hold the superblock lock over * block allocations, but for now: */ if (!(gfp_mask & __GFP_FS)) return 0; count = dentry_stat.nr_unused / priority; prune_dcache(count); kmem_cache_shrink(dentry_cache); return 0;}#define NAME_ALLOC_LEN(len) ((len+16) & ~15)/** * d_alloc - allocate a dcache entry * @parent: parent of entry to allocate * @name: qstr of the name * * Allocates a dentry. It returns %NULL if there is insufficient memory * available. On a success the dentry is returned. The name passed in is * copied and the copy passed in may be reused after this call. */ struct dentry * d_alloc(struct dentry * parent, const struct qstr *name){ char * str; struct dentry *dentry; dentry = kmem_cache_alloc(dentry_cache, GFP_KERNEL); if (!dentry) return NULL; if (name->len > DNAME_INLINE_LEN-1) { str = kmalloc(NAME_ALLOC_LEN(name->len), GFP_KERNEL); if (!str) { kmem_cache_free(dentry_cache, dentry); return NULL; } } else str = dentry->d_iname; memcpy(str, name->name, name->len); str[name->len] = 0; atomic_set(&dentry->d_count, 1); dentry->d_vfs_flags = 0; dentry->d_flags = 0; dentry->d_inode = NULL; dentry->d_parent = NULL; dentry->d_sb = NULL; dentry->d_name.name = str; dentry->d_name.len = name->len; dentry->d_name.hash = name->hash; dentry->d_op = NULL; dentry->d_fsdata = NULL; dentry->d_mounted = 0; INIT_LIST_HEAD(&dentry->d_hash); INIT_LIST_HEAD(&dentry->d_lru); INIT_LIST_HEAD(&dentry->d_subdirs); INIT_LIST_HEAD(&dentry->d_alias); if (parent) { dentry->d_parent = dget(parent); dentry->d_sb = parent->d_sb; spin_lock(&dcache_lock); list_add(&dentry->d_child, &parent->d_subdirs); spin_unlock(&dcache_lock); } else INIT_LIST_HEAD(&dentry->d_child); dentry_stat.nr_dentry++; return dentry;}/** * d_instantiate - fill in inode information for a dentry * @entry: dentry to complete * @inode: inode to attach to this dentry * * Fill in inode information in the entry. * * This turns negative dentries into productive full members * of society. * * NOTE! This assumes that the inode count has been incremented * (or otherwise set) by the caller to indicate that it is now * in use by the dcache. */ void d_instantiate(struct dentry *entry, struct inode * inode){ if (!list_empty(&entry->d_alias)) BUG(); spin_lock(&dcache_lock); if (inode) list_add(&entry->d_alias, &inode->i_dentry); entry->d_inode = inode; spin_unlock(&dcache_lock);}/** * d_alloc_root - allocate root dentry * @root_inode: inode to allocate the root for * * Allocate a root ("/") dentry for the inode given. The inode is * instantiated and returned. %NULL is returned if there is insufficient * memory or the inode passed is %NULL. */ struct dentry * d_alloc_root(struct inode * root_inode){ struct dentry *res = NULL; if (root_inode) { res = d_alloc(NULL, &(const struct qstr) { "/", 1, 0 }); if (res) { res->d_sb = root_inode->i_sb; res->d_parent = res; d_instantiate(res, root_inode); } } return res;}static inline struct list_head * d_hash(struct dentry * parent, unsigned long hash){ hash += (unsigned long) parent / L1_CACHE_BYTES; hash = hash ^ (hash >> D_HASHBITS); return dentry_hashtable + (hash & D_HASHMASK);}/** * d_lookup - search for a dentry * @parent: parent dentry * @name: qstr of name we wish to find * * Searches the children of the parent dentry for the name in question. If * the dentry is found its reference count is incremented and the dentry * is returned. The caller must use d_put to free the entry when it has * finished using it. %NULL is returned on failure. */ struct dentry * d_lookup(struct dentry * parent, struct qstr * name){ unsigned int len = name->len; unsigned int hash = name->hash; const unsigned char *str = name->name; struct list_head *head = d_hash(parent,hash); struct list_head *tmp; spin_lock(&dcache_lock); tmp = head->next; for (;;) { struct dentry * dentry = list_entry(tmp, struct dentry, d_hash); if (tmp == head) break; tmp = tmp->next; if (dentry->d_name.hash != hash) continue; if (dentry->d_parent != parent) continue; if (parent->d_op && parent->d_op->d_compare) { if (parent->d_op->d_compare(parent, &dentry->d_name, name)) continue; } else { if (dentry->d_name.len != len) continue; if (memcmp(dentry->d_name.name, str, len)) continue; } __dget_locked(dentry); dentry->d_vfs_flags |= DCACHE_REFERENCED; spin_unlock(&dcache_lock); return dentry; } spin_unlock(&dcache_lock); return NULL;}/** * d_validate - verify dentry provided from insecure source * @dentry: The dentry alleged to be valid child of @dparent * @dparent: The parent dentry (known to be valid) * @hash: Hash of the dentry * @len: Length of the name * * An insecure source has sent us a dentry, here we verify it and dget() it. * This is used by ncpfs in its readdir implementation. * Zero is returned in the dentry is invalid. */ int d_validate(struct dentry *dentry, struct dentry *dparent){ unsigned long dent_addr = (unsigned long) dentry; unsigned long min_addr = PAGE_OFFSET; unsigned long align_mask = 0x0F; struct list_head *base, *lhp; if (dent_addr < min_addr) goto out; if (dent_addr > (unsigned long)high_memory - sizeof(struct dentry)) goto out; if (dent_addr & align_mask) goto out; if ((!kern_addr_valid(dent_addr)) || (!kern_addr_valid(dent_addr -1 + sizeof(struct dentry)))) goto out; if (dentry->d_parent != dparent) goto out; spin_lock(&dcache_lock); lhp = base = d_hash(dparent, dentry->d_name.hash); while ((lhp = lhp->next) != base) { if (dentry == list_entry(lhp, struct dentry, d_hash)) { __dget_locked(dentry); spin_unlock(&dcache_lock); return 1; } } spin_unlock(&dcache_lock);out: return 0;}/* * When a file is deleted, we have two options: * - turn this dentry into a negative dentry * - unhash this dentry and free it. * * Usually, we want to just turn this into * a negative dentry, but if anybody else is * currently using the dentry or the inode * we can't do that and we fall back on removing * it from the hash queues and waiting for * it to be deleted later when it has no users */ /** * d_delete - delete a dentry * @dentry: The dentry to delete * * Turn the dentry into a negative dentry if possible, otherwise * remove it from the hash queues so it can be deleted later */ void d_delete(struct dentry * dentry){ /* * Are we the only user? */ spin_lock(&dcache_lock); if (atomic_read(&dentry->d_count) == 1) { dentry_iput(dentry); return; } spin_unlock(&dcache_lock); /* * If not, just drop the dentry and let dput * pick up the tab.. */ d_drop(dentry);}/** * d_rehash - add an entry back to the hash * @entry: dentry to add to the hash * * Adds a dentry to the hash according to its name. */ void d_rehash(struct dentry * entry){ struct list_head *list = d_hash(entry->d_parent, entry->d_name.hash); if (!list_empty(&entry->d_hash)) BUG(); spin_lock(&dcache_lock); list_add(&entry->d_hash, list); spin_unlock(&dcache_lock);}#define do_switch(x,y) do { \ __typeof__ (x) __tmp = x; \ x = y; y = __tmp; } while (0)/* * When switching names, the actual string doesn't strictly have to * be preserved in the target - because we're dropping the target * anyway. As such, we can just do a simple memcpy() to copy over * the new name before we switch. * * Note that we have to be a lot more careful about getting the hash * switched - we have to switch the hash value properly even if it * then no longer matches the actual (corrupted) string of the target. * The hash value has to match the hash queue that the dentry is on.. */static inline void switch_names(struct dentry * dentry, struct dentry * target){ const unsigned char *old_name, *new_name;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -