dcache.c

来自「基于组件方式开发操作系统的OSKIT源代码」· C语言 代码 · 共 931 行 · 第 1/2 页

C
931
字号
 * Priority: *   0 - very urgent: shrink everything *  ... *   6 - base-level: try to shrink a bit. */void shrink_dcache_memory(int priority, unsigned int gfp_mask){	if (gfp_mask & __GFP_IO) {		int count = 0;		if (priority)			count = dentry_stat.nr_unused / priority;		prune_dcache(count);	}}#define NAME_ALLOC_LEN(len)	((len+16) & ~15)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;	dentry->d_count = 1;	dentry->d_flags = 0;	dentry->d_inode = NULL;	dentry->d_parent = NULL;	dentry->d_sb = NULL;	if (parent) {		dentry->d_parent = dget(parent);		dentry->d_sb = parent->d_sb;		list_add(&dentry->d_child, &parent->d_subdirs);	} else		INIT_LIST_HEAD(&dentry->d_child);			dentry->d_mounts = dentry;	dentry->d_covers = dentry;	INIT_LIST_HEAD(&dentry->d_hash);	INIT_LIST_HEAD(&dentry->d_lru);	INIT_LIST_HEAD(&dentry->d_subdirs);	INIT_LIST_HEAD(&dentry->d_alias);	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;	return 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 (inode)		list_add(&entry->d_alias, &inode->i_dentry);	entry->d_inode = inode;}struct dentry * d_alloc_root(struct inode * root_inode, struct dentry *old_root){	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;	hash = hash ^ (hash >> D_HASHBITS) ^ (hash >> D_HASHBITS*2);	return dentry_hashtable + (hash & D_HASHMASK);}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 = 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;		}		return dget(dentry);	}	return NULL;}/* * An insecure source has sent us a dentry, here we verify it. * * This is just to make knfsd able to have the dentry pointer * in the NFS file handle. * * NOTE! Do _not_ dereference the pointers before we have * validated them. We can test the pointer values, but we * must not actually use them until we have found a valid * copy of the pointer in kernel space.. */int d_validate(struct dentry *dentry, struct dentry *dparent,	       unsigned int hash, unsigned int len){	struct list_head *base, *lhp;	int valid = 1;	if (dentry != dparent) {		base = d_hash(dparent, hash);		lhp = base;		while ((lhp = lhp->next) != base) {			if (dentry == list_entry(lhp, struct dentry, d_hash))				goto out;		}	} else {		/*		 * Special case: local mount points don't live in		 * the hashes, so we search the super blocks.		 */		struct super_block *sb = sb_entry(super_blocks.next);		for (; sb != sb_entry(&super_blocks); 		     sb = sb_entry(sb->s_list.next)) {			if (!sb->s_dev)				continue;			if (sb->s_root == dentry)				goto out;		}	}	valid = 0;out:	return valid;}/* * 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 */void d_delete(struct dentry * dentry){	/*	 * Are we the only user?	 */	if (dentry->d_count == 1) {		dentry_iput(dentry);		return;	}	/*	 * If not, just drop the dentry and let dput	 * pick up the tab..	 */	d_drop(dentry);}void d_rehash(struct dentry * entry){	struct dentry * parent = entry->d_parent;	list_add(&entry->d_hash, d_hash(parent, entry->d_name.hash));}#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 has 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;	memcpy(dentry->d_iname, target->d_iname, DNAME_INLINE_LEN); 	old_name = target->d_name.name;	new_name = dentry->d_name.name;	if (old_name == target->d_iname)		old_name = dentry->d_iname;	if (new_name == dentry->d_iname)		new_name = target->d_iname;	target->d_name.name = new_name;	dentry->d_name.name = old_name;}/* * We cannibalize "target" when moving dentry on top of it, * because it's going to be thrown away anyway. We could be more * polite about it, though. * * This forceful removal will result in ugly /proc output if * somebody holds a file open that got deleted due to a rename. * We could be nicer about the deleted file, and let it show * up under the name it got deleted rather than the name that * deleted it. * * Careful with the hash switch. The hash switch depends on * the fact that any list-entry can be a head of the list. * Think about it. */void d_move(struct dentry * dentry, struct dentry * target){	if (!dentry->d_inode)		printk(KERN_WARNING "VFS: moving negative dcache entry\n");	/* Move the dentry to the target hash queue */	list_del(&dentry->d_hash);	list_add(&dentry->d_hash, &target->d_hash);	/* Unhash the target: dput() will then get rid of it */	list_del(&target->d_hash);	INIT_LIST_HEAD(&target->d_hash);	list_del(&dentry->d_child);	list_del(&target->d_child);	/* Switch the parents and the names.. */	switch_names(dentry, target);	do_switch(dentry->d_parent, target->d_parent);	do_switch(dentry->d_name.len, target->d_name.len);	do_switch(dentry->d_name.hash, target->d_name.hash);	/* And add them back to the (new) parent lists */	list_add(&target->d_child, &target->d_parent->d_subdirs);	list_add(&dentry->d_child, &dentry->d_parent->d_subdirs);}/* * "buflen" should be PAGE_SIZE or more. */char * d_path(struct dentry *dentry, char *buffer, int buflen){	char * end = buffer+buflen;	char * retval;	struct dentry * root = current->fs->root;	*--end = '\0';	buflen--;	if (dentry->d_parent != dentry && list_empty(&dentry->d_hash)) {		buflen -= 10;		end -= 10;		memcpy(end, " (deleted)", 10);	}	/* Get '/' right */	retval = end-1;	*retval = '/';	for (;;) {		struct dentry * parent;		int namelen;		if (dentry == root)			break;		dentry = dentry->d_covers;		parent = dentry->d_parent;		if (dentry == parent)			break;		namelen = dentry->d_name.len;		buflen -= namelen + 1;		if (buflen < 0)			break;		end -= namelen;		memcpy(end, dentry->d_name.name, namelen);		*--end = '/';		retval = end;		dentry = parent;	}	return retval;}/* * NOTE! The user-level library version returns a * character pointer. The kernel system call just * returns the length of the buffer filled (which * includes the ending '\0' character), or a negative * error value. So libc would do something like * *	char *getcwd(char * buf, size_t size) *	{ *		int retval; * *		retval = sys_getcwd(buf, size); *		if (retval >= 0) *			return buf; *		errno = -retval; *		return NULL; *	} */asmlinkage int sys_getcwd(char *buf, unsigned long size){	int error;	struct dentry *pwd = current->fs->pwd; 	error = -ENOENT;	/* Has the current directory has been unlinked? */	if (pwd->d_parent == pwd || !list_empty(&pwd->d_hash)) {		char *page = (char *) __get_free_page(GFP_USER);		error = -ENOMEM;		if (page) {			unsigned long len;			char * cwd = d_path(pwd, page, PAGE_SIZE);			error = -ERANGE;			len = PAGE_SIZE + page - cwd;			if (len <= size) {				error = len;				if (copy_to_user(buf, cwd, len))					error = -EFAULT;			}			free_page((unsigned long) page);		}	}	return error;}/* * Test whether new_dentry is a subdirectory of old_dentry. * * Trivially implemented using the dcache structure */int is_subdir(struct dentry * new_dentry, struct dentry * old_dentry){	int result;	result = 0;	for (;;) {		if (new_dentry != old_dentry) {			struct dentry * parent = new_dentry->d_parent;			if (parent == new_dentry)				break;			new_dentry = parent;			continue;		}		result = 1;		break;	}	return result;}/* * Check whether a dentry already exists for the given name, * and return the inode number if it has an inode. * * This routine is used to post-process directory listings for * filesystems using synthetic inode numbers, and is necessary * to keep getcwd() working. */ino_t find_inode_number(struct dentry *dir, struct qstr *name){	struct dentry * dentry;	ino_t ino = 0;	/*	 * Check for a fs-specific hash function. Note that we must	 * calculate the standard hash first, as the d_op->d_hash()	 * routine may choose to leave the hash value unchanged.	 */	name->hash = full_name_hash(name->name, name->len);	if (dir->d_op && dir->d_op->d_hash)	{		if (dir->d_op->d_hash(dir, name) != 0)			goto out;	}	dentry = d_lookup(dir, name);	if (dentry)	{		if (dentry->d_inode)			ino = dentry->d_inode->i_ino;		dput(dentry);	}out:	return ino;}void __init dcache_init(void){	int i;	struct list_head *d = dentry_hashtable;	/* 	 * A constructor could be added for stable state like the lists,	 * but it is probably not worth it because of the cache nature	 * of the dcache. 	 * If fragmentation is too bad then the SLAB_HWCACHE_ALIGN	 * flag could be removed here, to hint to the allocator that	 * it should not try to get multiple page regions.  	 */	dentry_cache = kmem_cache_create("dentry_cache",					 sizeof(struct dentry),					 0,					 SLAB_HWCACHE_ALIGN,					 NULL, NULL);	if (!dentry_cache)		panic("Cannot create dentry cache");	i = D_HASHSIZE;	do {		INIT_LIST_HEAD(d);		d++;		i--;	} while (i);}

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?