login
Header Space

 
 

[PATCH 20/42] Unionfs: readdir state helpers

Score:
Previous message: [thread] [date] [author]
Next message: [thread] [date] [author]
To: <hch@...>, <viro@...>, <akpm@...>
Cc: <linux-kernel@...>, <linux-fsdevel@...>, Erez Zadok <ezk@...>
Date: Sunday, December 9, 2007 - 10:41 pm

Includes duplicate name elimination and whiteout-handling code.

Signed-off-by: Erez Zadok <ezk@cs.sunysb.edu>
---
 fs/unionfs/rdstate.c |  285 ++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 285 insertions(+), 0 deletions(-)
 create mode 100644 fs/unionfs/rdstate.c

diff --git a/fs/unionfs/rdstate.c b/fs/unionfs/rdstate.c
new file mode 100644
index 0000000..7ba1e1a
--- /dev/null
+++ b/fs/unionfs/rdstate.c
@@ -0,0 +1,285 @@
+/*
+ * Copyright (c) 2003-2007 Erez Zadok
+ * Copyright (c) 2003-2006 Charles P. Wright
+ * Copyright (c) 2005-2007 Josef 'Jeff' Sipek
+ * Copyright (c) 2005-2006 Junjiro Okajima
+ * Copyright (c) 2005      Arun M. Krishnakumar
+ * Copyright (c) 2004-2006 David P. Quigley
+ * Copyright (c) 2003-2004 Mohammad Nayyer Zubair
+ * Copyright (c) 2003      Puja Gupta
+ * Copyright (c) 2003      Harikesavan Krishnan
+ * Copyright (c) 2003-2007 Stony Brook University
+ * Copyright (c) 2003-2007 The Research Foundation of SUNY
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include "union.h"
+
+/* This file contains the routines for maintaining readdir state. */
+
+/*
+ * There are two structures here, rdstate which is a hash table
+ * of the second structure which is a filldir_node.
+ */
+
+/*
+ * This is a struct kmem_cache for filldir nodes, because we allocate a lot
+ * of them and they shouldn't waste memory.  If the node has a small name
+ * (as defined by the dentry structure), then we use an inline name to
+ * preserve kmalloc space.
+ */
+static struct kmem_cache *unionfs_filldir_cachep;
+
+int unionfs_init_filldir_cache(void)
+{
+	unionfs_filldir_cachep =
+		kmem_cache_create("unionfs_filldir",
+				  sizeof(struct filldir_node), 0,
+				  SLAB_RECLAIM_ACCOUNT, NULL);
+
+	return (unionfs_filldir_cachep ? 0 : -ENOMEM);
+}
+
+void unionfs_destroy_filldir_cache(void)
+{
+	if (unionfs_filldir_cachep)
+		kmem_cache_destroy(unionfs_filldir_cachep);
+}
+
+/*
+ * This is a tuning parameter that tells us roughly how big to make the
+ * hash table in directory entries per page.  This isn't perfect, but
+ * at least we get a hash table size that shouldn't be too overloaded.
+ * The following averages are based on my home directory.
+ * 14.44693	Overall
+ * 12.29	Single Page Directories
+ * 117.93	Multi-page directories
+ */
+#define DENTPAGE 4096
+#define DENTPERONEPAGE 12
+#define DENTPERPAGE 118
+#define MINHASHSIZE 1
+static int guesstimate_hash_size(struct inode *inode)
+{
+	struct inode *lower_inode;
+	int bindex;
+	int hashsize = MINHASHSIZE;
+
+	if (UNIONFS_I(inode)->hashsize > 0)
+		return UNIONFS_I(inode)->hashsize;
+
+	for (bindex = ibstart(inode); bindex <= ibend(inode); bindex++) {
+		lower_inode = unionfs_lower_inode_idx(inode, bindex);
+		if (!lower_inode)
+			continue;
+
+		if (i_size_read(lower_inode) == DENTPAGE)
+			hashsize += DENTPERONEPAGE;
+		else
+			hashsize += (i_size_read(lower_inode) / DENTPAGE) *
+				DENTPERPAGE;
+	}
+
+	return hashsize;
+}
+
+int init_rdstate(struct file *file)
+{
+	BUG_ON(sizeof(loff_t) !=
+	       (sizeof(unsigned int) + sizeof(unsigned int)));
+	BUG_ON(UNIONFS_F(file)->rdstate != NULL);
+
+	UNIONFS_F(file)->rdstate = alloc_rdstate(file->f_path.dentry->d_inode,
+						 fbstart(file));
+
+	return (UNIONFS_F(file)->rdstate ? 0 : -ENOMEM);
+}
+
+struct unionfs_dir_state *find_rdstate(struct inode *inode, loff_t fpos)
+{
+	struct unionfs_dir_state *rdstate = NULL;
+	struct list_head *pos;
+
+	spin_lock(&UNIONFS_I(inode)->rdlock);
+	list_for_each(pos, &UNIONFS_I(inode)->readdircache) {
+		struct unionfs_dir_state *r =
+			list_entry(pos, struct unionfs_dir_state, cache);
+		if (fpos == rdstate2offset(r)) {
+			UNIONFS_I(inode)->rdcount--;
+			list_del(&r->cache);
+			rdstate = r;
+			break;
+		}
+	}
+	spin_unlock(&UNIONFS_I(inode)->rdlock);
+	return rdstate;
+}
+
+struct unionfs_dir_state *alloc_rdstate(struct inode *inode, int bindex)
+{
+	int i = 0;
+	int hashsize;
+	unsigned long mallocsize = sizeof(struct unionfs_dir_state);
+	struct unionfs_dir_state *rdstate;
+
+	hashsize = guesstimate_hash_size(inode);
+	mallocsize += hashsize * sizeof(struct list_head);
+	mallocsize = __roundup_pow_of_two(mallocsize);
+
+	/* This should give us about 500 entries anyway. */
+	if (mallocsize > PAGE_SIZE)
+		mallocsize = PAGE_SIZE;
+
+	hashsize = (mallocsize - sizeof(struct unionfs_dir_state)) /
+		sizeof(struct list_head);
+
+	rdstate = kmalloc(mallocsize, GFP_KERNEL);
+	if (unlikely(!rdstate))
+		return NULL;
+
+	spin_lock(&UNIONFS_I(inode)->rdlock);
+	if (UNIONFS_I(inode)->cookie >= (MAXRDCOOKIE - 1))
+		UNIONFS_I(inode)->cookie = 1;
+	else
+		UNIONFS_I(inode)->cookie++;
+
+	rdstate->cookie = UNIONFS_I(inode)->cookie;
+	spin_unlock(&UNIONFS_I(inode)->rdlock);
+	rdstate->offset = 1;
+	rdstate->access = jiffies;
+	rdstate->bindex = bindex;
+	rdstate->dirpos = 0;
+	rdstate->hashentries = 0;
+	rdstate->size = hashsize;
+	for (i = 0; i < rdstate->size; i++)
+		INIT_LIST_HEAD(&rdstate->list[i]);
+
+	return rdstate;
+}
+
+static void free_filldir_node(struct filldir_node *node)
+{
+	if (node->namelen >= DNAME_INLINE_LEN_MIN)
+		kfree(node->name);
+	kmem_cache_free(unionfs_filldir_cachep, node);
+}
+
+void free_rdstate(struct unionfs_dir_state *state)
+{
+	struct filldir_node *tmp;
+	int i;
+
+	for (i = 0; i < state->size; i++) {
+		struct list_head *head = &(state->list[i]);
+		struct list_head *pos, *n;
+
+		/* traverse the list and deallocate space */
+		list_for_each_safe(pos, n, head) {
+			tmp = list_entry(pos, struct filldir_node, file_list);
+			list_del(&tmp->file_list);
+			free_filldir_node(tmp);
+		}
+	}
+
+	kfree(state);
+}
+
+struct filldir_node *find_filldir_node(struct unionfs_dir_state *rdstate,
+				       const char *name, int namelen,
+				       int is_whiteout)
+{
+	int index;
+	unsigned int hash;
+	struct list_head *head;
+	struct list_head *pos;
+	struct filldir_node *cursor = NULL;
+	int found = 0;
+
+	BUG_ON(namelen <= 0);
+
+	hash = full_name_hash(name, namelen);
+	index = hash % rdstate->size;
+
+	head = &(rdstate->list[index]);
+	list_for_each(pos, head) {
+		cursor = list_entry(pos, struct filldir_node, file_list);
+
+		if (cursor->namelen == namelen && cursor->hash == hash &&
+		    !strncmp(cursor->name, name, namelen)) {
+			/*
+			 * a duplicate exists, and hence no need to create
+			 * entry to the list
+			 */
+			found = 1;
+
+			/*
+			 * if a duplicate is found in this branch, and is
+			 * not due to the caller looking for an entry to
+			 * whiteout, then the file system may be corrupted.
+			 */
+			if (unlikely(!is_whiteout &&
+				     cursor->bindex == rdstate->bindex))
+				printk(KERN_ERR "unionfs: filldir: possible "
+				       "I/O error: a file is duplicated "
+				       "in the same branch %d: %s\n",
+				       rdstate->bindex, cursor->name);
+			break;
+		}
+	}
+
+	if (!found)
+		cursor = NULL;
+
+	return cursor;
+}
+
+int add_filldir_node(struct unionfs_dir_state *rdstate, const char *name,
+		     int namelen, int bindex, int whiteout)
+{
+	struct filldir_node *new;
+	unsigned int hash;
+	int index;
+	int err = 0;
+	struct list_head *head;
+
+	BUG_ON(namelen <= 0);
+
+	hash = full_name_hash(name, namelen);
+	index = hash % rdstate->size;
+	head = &(rdstate->list[index]);
+
+	new = kmem_cache_alloc(unionfs_filldir_cachep, GFP_KERNEL);
+	if (unlikely(!new)) {
+		err = -ENOMEM;
+		goto out;
+	}
+
+	INIT_LIST_HEAD(&new->file_list);
+	new->namelen = namelen;
+	new->hash = hash;
+	new->bindex = bindex;
+	new->whiteout = whiteout;
+
+	if (namelen < DNAME_INLINE_LEN_MIN) {
+		new->name = new->iname;
+	} else {
+		new->name = kmalloc(namelen + 1, GFP_KERNEL);
+		if (unlikely(!new->name)) {
+			kmem_cache_free(unionfs_filldir_cachep, new);
+			new = NULL;
+			goto out;
+		}
+	}
+
+	memcpy(new->name, name, namelen);
+	new->name[namelen] = '\0';
+
+	rdstate->hashentries++;
+
+	list_add(&(new->file_list), head);
+out:
+	return err;
+}
-- 
1.5.2.2

--
Previous message: [thread] [date] [author]
Next message: [thread] [date] [author]

Messages in current thread:
[UNIONFS] 00/42 Unionfs and related patches review, Erez Zadok, (Sun Dec 9, 10:41 pm)
[PATCH 21/42] Unionfs: inode operations, Erez Zadok, (Sun Dec 9, 10:41 pm)
[PATCH 30/42] Unionfs: debugging infrastructure, Erez Zadok, (Sun Dec 9, 10:42 pm)
[PATCH 20/42] Unionfs: readdir state helpers, Erez Zadok, (Sun Dec 9, 10:41 pm)
[PATCH 13/42] Unionfs: basic file operations, Erez Zadok, (Sun Dec 9, 10:41 pm)
[PATCH 36/42] VFS: export drop_pagecache_sb, Erez Zadok, (Sun Dec 9, 10:42 pm)
Re: [PATCH 36/42] VFS: export drop_pagecache_sb, Nick Piggin, (Wed Dec 12, 1:38 am)
Re: [PATCH 36/42] VFS: export drop_pagecache_sb , Erez Zadok, (Thu Dec 13, 11:24 am)
Re: [PATCH 36/42] VFS: export drop_pagecache_sb, Nick Piggin, (Thu Dec 13, 6:47 pm)
[PATCH 11/42] Unionfs: main header file, Erez Zadok, (Sun Dec 9, 10:41 pm)
[PATCH 25/42] Unionfs: super_block operations, Erez Zadok, (Sun Dec 9, 10:41 pm)
[PATCH 16/42] Unionfs: lower-level lookup routines, Erez Zadok, (Sun Dec 9, 10:41 pm)
[PATCH 10/42] Unionfs: fanout header definitions, Erez Zadok, (Sun Dec 9, 10:41 pm)
[PATCH 17/42] Unionfs: rename method and helpers, Erez Zadok, (Sun Dec 9, 10:41 pm)
[PATCH 34/42] VFS path get/put ops used by Unionfs, Erez Zadok, (Sun Dec 9, 10:42 pm)
[PATCH 23/42] Unionfs: address-space operations, Erez Zadok, (Sun Dec 9, 10:41 pm)
[PATCH 19/42] Unionfs: readdir helper functions, Erez Zadok, (Sun Dec 9, 10:41 pm)
[PATCH 14/42] Unionfs: lower-level copyup routines, Erez Zadok, (Sun Dec 9, 10:41 pm)
[PATCH 38/42] VFS: simplified fsstack_copy_attr_all, Erez Zadok, (Sun Dec 9, 10:42 pm)
[PATCH 32/42] Unionfs file system magic number, Erez Zadok, (Sun Dec 9, 10:42 pm)
[PATCH 37/42] VFS: export release_open_intent symbol, Erez Zadok, (Sun Dec 9, 10:42 pm)
[PATCH 33/42] MM: extern for drop_pagecache_sb, Erez Zadok, (Sun Dec 9, 10:42 pm)
Re: [PATCH 33/42] MM: extern for drop_pagecache_sb, Adrian Bunk, (Mon Dec 10, 10:04 am)
Re: [PATCH 33/42] MM: extern for drop_pagecache_sb , Erez Zadok, (Thu Dec 13, 11:05 am)
[PATCH 29/42] Unionfs: miscellaneous helper routines, Erez Zadok, (Sun Dec 9, 10:42 pm)
[PATCH 31/42] VFS: fs_stack header cleanups, Erez Zadok, (Sun Dec 9, 10:42 pm)
[PATCH 15/42] Unionfs: dentry revalidation, Erez Zadok, (Sun Dec 9, 10:41 pm)
[PATCH 22/42] Unionfs: unlink/rmdir operations, Erez Zadok, (Sun Dec 9, 10:41 pm)
[PATCH 28/42] Unionfs: async I/O queue operations, Erez Zadok, (Sun Dec 9, 10:42 pm)
[PATCH 27/42] Unionfs: async I/O queue headers, Erez Zadok, (Sun Dec 9, 10:42 pm)
[PATCH 09/42] Unionfs: main Makefile, Erez Zadok, (Sun Dec 9, 10:41 pm)
[PATCH 08/42] Makefile: hook to compile unionfs, Erez Zadok, (Sun Dec 9, 10:41 pm)
[PATCH 04/42] Unionfs: usage documentation for users, Erez Zadok, (Sun Dec 9, 10:41 pm)
[PATCH 07/42] Unionfs maintainers, Erez Zadok, (Sun Dec 9, 10:41 pm)
[PATCH 02/42] Unionfs: unionfs documentation index, Erez Zadok, (Sun Dec 9, 10:41 pm)
Re: [PATCH 01/42] Unionfs: filesystems documentation index, Jan Engelhardt, (Mon Dec 10, 10:47 am)
speck-geostationary