--- linux-2.6.11-rc1/fs/ext2/balloc.c	2004-10-23 05:06:16 +0200
+++ linux-2.6.11-rc1-vs1.9.4-rc2/fs/ext2/balloc.c	2005-01-15 11:27:52 +0100
@@ -108,6 +109,8 @@ static int reserve_blocks(struct super_b
 	free_blocks = percpu_counter_read_positive(&sbi->s_freeblocks_counter);
 	root_blocks = le32_to_cpu(es->s_r_blocks_count);
 
+	DLIMIT_ADJUST_BLOCK(sb, vx_current_xid(), &free_blocks, &root_blocks);
+
 	if (free_blocks < count)
 		count = free_blocks;
 
--- linux-2.6.11-rc1/fs/ext2/balloc.c	2004-10-23 05:06:16 +0200
+++ linux-2.6.11-rc1-vs1.9.4-rc2/fs/ext2/balloc.c	2005-01-15 11:27:52 +0100
@@ -258,6 +261,7 @@ do_more:
 	}
 error_return:
 	brelse(bitmap_bh);
+	DLIMIT_FREE_BLOCK(sb, inode->i_xid, freed);
 	release_blocks(sb, freed);
 	DQUOT_FREE_BLOCK(inode, freed);
 }
--- linux-2.6.11-rc1/fs/ext2/balloc.c	2004-10-23 05:06:16 +0200
+++ linux-2.6.11-rc1-vs1.9.4-rc2/fs/ext2/balloc.c	2005-01-15 11:27:52 +0100
@@ -361,6 +365,10 @@ int ext2_new_block(struct inode *inode, 
 		*err = -ENOSPC;
 		goto out_dquot;
 	}
+	if (DLIMIT_ALLOC_BLOCK(sb, inode->i_xid, es_alloc)) {
+		*err = -ENOSPC;
+		goto out_dlimit;
+	}
 
 	ext2_debug ("goal=%lu.\n", goal);
 
--- linux-2.6.11-rc1/fs/ext2/balloc.c	2004-10-23 05:06:16 +0200
+++ linux-2.6.11-rc1-vs1.9.4-rc2/fs/ext2/balloc.c	2005-01-15 11:27:52 +0100
@@ -508,6 +516,8 @@ got_block:
 	*err = 0;
 out_release:
 	group_release_blocks(sb, group_no, desc, gdp_bh, group_alloc);
+	DLIMIT_FREE_BLOCK(sb, inode->i_xid, es_alloc);
+out_dlimit:
 	release_blocks(sb, es_alloc);
 out_dquot:
 	DQUOT_FREE_BLOCK(inode, dq_alloc);
--- linux-2.6.11-rc1/fs/ext2/ialloc.c	2005-01-14 12:35:55 +0100
+++ linux-2.6.11-rc1-vs1.9.4-rc2/fs/ext2/ialloc.c	2005-01-15 11:27:52 +0100
@@ -124,6 +126,7 @@ void ext2_free_inode (struct inode * ino
 	if (!is_bad_inode(inode)) {
 		/* Quota is already initialized in iput() */
 		ext2_xattr_delete_inode(inode);
+		DLIMIT_FREE_INODE(sb, inode->i_xid);
 	    	DQUOT_FREE_INODE(inode);
 		DQUOT_DROP(inode);
 	}
--- linux-2.6.11-rc1/fs/ext2/ialloc.c	2005-01-14 12:35:55 +0100
+++ linux-2.6.11-rc1-vs1.9.4-rc2/fs/ext2/ialloc.c	2005-01-15 11:27:52 +0100
@@ -465,6 +468,15 @@ struct inode *ext2_new_inode(struct inod
 	if (!inode)
 		return ERR_PTR(-ENOMEM);
 
+	if (sb->s_flags & MS_TAGXID)
+		inode->i_xid = vx_current_xid();
+	else
+		inode->i_xid = 0;
+
+	if (DLIMIT_ALLOC_INODE(sb, inode->i_xid)) {
+		err = -ENOSPC;
+		goto fail_dlim;
+	}
 	ei = EXT2_I(inode);
 	sbi = EXT2_SB(sb);
 	es = sbi->s_es;
--- linux-2.6.11-rc1/fs/ext2/ialloc.c	2005-01-14 12:35:55 +0100
+++ linux-2.6.11-rc1-vs1.9.4-rc2/fs/ext2/ialloc.c	2005-01-15 11:27:52 +0100
@@ -620,12 +633,15 @@ got:
 	return inode;
 
 fail2:
+	DLIMIT_FREE_INODE(sb, inode->i_xid);
 	inode->i_flags |= S_NOQUOTA;
 	inode->i_nlink = 0;
 	iput(inode);
 	return ERR_PTR(err);
 
 fail:
+	DLIMIT_FREE_INODE(sb, inode->i_xid);
+fail_dlim:
 	make_bad_inode(inode);
 	iput(inode);
 	return ERR_PTR(err);
--- linux-2.6.11-rc1/fs/ext2/xattr.c	2005-01-14 12:35:55 +0100
+++ linux-2.6.11-rc1-vs1.9.4-rc2/fs/ext2/xattr.c	2005-01-15 11:27:52 +0100
@@ -643,8 +644,12 @@ ext2_xattr_set2(struct inode *inode, str
 				   the inode.  */
 				ea_bdebug(new_bh, "reusing block");
 
+				error = -ENOSPC;
+				if (DLIMIT_ALLOC_BLOCK(sb, inode->i_xid, 1))
+					goto cleanup;
 				error = -EDQUOT;
 				if (DQUOT_ALLOC_BLOCK(inode, 1)) {
+					DLIMIT_FREE_BLOCK(sb, inode->i_xid, 1);
 					unlock_buffer(new_bh);
 					goto cleanup;
 				}
--- linux-2.6.11-rc1/fs/ext2/xattr.c	2005-01-14 12:35:55 +0100
+++ linux-2.6.11-rc1-vs1.9.4-rc2/fs/ext2/xattr.c	2005-01-15 11:27:52 +0100
@@ -724,6 +729,7 @@ ext2_xattr_set2(struct inode *inode, str
 			/* Decrement the refcount only. */
 			HDR(old_bh)->h_refcount = cpu_to_le32(
 				le32_to_cpu(HDR(old_bh)->h_refcount) - 1);
+			DLIMIT_FREE_BLOCK(sb, inode->i_xid, 1);
 			DQUOT_FREE_BLOCK(inode, 1);
 			mark_buffer_dirty(old_bh);
 			ea_bdebug(old_bh, "refcount now=%d",
--- linux-2.6.11-rc1/fs/ext2/xattr.c	2005-01-14 12:35:55 +0100
+++ linux-2.6.11-rc1-vs1.9.4-rc2/fs/ext2/xattr.c	2005-01-15 11:27:52 +0100
@@ -779,6 +785,7 @@ ext2_xattr_delete_inode(struct inode *in
 		mark_buffer_dirty(bh);
 		if (IS_SYNC(inode))
 			sync_dirty_buffer(bh);
+		DLIMIT_FREE_BLOCK(inode->i_sb, inode->i_xid, 1);
 		DQUOT_FREE_BLOCK(inode, 1);
 	}
 	ea_bdebug(bh, "refcount now=%d", le32_to_cpu(HDR(bh)->h_refcount) - 1);
--- linux-2.6.11-rc1/fs/ext3/balloc.c	2005-01-14 12:35:55 +0100
+++ linux-2.6.11-rc1-vs1.9.4-rc2/fs/ext3/balloc.c	2005-01-15 11:27:52 +0100
@@ -467,8 +468,10 @@ void ext3_free_blocks(handle_t *handle, 
 		return;
 	}
 	ext3_free_blocks_sb(handle, sb, block, count, &dquot_freed_blocks);
-	if (dquot_freed_blocks)
+	if (dquot_freed_blocks) {
+		DLIMIT_FREE_BLOCK(sb, inode->i_xid, dquot_freed_blocks);
 		DQUOT_FREE_BLOCK(inode, dquot_freed_blocks);
+	}
 	return;
 }
 
--- linux-2.6.11-rc1/fs/ext3/balloc.c	2005-01-14 12:35:55 +0100
+++ linux-2.6.11-rc1-vs1.9.4-rc2/fs/ext3/balloc.c	2005-01-15 11:27:52 +0100
@@ -1096,18 +1099,32 @@ out:
 	return ret;
 }
 
-static int ext3_has_free_blocks(struct ext3_sb_info *sbi)
+static int ext3_has_free_blocks(struct super_block *sb)
 {
-	int free_blocks, root_blocks;
+	struct ext3_sb_info *sbi = EXT3_SB(sb);
+	int free_blocks, root_blocks, cond;
 
 	free_blocks = percpu_counter_read_positive(&sbi->s_freeblocks_counter);
 	root_blocks = le32_to_cpu(sbi->s_es->s_r_blocks_count);
-	if (free_blocks < root_blocks + 1 && !capable(CAP_SYS_RESOURCE) &&
+
+	vxdprintk(VXD_CBIT(dlim, 3),
+		"ext3_has_free_blocks(%p): free=%u, root=%u",
+		sb, free_blocks, root_blocks);
+
+	DLIMIT_ADJUST_BLOCK(sb, vx_current_xid(), &free_blocks, &root_blocks);
+
+	cond = (free_blocks < root_blocks + 1 &&
+		!capable(CAP_SYS_RESOURCE) &&
 		sbi->s_resuid != current->fsuid &&
-		(sbi->s_resgid == 0 || !in_group_p (sbi->s_resgid))) {
-		return 0;
-	}
-	return 1;
+		(sbi->s_resgid == 0 || !in_group_p (sbi->s_resgid)));
+
+	vxdprintk(VXD_CBIT(dlim, 3),
+		"ext3_has_free_blocks(%p): %u<%u+1, %c, %u!=%u r=%d",
+		sb, free_blocks, root_blocks,
+		!capable(CAP_SYS_RESOURCE)?'1':'0',
+		sbi->s_resuid, current->fsuid, cond?0:1);
+
+	return (cond ? 0 : 1);
 }
 
 /*
--- linux-2.6.11-rc1/fs/ext3/balloc.c	2005-01-14 12:35:55 +0100
+++ linux-2.6.11-rc1-vs1.9.4-rc2/fs/ext3/balloc.c	2005-01-15 11:27:52 +0100
@@ -1118,7 +1135,7 @@ static int ext3_has_free_blocks(struct e
  */
 int ext3_should_retry_alloc(struct super_block *sb, int *retries)
 {
-	if (!ext3_has_free_blocks(EXT3_SB(sb)) || (*retries)++ > 3)
+	if (!ext3_has_free_blocks(sb) || (*retries)++ > 3)
 		return 0;
 
 	jbd_debug(1, "%s: retrying operation after ENOSPC\n", sb->s_id);
--- linux-2.6.11-rc1/fs/ext3/balloc.c	2005-01-14 12:35:55 +0100
+++ linux-2.6.11-rc1-vs1.9.4-rc2/fs/ext3/balloc.c	2005-01-15 11:27:52 +0100
@@ -1173,6 +1190,8 @@ int ext3_new_block(handle_t *handle, str
 		*errp = -EDQUOT;
 		return 0;
 	}
+	if (DLIMIT_ALLOC_BLOCK(sb, inode->i_xid, 1))
+		goto out_dlimit;
 
 	sbi = EXT3_SB(sb);
 	es = EXT3_SB(sb)->s_es;
--- linux-2.6.11-rc1/fs/ext3/balloc.c	2005-01-14 12:35:55 +0100
+++ linux-2.6.11-rc1-vs1.9.4-rc2/fs/ext3/balloc.c	2005-01-15 11:27:52 +0100
@@ -1189,7 +1208,7 @@ int ext3_new_block(handle_t *handle, str
 	if (test_opt(sb, RESERVATION) &&
 		S_ISREG(inode->i_mode) && (windowsz > 0))
 		my_rsv = rsv;
-	if (!ext3_has_free_blocks(sbi)) {
+	if (!ext3_has_free_blocks(sb)) {
 		*errp = -ENOSPC;
 		goto out;
 	}
--- linux-2.6.11-rc1/fs/ext3/balloc.c	2005-01-14 12:35:55 +0100
+++ linux-2.6.11-rc1-vs1.9.4-rc2/fs/ext3/balloc.c	2005-01-15 11:27:52 +0100
@@ -1365,6 +1384,9 @@ allocated:
 io_error:
 	*errp = -EIO;
 out:
+	if (!performed_allocation)
+		DLIMIT_FREE_BLOCK(sb, inode->i_xid, 1);
+out_dlimit:
 	if (fatal) {
 		*errp = fatal;
 		ext3_std_error(sb, fatal);
--- linux-2.6.11-rc1/fs/ext3/ialloc.c	2005-01-14 12:35:55 +0100
+++ linux-2.6.11-rc1-vs1.9.4-rc2/fs/ext3/ialloc.c	2005-01-15 11:27:52 +0100
@@ -125,6 +126,7 @@ void ext3_free_inode (handle_t *handle, 
 	 */
 	DQUOT_INIT(inode);
 	ext3_xattr_delete_inode(handle, inode);
+	DLIMIT_FREE_INODE(sb, inode->i_xid);
 	DQUOT_FREE_INODE(inode);
 	DQUOT_DROP(inode);
 
--- linux-2.6.11-rc1/fs/ext3/ialloc.c	2005-01-14 12:35:55 +0100
+++ linux-2.6.11-rc1-vs1.9.4-rc2/fs/ext3/ialloc.c	2005-01-15 11:27:52 +0100
@@ -443,6 +445,16 @@ struct inode *ext3_new_inode(handle_t *h
 	inode = new_inode(sb);
 	if (!inode)
 		return ERR_PTR(-ENOMEM);
+
+	if (sb->s_flags & MS_TAGXID)
+		inode->i_xid = vx_current_xid();
+	else
+		inode->i_xid = 0;
+
+	if (DLIMIT_ALLOC_INODE(sb, inode->i_xid)) {
+		err = -ENOSPC;
+		goto out;
+	}
 	ei = EXT3_I(inode);
 
 	sbi = EXT3_SB(sb);
--- linux-2.6.11-rc1/fs/ext3/ialloc.c	2005-01-14 12:35:55 +0100
+++ linux-2.6.11-rc1-vs1.9.4-rc2/fs/ext3/ialloc.c	2005-01-15 11:27:52 +0100
@@ -623,6 +636,7 @@ got:
 	ext3_debug("allocating inode %lu\n", inode->i_ino);
 	goto really_out;
 fail:
+	DLIMIT_FREE_INODE(sb, inode->i_xid);
 	ext3_std_error(sb, err);
 out:
 	iput(inode);
--- linux-2.6.11-rc1/fs/ext3/ialloc.c	2005-01-14 12:35:55 +0100
+++ linux-2.6.11-rc1-vs1.9.4-rc2/fs/ext3/ialloc.c	2005-01-15 11:27:52 +0100
@@ -632,6 +646,7 @@ really_out:
 	return ret;
 
 fail2:
+	DLIMIT_FREE_INODE(sb, inode->i_xid);
 	inode->i_flags |= S_NOQUOTA;
 	inode->i_nlink = 0;
 	iput(inode);
--- linux-2.6.11-rc1/fs/ext3/xattr.c	2005-01-14 12:35:55 +0100
+++ linux-2.6.11-rc1-vs1.9.4-rc2/fs/ext3/xattr.c	2005-01-15 11:27:52 +0100
@@ -1228,8 +1229,12 @@ ext3_xattr_set_handle2(handle_t *handle,
 				   the inode. */
 				ea_bdebug(new_bh, "reusing block");
 
+				error = -ENOSPC;
+				if (DLIMIT_ALLOC_BLOCK(sb, inode->i_xid, 1))
+					goto cleanup;
 				error = -EDQUOT;
 				if (DQUOT_ALLOC_BLOCK(inode, 1)) {
+					DLIMIT_FREE_BLOCK(sb, inode->i_xid, 1);
 					unlock_buffer(new_bh);
 					journal_release_buffer(handle, new_bh,
 							       credits);
--- linux-2.6.11-rc1/fs/ext3/xattr.c	2005-01-14 12:35:55 +0100
+++ linux-2.6.11-rc1-vs1.9.4-rc2/fs/ext3/xattr.c	2005-01-15 11:27:52 +0100
@@ -1314,6 +1319,7 @@ getblk_failed:
 			/* Decrement the refcount only. */
 			HDR(old_bh)->h_refcount = cpu_to_le32(
 				le32_to_cpu(HDR(old_bh)->h_refcount) - 1);
+			DLIMIT_FREE_BLOCK(sb, inode->i_xid, 1);
 			DQUOT_FREE_BLOCK(inode, 1);
 			ext3_journal_dirty_metadata(handle, old_bh);
 			ea_bdebug(old_bh, "refcount now=%d",
--- linux-2.6.11-rc1/fs/ext3/xattr.c	2005-01-14 12:35:55 +0100
+++ linux-2.6.11-rc1-vs1.9.4-rc2/fs/ext3/xattr.c	2005-01-15 11:27:52 +0100
@@ -1405,6 +1411,7 @@ ext3_xattr_delete_inode(handle_t *handle
 		ext3_journal_dirty_metadata(handle, bh);
 		if (IS_SYNC(inode))
 			handle->h_sync = 1;
+		DLIMIT_FREE_BLOCK(inode->i_sb, inode->i_xid, 1);
 		DQUOT_FREE_BLOCK(inode, 1);
 	}
 	ea_bdebug(bh, "refcount now=%d", le32_to_cpu(HDR(bh)->h_refcount) - 1);
--- linux-2.6.11-rc1/include/linux/vserver/dlimit.h	1970-01-01 01:00:00 +0100
+++ linux-2.6.11-rc1-vs1.9.4-rc2/include/linux/vserver/dlimit.h	2005-01-15 11:27:52 +0100
@@ -0,0 +1,85 @@
+#ifndef _VX_DLIMIT_H
+#define _VX_DLIMIT_H
+
+#include "switch.h"
+#include <linux/spinlock.h>
+
+/*  inode vserver commands */
+
+#define VCMD_add_dlimit		VC_CMD(DLIMIT, 1, 0)
+#define VCMD_rem_dlimit		VC_CMD(DLIMIT, 2, 0)
+
+#define VCMD_set_dlimit		VC_CMD(DLIMIT, 5, 0)
+#define VCMD_get_dlimit		VC_CMD(DLIMIT, 6, 0)
+
+
+struct	vcmd_ctx_dlimit_base_v0 {
+	const char __user *name;
+	uint32_t flags;
+};
+
+struct	vcmd_ctx_dlimit_v0 {
+	const char __user *name;
+	uint32_t space_used;			/* used space in kbytes */
+	uint32_t space_total;			/* maximum space in kbytes */
+	uint32_t inodes_used;			/* used inodes */
+	uint32_t inodes_total;			/* maximum inodes */
+	uint32_t reserved;			/* reserved for root in % */
+	uint32_t flags;
+};
+
+#define CDLIM_UNSET		(0ULL)
+#define CDLIM_INFINITY		(~0ULL)
+#define CDLIM_KEEP		(~1ULL)
+
+
+#ifdef	__KERNEL__
+
+struct super_block;
+
+struct dl_info {
+	struct hlist_node dl_hlist;		/* linked list of contexts */
+	struct rcu_head dl_rcu;			/* the rcu head */
+	xid_t dl_xid;				/* context id */
+	atomic_t dl_usecnt;			/* usage count */
+	atomic_t dl_refcnt;			/* reference count */
+
+	struct super_block *dl_sb;		/* associated superblock */
+
+//	struct rw_semaphore dl_sem;		/* protect the values */
+	spinlock_t dl_lock;			/* protect the values */
+
+	uint64_t dl_space_used;			/* used space in bytes */
+	uint64_t dl_space_total;		/* maximum space in bytes */
+	uint32_t dl_inodes_used;		/* used inodes */
+	uint32_t dl_inodes_total;		/* maximum inodes */
+
+	unsigned int dl_nrlmult;		/* non root limit mult */
+};
+
+struct rcu_head;
+
+extern void rcu_free_dl_info(struct rcu_head *);
+extern void unhash_dl_info(struct dl_info *);
+
+extern struct dl_info *locate_dl_info(struct super_block *, xid_t);
+
+
+struct kstatfs;
+
+extern void vx_vsi_statfs(struct super_block *, struct kstatfs *);
+
+
+extern int vc_add_dlimit(uint32_t, void __user *);
+extern int vc_rem_dlimit(uint32_t, void __user *);
+
+extern int vc_set_dlimit(uint32_t, void __user *);
+extern int vc_get_dlimit(uint32_t, void __user *);
+
+
+typedef uint64_t dlsize_t;
+
+#endif	/* __KERNEL__ */
+#else	/* _VX_DLIMIT_H */
+#warning duplicate inclusion
+#endif	/* _VX_DLIMIT_H */
--- linux-2.6.11-rc1/kernel/vserver/dlimit.c	1970-01-01 01:00:00 +0100
+++ linux-2.6.11-rc1-vs1.9.4-rc2/kernel/vserver/dlimit.c	2005-01-15 11:27:52 +0100
@@ -0,0 +1,450 @@
+/*
+ *  linux/kernel/vserver/dlimit.c
+ *
+ *  Virtual Server: Context Disk Limits
+ *
+ *  Copyright (C) 2004  Herbert Pötzl
+ *
+ *  V0.01  initial version
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/fs.h>
+#include <linux/namespace.h>
+#include <linux/namei.h>
+#include <linux/statfs.h>
+#include <linux/vserver/switch.h>
+#include <linux/vs_context.h>
+#include <linux/vs_dlimit.h>
+
+#include <asm/errno.h>
+#include <asm/uaccess.h>
+
+/*	__alloc_dl_info()
+
+	* allocate an initialized dl_info struct
+	* doesn't make it visible (hash)			*/
+
+static struct dl_info *__alloc_dl_info(struct super_block *sb, xid_t xid)
+{
+	struct dl_info *new = NULL;
+
+	vxdprintk(VXD_CBIT(dlim, 5),
+		"alloc_dl_info(%p,%d)*", sb, xid);
+
+	/* would this benefit from a slab cache? */
+	new = kmalloc(sizeof(struct dl_info), GFP_KERNEL);
+	if (!new)
+		return 0;
+
+	memset (new, 0, sizeof(struct dl_info));
+	new->dl_xid = xid;
+	new->dl_sb = sb;
+	INIT_RCU_HEAD(&new->dl_rcu);
+	INIT_HLIST_NODE(&new->dl_hlist);
+	spin_lock_init(&new->dl_lock);
+	atomic_set(&new->dl_refcnt, 0);
+	atomic_set(&new->dl_usecnt, 0);
+
+	/* rest of init goes here */
+
+	vxdprintk(VXD_CBIT(dlim, 4),
+		"alloc_dl_info(%p,%d) = %p", sb, xid, new);
+	return new;
+}
+
+/*	__dealloc_dl_info()
+
+	* final disposal of dl_info				*/
+
+static void __dealloc_dl_info(struct dl_info *dli)
+{
+	vxdprintk(VXD_CBIT(dlim, 4),
+		"dealloc_dl_info(%p)", dli);
+
+	dli->dl_hlist.next = LIST_POISON1;
+	dli->dl_xid = -1;
+	dli->dl_sb = 0;
+
+	BUG_ON(atomic_read(&dli->dl_usecnt));
+	BUG_ON(atomic_read(&dli->dl_refcnt));
+
+	kfree(dli);
+}
+
+
+/*	hash table for dl_info hash */
+
+#define DL_HASH_SIZE	13
+
+struct hlist_head dl_info_hash[DL_HASH_SIZE];
+
+static spinlock_t dl_info_hash_lock = SPIN_LOCK_UNLOCKED;
+
+
+static inline unsigned int __hashval(struct super_block *sb, xid_t xid)
+{
+	return ((xid ^ (unsigned long)sb) % DL_HASH_SIZE);
+}
+
+
+
+/*	__hash_dl_info()
+
+	* add the dli to the global hash table
+	* requires the hash_lock to be held			*/
+
+static inline void __hash_dl_info(struct dl_info *dli)
+{
+	struct hlist_head *head;
+
+	vxdprintk(VXD_CBIT(dlim, 6),
+		"__hash_dl_info: %p[#%d]", dli, dli->dl_xid);
+	get_dl_info(dli);
+	head = &dl_info_hash[__hashval(dli->dl_sb, dli->dl_xid)];
+	hlist_add_head_rcu(&dli->dl_hlist, head);
+}
+
+/*	__unhash_dl_info()
+
+	* remove the dli from the global hash table
+	* requires the hash_lock to be held			*/
+
+static inline void __unhash_dl_info(struct dl_info *dli)
+{
+	vxdprintk(VXD_CBIT(dlim, 6),
+		"__unhash_dl_info: %p[#%d]", dli, dli->dl_xid);
+	hlist_del_rcu(&dli->dl_hlist);
+	put_dl_info(dli);
+}
+
+
+/*	__lookup_dl_info()
+
+	* requires the rcu_read_lock()
+	* doesn't increment the dl_refcnt			*/
+
+static inline struct dl_info *__lookup_dl_info(struct super_block *sb, xid_t xid)
+{
+	struct hlist_head *head = &dl_info_hash[__hashval(sb, xid)];
+	struct hlist_node *pos;
+
+	hlist_for_each_rcu(pos, head) {
+		struct dl_info *dli =
+			hlist_entry(pos, struct dl_info, dl_hlist);
+
+		if (dli->dl_xid == xid && dli->dl_sb == sb) {
+			return dli;
+		}
+	}
+	return NULL;
+}
+
+
+struct dl_info *locate_dl_info(struct super_block *sb, xid_t xid)
+{
+	struct dl_info *dli;
+
+	rcu_read_lock();
+	dli = get_dl_info(__lookup_dl_info(sb, xid));
+	vxdprintk(VXD_CBIT(dlim, 7),
+		"locate_dl_info(%p,#%d) = %p", sb, xid, dli);
+	rcu_read_unlock();
+	return dli;
+}
+
+void rcu_free_dl_info(struct rcu_head *head)
+{
+	struct dl_info *dli = container_of(head, struct dl_info, dl_rcu);
+	int usecnt, refcnt;
+
+	BUG_ON(!dli || !head);
+
+	usecnt = atomic_read(&dli->dl_usecnt);
+	BUG_ON(usecnt < 0);
+
+	refcnt = atomic_read(&dli->dl_refcnt);
+	BUG_ON(refcnt < 0);
+
+	vxdprintk(VXD_CBIT(dlim, 3),
+		"rcu_free_dl_info(%p)", dli);
+	if (!usecnt)
+		__dealloc_dl_info(dli);
+	else
+		printk("!!! rcu didn't free\n");
+}
+
+
+
+
+int vc_add_dlimit(uint32_t id, void __user *data)
+{
+	struct nameidata nd;
+	struct vcmd_ctx_dlimit_base_v0 vc_data;
+	int ret;
+
+	if (!vx_check(0, VX_ADMIN))
+		return -ENOSYS;
+	if (copy_from_user (&vc_data, data, sizeof(vc_data)))
+		return -EFAULT;
+
+	ret = user_path_walk_link(vc_data.name, &nd);
+	if (!ret) {
+		struct super_block *sb;
+		struct dl_info *dli;
+
+		ret = -EINVAL;
+		if (!nd.dentry->d_inode)
+			goto out_release;
+		if (!(sb = nd.dentry->d_inode->i_sb))
+			goto out_release;
+
+		dli = __alloc_dl_info(sb, id);
+		spin_lock(&dl_info_hash_lock);
+
+		ret = -EEXIST;
+		if (__lookup_dl_info(sb, id))
+			goto out_unlock;
+		__hash_dl_info(dli);
+		dli = NULL;
+		ret = 0;
+
+	out_unlock:
+		spin_unlock(&dl_info_hash_lock);
+		if (dli)
+			__dealloc_dl_info(dli);
+	out_release:
+		path_release(&nd);
+	}
+	return ret;
+}
+
+
+int vc_rem_dlimit(uint32_t id, void __user *data)
+{
+	struct nameidata nd;
+	struct vcmd_ctx_dlimit_base_v0 vc_data;
+	int ret;
+
+	if (!vx_check(0, VX_ADMIN))
+		return -ENOSYS;
+	if (copy_from_user (&vc_data, data, sizeof(vc_data)))
+		return -EFAULT;
+
+	ret = user_path_walk_link(vc_data.name, &nd);
+	if (!ret) {
+		struct super_block *sb;
+		struct dl_info *dli;
+
+		ret = -EINVAL;
+		if (!nd.dentry->d_inode)
+			goto out_release;
+		if (!(sb = nd.dentry->d_inode->i_sb))
+			goto out_release;
+
+		spin_lock(&dl_info_hash_lock);
+		dli = __lookup_dl_info(sb, id);
+
+		ret = -ESRCH;
+		if (!dli)
+			goto out_unlock;
+
+		__unhash_dl_info(dli);
+		ret = 0;
+
+	out_unlock:
+		spin_unlock(&dl_info_hash_lock);
+	out_release:
+		path_release(&nd);
+	}
+	return ret;
+}
+
+
+int vc_set_dlimit(uint32_t id, void __user *data)
+{
+	struct nameidata nd;
+	struct vcmd_ctx_dlimit_v0 vc_data;
+	int ret;
+
+	if (!vx_check(0, VX_ADMIN))
+		return -ENOSYS;
+	if (copy_from_user (&vc_data, data, sizeof(vc_data)))
+		return -EFAULT;
+
+	ret = user_path_walk_link(vc_data.name, &nd);
+	if (!ret) {
+		struct super_block *sb;
+		struct dl_info *dli;
+
+		ret = -EINVAL;
+		if (!nd.dentry->d_inode)
+			goto out_release;
+		if (!(sb = nd.dentry->d_inode->i_sb))
+			goto out_release;
+		if ((vc_data.reserved != (uint32_t)CDLIM_KEEP &&
+			vc_data.reserved > 100) ||
+			(vc_data.inodes_used != (uint32_t)CDLIM_KEEP &&
+			vc_data.inodes_used > vc_data.inodes_total) ||
+			(vc_data.space_used != (uint32_t)CDLIM_KEEP &&
+			vc_data.space_used > vc_data.space_total))
+			goto out_release;
+
+		ret = -ESRCH;
+		dli = locate_dl_info(sb, id);
+		if (!dli)
+			goto out_release;
+
+		spin_lock(&dli->dl_lock);
+
+		if (vc_data.inodes_used != (uint32_t)CDLIM_KEEP)
+			dli->dl_inodes_used = vc_data.inodes_used;
+		if (vc_data.inodes_total != (uint32_t)CDLIM_KEEP)
+			dli->dl_inodes_total = vc_data.inodes_total;
+		if (vc_data.space_used != (uint32_t)CDLIM_KEEP) {
+			dli->dl_space_used = vc_data.space_used;
+			dli->dl_space_used <<= 10;
+		}
+		if (vc_data.space_total == (uint32_t)CDLIM_INFINITY)
+			dli->dl_space_total = (uint64_t)CDLIM_INFINITY;
+		else if (vc_data.space_total != (uint32_t)CDLIM_KEEP) {
+			dli->dl_space_total = vc_data.space_total;
+			dli->dl_space_total <<= 10;
+		}
+		if (vc_data.reserved != (uint32_t)CDLIM_KEEP)
+			dli->dl_nrlmult = (1 << 10) * (100 - vc_data.reserved) / 100;
+
+		spin_unlock(&dli->dl_lock);
+
+		put_dl_info(dli);
+		ret = 0;
+
+	out_release:
+		path_release(&nd);
+	}
+	return ret;
+}
+
+int vc_get_dlimit(uint32_t id, void __user *data)
+{
+	struct nameidata nd;
+	struct vcmd_ctx_dlimit_v0 vc_data;
+	int ret;
+
+	if (!vx_check(0, VX_ADMIN))
+		return -ENOSYS;
+	if (copy_from_user (&vc_data, data, sizeof(vc_data)))
+		return -EFAULT;
+
+	ret = user_path_walk_link(vc_data.name, &nd);
+	if (!ret) {
+		struct super_block *sb;
+		struct dl_info *dli;
+
+		ret = -EINVAL;
+		if (!nd.dentry->d_inode)
+			goto out_release;
+		if (!(sb = nd.dentry->d_inode->i_sb))
+			goto out_release;
+		if (vc_data.reserved > 100 ||
+			vc_data.inodes_used > vc_data.inodes_total ||
+			vc_data.space_used > vc_data.space_total)
+			goto out_release;
+
+		ret = -ESRCH;
+		dli = locate_dl_info(sb, id);
+		if (!dli)
+			goto out_release;
+
+		spin_lock(&dli->dl_lock);
+		vc_data.inodes_used = dli->dl_inodes_used;
+		vc_data.inodes_total = dli->dl_inodes_total;
+		vc_data.space_used = dli->dl_space_used >> 10;
+		if (dli->dl_space_total == (uint64_t)CDLIM_INFINITY)
+			vc_data.space_total = (uint32_t)CDLIM_INFINITY;
+		else
+			vc_data.space_total = dli->dl_space_total >> 10;
+
+		vc_data.reserved = 100 - ((dli->dl_nrlmult * 100 + 512) >> 10);
+		spin_unlock(&dli->dl_lock);
+
+		put_dl_info(dli);
+		ret = -EFAULT;
+		if (copy_to_user(data, &vc_data, sizeof(vc_data)))
+			goto out_release;
+
+		ret = 0;
+	out_release:
+		path_release(&nd);
+	}
+	return ret;
+}
+
+
+void vx_vsi_statfs(struct super_block *sb, struct kstatfs *buf)
+{
+	struct dl_info *dli;
+	__u64 blimit, bfree, bavail;
+	__u32 ifree;
+
+	dli = locate_dl_info(sb, vx_current_xid());
+	if (!dli)
+		return;
+
+	spin_lock(&dli->dl_lock);
+	if (dli->dl_inodes_total == (uint32_t)CDLIM_INFINITY)
+		goto no_ilim;
+
+	/* reduce max inodes available to limit */
+	if (buf->f_files > dli->dl_inodes_total)
+		buf->f_files = dli->dl_inodes_total;
+
+	ifree = dli->dl_inodes_total - dli->dl_inodes_used;
+	/* reduce free inodes to min */
+	if (ifree < buf->f_ffree)
+		buf->f_ffree = ifree;
+
+no_ilim:
+	if (dli->dl_space_total == (uint64_t)CDLIM_INFINITY)
+		goto no_blim;
+
+	blimit = dli->dl_space_total >> sb->s_blocksize_bits;
+
+	if (dli->dl_space_total < dli->dl_space_used)
+		bfree = 0;
+	else
+		bfree = (dli->dl_space_total - dli->dl_space_used)
+			>> sb->s_blocksize_bits;
+
+	bavail = ((dli->dl_space_total >> 10) * dli->dl_nrlmult);
+	if (bavail < dli->dl_space_used)
+		bavail = 0;
+	else
+		bavail = (bavail - dli->dl_space_used)
+			>> sb->s_blocksize_bits;
+
+	/* reduce max space available to limit */
+	if (buf->f_blocks > blimit)
+		buf->f_blocks = blimit;
+
+	/* reduce free space to min */
+	if (bfree < buf->f_bfree)
+		buf->f_bfree = bfree;
+
+	/* reduce avail space to min */
+	if (bavail < buf->f_bavail)
+		buf->f_bavail = bavail;
+
+no_blim:
+	spin_unlock(&dli->dl_lock);
+	put_dl_info(dli);
+
+	return;
+}
+
+#include <linux/module.h>
+
+EXPORT_SYMBOL_GPL(locate_dl_info);
+EXPORT_SYMBOL_GPL(rcu_free_dl_info);
+
