; ; Virtual Server Project ; Per Context Disk Limits ; ; (C) 2003 Herbert Pötzl ; ; Changelog: ; ; 0.02 - first public release ; - whitespace cleanups ; 0.03 - security fixes ; 0.04 - small bugfix in accounting ; - adaptation to cq0.10/cq0.11 ; 0.05 - added enforcement for ext3 ; - bugfix in dlimit_transfer ; - clamped negative values to zero ; - changed from blocksize to 1k ; 0.06 - adapted to vs1.1 devel branch ; - changed internal nomenclature ; ; this patch 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 2 ; of the License, or (at your option) any later version. ; ; this patch 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. ; diff -NurpP --minimal linux-2.4.23-rc5-vs1.1.6-qh0.12-cx0.07-cq0.12/fs/attr.c linux-2.4.23-rc5-vs1.1.6-qh0.12-cx0.07-cq0.12-dl0.06/fs/attr.c --- linux-2.4.23-rc5-vs1.1.6-qh0.12-cx0.07-cq0.12/fs/attr.c Sat Nov 29 17:14:59 2003 +++ linux-2.4.23-rc5-vs1.1.6-qh0.12-cx0.07-cq0.12-dl0.06/fs/attr.c Sat Nov 29 17:17:21 2003 @@ -133,6 +133,7 @@ int notify_change(struct dentry * dentry else { error = inode_change_ok(inode, attr); if (!error) { + /* should verify disk limit here */ if ((ia_valid & ATTR_UID && attr->ia_uid != inode->i_uid) || (ia_valid & ATTR_GID && attr->ia_gid != inode->i_gid)) error = DQUOT_TRANSFER(inode, attr) ? -EDQUOT : 0; diff -NurpP --minimal linux-2.4.23-rc5-vs1.1.6-qh0.12-cx0.07-cq0.12/fs/dquot.c linux-2.4.23-rc5-vs1.1.6-qh0.12-cx0.07-cq0.12-dl0.06/fs/dquot.c --- linux-2.4.23-rc5-vs1.1.6-qh0.12-cx0.07-cq0.12/fs/dquot.c Sat Nov 29 17:16:05 2003 +++ linux-2.4.23-rc5-vs1.1.6-qh0.12-cx0.07-cq0.12-dl0.06/fs/dquot.c Sat Nov 29 17:17:21 2003 @@ -454,6 +454,12 @@ struct dqhash *new_dqhash(struct super_b hash->dqh_qcop = sb->s_qcop; hash->dqh_sb = sb; + hash->dqh_dlimit.dl_ilimit = NOILIMIT; + hash->dqh_dlimit.dl_blimit = NOBLIMIT; + hash->dqh_dlimit.dl_l2bsize = sb->s_blocksize_bits; + hash->dqh_dlimit.dl_nrlmult = (1 << 10) * 90 / 100; + init_rwsem(&hash->dqh_dlimit.dl_sem); + lock_kernel(); dprintk ("ˇˇˇ new_dqhash: #%d %p (sb=%p,ctx=%d)\n", hash->dqh_id, hash, hash->dqh_sb, hash->dqh_xid); insert_dqhash_hash(hash); @@ -1399,6 +1405,30 @@ int dqhash_transfer(struct inode *inode, ret = __dquot_transfer(inode, &iattr, hash); return ret; } + +int dlimit_transfer(struct inode *inode, struct dqhash *hash) +{ + qsize_t space; + int ret = 0; + + if (hash == inode->i_dqh) + return ret; + + space = inode_get_bytes(inode); + + /* check for available space, and then? */ + + if (dqhash_valid(inode->i_dqh)) { + dlimit_decr_inodes(inode->i_dqh, 1); + dlimit_decr_space(inode->i_dqh, space); + } + if (dqhash_valid(hash)) { + dlimit_incr_inodes(hash, 1); + dlimit_incr_space(hash, space); + } + return ret; +} + /* * Definitions of diskquota operations. diff -NurpP --minimal linux-2.4.23-rc5-vs1.1.6-qh0.12-cx0.07-cq0.12/fs/ext2/balloc.c linux-2.4.23-rc5-vs1.1.6-qh0.12-cx0.07-cq0.12-dl0.06/fs/ext2/balloc.c --- linux-2.4.23-rc5-vs1.1.6-qh0.12-cx0.07-cq0.12/fs/ext2/balloc.c Fri Jun 13 16:51:37 2003 +++ linux-2.4.23-rc5-vs1.1.6-qh0.12-cx0.07-cq0.12-dl0.06/fs/ext2/balloc.c Sat Nov 29 17:17:21 2003 @@ -359,7 +359,7 @@ int ext2_new_block (struct inode * inode struct buffer_head * bh2; char * p, * r; int i, j, k, tmp; - int bitmap_nr; + int bitmap_nr, sys_res; struct super_block * sb; struct ext2_group_desc * gdp; struct ext2_super_block * es; @@ -374,12 +374,14 @@ int ext2_new_block (struct inode * inode } lock_super (sb); + sys_res = (sb->u.ext2_sb.s_resuid == current->fsuid) || + (sb->u.ext2_sb.s_resgid != 0 && + in_group_p (sb->u.ext2_sb.s_resgid)) || + capable(CAP_SYS_RESOURCE); + es = sb->u.ext2_sb.s_es; if (le32_to_cpu(es->s_free_blocks_count) <= le32_to_cpu(es->s_r_blocks_count) && - ((sb->u.ext2_sb.s_resuid != current->fsuid) && - (sb->u.ext2_sb.s_resgid == 0 || - !in_group_p (sb->u.ext2_sb.s_resgid)) && - !capable(CAP_SYS_RESOURCE))) + !sys_res) goto out; ext2_debug ("goal=%lu.\n", goal); @@ -508,7 +510,11 @@ got_block: /* * Check quota for allocation of this block. */ - if(DQUOT_ALLOC_BLOCK(inode, 1)) { + if (!dlimit_block_avail(inode->i_sb, sys_res, 1)) { + *err = -ENOSPC; + goto out; + } + if (DQUOT_ALLOC_BLOCK(inode, 1)) { *err = -EDQUOT; goto out; } @@ -553,6 +559,8 @@ got_block: for (k = 1; k < prealloc_goal && (j + k) < EXT2_BLOCKS_PER_GROUP(sb); k++, next_block++) { + if (!dlimit_block_avail(inode->i_sb, sys_res, 1)) + break; if (DQUOT_PREALLOC_BLOCK(inode, 1)) break; /* Writer: ->i_prealloc* */ diff -NurpP --minimal linux-2.4.23-rc5-vs1.1.6-qh0.12-cx0.07-cq0.12/fs/ext2/ialloc.c linux-2.4.23-rc5-vs1.1.6-qh0.12-cx0.07-cq0.12-dl0.06/fs/ext2/ialloc.c --- linux-2.4.23-rc5-vs1.1.6-qh0.12-cx0.07-cq0.12/fs/ext2/ialloc.c Thu Nov 27 16:00:23 2003 +++ linux-2.4.23-rc5-vs1.1.6-qh0.12-cx0.07-cq0.12-dl0.06/fs/ext2/ialloc.c Sat Nov 29 17:17:21 2003 @@ -327,6 +327,8 @@ struct inode * ext2_new_inode (const str inode = new_inode(sb); if (!inode) return ERR_PTR(-ENOMEM); + if (!dlimit_inodes_avail(sb, 1)) + return ERR_PTR(-ENOSPC); lock_super (sb); es = sb->u.ext2_sb.s_es; diff -NurpP --minimal linux-2.4.23-rc5-vs1.1.6-qh0.12-cx0.07-cq0.12/fs/ext3/balloc.c linux-2.4.23-rc5-vs1.1.6-qh0.12-cx0.07-cq0.12-dl0.06/fs/ext3/balloc.c --- linux-2.4.23-rc5-vs1.1.6-qh0.12-cx0.07-cq0.12/fs/ext3/balloc.c Fri Jun 13 16:51:37 2003 +++ linux-2.4.23-rc5-vs1.1.6-qh0.12-cx0.07-cq0.12-dl0.06/fs/ext3/balloc.c Sat Nov 29 17:17:21 2003 @@ -538,7 +538,7 @@ int ext3_new_block (handle_t *handle, st char * p, * r; #endif int i, j, k, tmp, alloctmp; - int bitmap_nr; + int bitmap_nr, sys_res; int fatal = 0, err; int performed_allocation = 0; struct super_block * sb; @@ -563,13 +563,20 @@ int ext3_new_block (handle_t *handle, st } lock_super (sb); + sys_res = (sb->u.ext3_sb.s_resuid == current->fsuid) || + (sb->u.ext3_sb.s_resgid != 0 && + in_group_p (sb->u.ext3_sb.s_resgid)) || + capable(CAP_SYS_RESOURCE); + + if (!dlimit_block_avail(inode->i_sb, sys_res, 1)) { + *errp = -ENOSPC; + goto out; + } + es = sb->u.ext3_sb.s_es; if (le32_to_cpu(es->s_free_blocks_count) <= le32_to_cpu(es->s_r_blocks_count) && - ((sb->u.ext3_sb.s_resuid != current->fsuid) && - (sb->u.ext3_sb.s_resgid == 0 || - !in_group_p (sb->u.ext3_sb.s_resgid)) && - !capable(CAP_SYS_RESOURCE))) + !sys_res) goto out; ext3_debug ("goal=%lu.\n", goal); @@ -747,6 +754,8 @@ got_block: for (k = 1; k < prealloc_goal && (j + k) < EXT3_BLOCKS_PER_GROUP(sb); k++, next_block++) { + if (!dlimit_block_avail(inode->i_sb, sys_res, 1)) + break; if (DQUOT_PREALLOC_BLOCK(inode, 1)) break; /* Writer: ->i_prealloc* */ diff -NurpP --minimal linux-2.4.23-rc5-vs1.1.6-qh0.12-cx0.07-cq0.12/fs/ext3/ialloc.c linux-2.4.23-rc5-vs1.1.6-qh0.12-cx0.07-cq0.12-dl0.06/fs/ext3/ialloc.c --- linux-2.4.23-rc5-vs1.1.6-qh0.12-cx0.07-cq0.12/fs/ext3/ialloc.c Thu Nov 27 16:00:23 2003 +++ linux-2.4.23-rc5-vs1.1.6-qh0.12-cx0.07-cq0.12-dl0.06/fs/ext3/ialloc.c Sat Nov 29 17:17:21 2003 @@ -318,6 +318,8 @@ struct inode * ext3_new_inode (handle_t inode = new_inode(sb); if (!inode) return ERR_PTR(-ENOMEM); + if (!dlimit_inodes_avail(sb, 1)) + return ERR_PTR(-ENOSPC); init_rwsem(&inode->u.ext3_i.truncate_sem); lock_super (sb); diff -NurpP --minimal linux-2.4.23-rc5-vs1.1.6-qh0.12-cx0.07-cq0.12/fs/open.c linux-2.4.23-rc5-vs1.1.6-qh0.12-cx0.07-cq0.12-dl0.06/fs/open.c --- linux-2.4.23-rc5-vs1.1.6-qh0.12-cx0.07-cq0.12/fs/open.c Sat Nov 29 17:14:59 2003 +++ linux-2.4.23-rc5-vs1.1.6-qh0.12-cx0.07-cq0.12-dl0.06/fs/open.c Sat Nov 29 17:17:21 2003 @@ -37,6 +37,79 @@ int vfs_statfs(struct super_block *sb, s } +static inline void ctx_modify_statfs(struct super_block *sb, struct statfs *buf) +{ + struct dqhash *dqh; + struct dqh_dlimit *dl; + __u64 blimit, bfree, bavail; + __u32 ifree; + + dqh = get_dqhash(sb, current->vx_id); + if (!dqhash_valid(dqh)) + return; + + dprintk("ˇˇˇ statfs %ld/%ld %ld/%ld/%ld (dqh=%p)\n", + buf->f_ffree, buf->f_files, + buf->f_bavail, buf->f_bfree, buf->f_blocks, + dqh); + + dl = &dqh->dqh_dlimit; + down_read(&dl->dl_sem); + + dprintk("ˇˇˇ dlimit %u/%u %Lu/%Lu\n", + dl->dl_inodes, dl->dl_ilimit, + dl->dl_bytes, dl->dl_blimit); + + if (dl->dl_ilimit == NOILIMIT) + goto no_ilim; + + /* reduce max inodes available to limit */ + if (buf->f_files > dl->dl_ilimit) + buf->f_files = dl->dl_ilimit; + + ifree = dl->dl_ilimit - dl->dl_inodes; + /* reduce free inodes to min */ + if (ifree < buf->f_ffree) + buf->f_ffree = ifree; + +no_ilim: + if (dl->dl_blimit == NOBLIMIT) + goto no_blim; + + blimit = dl->dl_blimit >> dl->dl_l2bsize; + + if (dl->dl_blimit < dl->dl_bytes) + bfree = 0; + else + bfree = (dl->dl_blimit - dl->dl_bytes) >> dl->dl_l2bsize; + + bavail = ((dl->dl_blimit >> 10) * dl->dl_nrlmult); + if (bavail < dl->dl_bytes) + bavail = 0; + else + bavail = (bavail - dl->dl_bytes) >> dl->dl_l2bsize; + + /* 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: + up_read(&dl->dl_sem); + dqhput(dqh); + + dprintk("ˇˇˇ sctxfs %ld/%ld %ld/%ld/%ld\n", + buf->f_ffree, buf->f_files, + buf->f_bavail, buf->f_bfree, buf->f_blocks); +} + asmlinkage long sys_statfs(const char * path, struct statfs * buf) { struct nameidata nd; @@ -46,6 +119,8 @@ asmlinkage long sys_statfs(const char * if (!error) { struct statfs tmp; error = vfs_statfs(nd.dentry->d_inode->i_sb, &tmp); + if (!error) + ctx_modify_statfs(nd.dentry->d_inode->i_sb, &tmp); if (!error && copy_to_user(buf, &tmp, sizeof(struct statfs))) error = -EFAULT; path_release(&nd); @@ -64,6 +139,8 @@ asmlinkage long sys_fstatfs(unsigned int if (!file) goto out; error = vfs_statfs(file->f_dentry->d_inode->i_sb, &tmp); + if (!error) + ctx_modify_statfs(file->f_dentry->d_inode->i_sb, &tmp); if (!error && copy_to_user(buf, &tmp, sizeof(struct statfs))) error = -EFAULT; fput(file); diff -NurpP --minimal linux-2.4.23-rc5-vs1.1.6-qh0.12-cx0.07-cq0.12/fs/quota.c linux-2.4.23-rc5-vs1.1.6-qh0.12-cx0.07-cq0.12-dl0.06/fs/quota.c --- linux-2.4.23-rc5-vs1.1.6-qh0.12-cx0.07-cq0.12/fs/quota.c Sat Nov 29 17:16:05 2003 +++ linux-2.4.23-rc5-vs1.1.6-qh0.12-cx0.07-cq0.12-dl0.06/fs/quota.c Sat Nov 29 17:17:21 2003 @@ -559,6 +559,82 @@ int do_dqhashctl(struct super_block *sb, return ret; } +int do_dqlimitctl(struct super_block *sb, uint cmds, qid_t id, caddr_t addr) +{ + int ctx = current->vx_id; + struct dqhash *dqh; + struct dqh_dlimit *dl; + struct dli_limits dli; + int ret = 0; + + if ((ctx != 0) && (ctx != 1)) + /* stealth should return EINVAL? */ + return -EPERM; + + dqh = get_dqhash(sb, id); + if (!dqhash_valid(dqh)) + return -ENXIO; + + if (cmds == Q_GET_DLIMIT) { + dl = &dqh->dqh_dlimit; + down_read(&dl->dl_sem); + + if (dl->dl_ilimit != NOILIMIT) { + dli.dli_inodes_total = dl->dl_ilimit; + dli.dli_inodes_used = dl->dl_inodes; + } + if (dl->dl_blimit != NOBLIMIT) { + dli.dli_blocks_total = dl->dl_blimit >> 10; + dli.dli_blocks_used = dl->dl_bytes >> 10; + } + dli.dli_reserved = 100 - ((dl->dl_nrlmult * 100 + + (1 << (10-1))) >> 10); + up_read(&dl->dl_sem); + + ret = -EFAULT; + if (copy_to_user(addr, &dli, sizeof(dli))) + goto out; + ret = 0; + } + if (cmds == Q_SET_DLIMIT) { + ret = -EFAULT; + if (copy_from_user(&dli, addr, sizeof(dli))) + goto out; + ret = -EINVAL; + if ((dli.dli_reserved > 100) || + (dli.dli_inodes_used > dli.dli_inodes_total) || + (dli.dli_blocks_used > dli.dli_blocks_total)) + goto out; + + dl = &dqh->dqh_dlimit; + down_write(&dl->dl_sem); + + if (dli.dli_inodes_total) + dl->dl_ilimit = dli.dli_inodes_total; + else + dl->dl_ilimit = NOILIMIT; + dl->dl_inodes = dli.dli_inodes_used; + + if (dli.dli_blocks_total) { + dl->dl_blimit = dli.dli_blocks_total; + dl->dl_blimit <<= 10; /* was dl->dl_l2bsize; */ + } + else + dl->dl_blimit = NOBLIMIT; + dl->dl_bytes = dli.dli_blocks_used; + dl->dl_bytes <<= 10; /* was dl->dl_l2bsize; */ + + /* calc mult for non-root free blocks */ + dl->dl_nrlmult = (1 << 10) * (100 - dli.dli_reserved) / 100; + + up_write(&dl->dl_sem); + ret = 0; + } +out: + dqhput(dqh); + return ret; +} + /* * This is the system call interface. This communicates with * the user-level programs. Currently this only supports diskquota @@ -584,6 +660,12 @@ asmlinkage long sys_quotactl(unsigned in else if (sb) { int ctx = current->vx_id; + if ((cmds == Q_SET_DLIMIT) || (cmds == Q_GET_DLIMIT)) { + ret = -EPERM; + if (ctx == 0) + ret = do_dqlimitctl(sb, cmds, id, addr); + goto out_sb; + } if ((cmds == Q_ADD_DQHASH) || (cmds == Q_REM_DQHASH)) { ret = -EPERM; if (ctx == 0) diff -NurpP --minimal linux-2.4.23-rc5-vs1.1.6-qh0.12-cx0.07-cq0.12/include/linux/quota.h linux-2.4.23-rc5-vs1.1.6-qh0.12-cx0.07-cq0.12-dl0.06/include/linux/quota.h --- linux-2.4.23-rc5-vs1.1.6-qh0.12-cx0.07-cq0.12/include/linux/quota.h Sat Nov 29 17:16:05 2003 +++ linux-2.4.23-rc5-vs1.1.6-qh0.12-cx0.07-cq0.12-dl0.06/include/linux/quota.h Sat Nov 29 17:19:45 2003 @@ -318,6 +318,18 @@ int register_quota_format(struct quota_f void unregister_quota_format(struct quota_format_type *fmt); void init_dquot_operations(struct dquot_operations *fsdqops); +struct dqh_dlimit { + __u32 dl_inodes; /* total inodes */ + __u32 dl_ilimit; /* maximum inodes */ + + __u64 dl_bytes; /* total bytes */ + __u64 dl_blimit; /* maximum bytes */ + + unsigned int dl_nrlmult; /* non root limit mult */ + unsigned int dl_l2bsize; /* bsize shift (bits) */ + struct rw_semaphore dl_sem; /* lock limits */ +}; + struct dqhash { struct list_head dqh_list; /* quota hashes within hash slot */ struct list_head dqh_sblist; /* all superblock quota hashes */ @@ -328,7 +340,18 @@ struct dqhash { struct dquot_operations *dqh_qop; struct quotactl_ops *dqh_qcop; struct super_block *dqh_sb; /* super block */ + struct dqh_dlimit dqh_dlimit; /* per hash disk limits */ struct list_head dqh_hash[NR_DQHASH]; +}; + +struct dli_limits { + __u32 dli_inodes_used; + __u32 dli_inodes_total; + + __u32 dli_blocks_used; + __u32 dli_blocks_total; + + __u32 dli_reserved; }; #if defined(CONFIG_QUOTA) diff -NurpP --minimal linux-2.4.23-rc5-vs1.1.6-qh0.12-cx0.07-cq0.12/include/linux/quotacompat.h linux-2.4.23-rc5-vs1.1.6-qh0.12-cx0.07-cq0.12-dl0.06/include/linux/quotacompat.h --- linux-2.4.23-rc5-vs1.1.6-qh0.12-cx0.07-cq0.12/include/linux/quotacompat.h Sat Nov 29 17:16:05 2003 +++ linux-2.4.23-rc5-vs1.1.6-qh0.12-cx0.07-cq0.12-dl0.06/include/linux/quotacompat.h Sat Nov 29 17:22:51 2003 @@ -85,5 +85,7 @@ struct v2c_dqstats { #define Q_ADD_DQHASH 0x2100 /* add dqhash */ #define Q_REM_DQHASH 0x2200 /* remove dqhash */ +#define Q_GET_DLIMIT 0x2300 /* get disk limit */ +#define Q_SET_DLIMIT 0x2400 /* set disk limit */ #endif diff -NurpP --minimal linux-2.4.23-rc5-vs1.1.6-qh0.12-cx0.07-cq0.12/include/linux/quotaops.h linux-2.4.23-rc5-vs1.1.6-qh0.12-cx0.07-cq0.12-dl0.06/include/linux/quotaops.h --- linux-2.4.23-rc5-vs1.1.6-qh0.12-cx0.07-cq0.12/include/linux/quotaops.h Sat Nov 29 17:16:05 2003 +++ linux-2.4.23-rc5-vs1.1.6-qh0.12-cx0.07-cq0.12-dl0.06/include/linux/quotaops.h Sat Nov 29 17:20:15 2003 @@ -37,6 +37,7 @@ extern void dquot_free_inode(const struc extern int dquot_transfer(struct inode *inode, struct iattr *iattr); extern int dqhash_transfer(struct inode *inode, struct dqhash *hash); +extern int dlimit_transfer(struct inode *inode, struct dqhash *hash); static __inline__ void __vx_dq_modify(struct inode *inode, unsigned int ctx) { @@ -44,6 +45,7 @@ static __inline__ void __vx_dq_modify(st if (dqh != inode->i_dqh) { dqhash_transfer(inode, dqh); + dlimit_transfer(inode, dqh); dqhput(inode->i_dqh); inode->i_dqh = dqh; } else @@ -92,6 +94,97 @@ static __inline__ void CHECK_DQHASH(stru } } +#define NOILIMIT ((__u32)0) +#define NOBLIMIT ((__u64)0) + +static __inline__ int dlimit_inodes_avail(struct super_block *sb, unsigned long number) +{ + struct dqhash *dqh = get_dqhash(sb, current->vx_id); + struct dqh_dlimit *dl; + int ret; + + if (!dqhash_valid(dqh)) + return 1; + + dl = &dqh->dqh_dlimit; + down_read(&dl->dl_sem); + ret = (dl->dl_ilimit == NOILIMIT) || + (dl->dl_ilimit >= (dl->dl_inodes + number)); + if (!ret) + dprintk("ˇˇˇ dlimit_inodes_avail: %lld + %ld > %lld\n", + dl->dl_inodes, number, dl->dl_ilimit); + up_read(&dl->dl_sem); + dqhput(dqh); + return ret; +} + +static __inline__ int dlimit_space_avail(struct super_block *sb, int sres, qsize_t number) +{ + struct dqhash *dqh = get_dqhash(sb, current->vx_id); + struct dqh_dlimit *dl; + int ret; + + if (!dqhash_valid(dqh)) + return 1; + + dl = &dqh->dqh_dlimit; + down_read(&dl->dl_sem); + ret = (dl->dl_blimit == NOBLIMIT) || ((sres ? dl->dl_blimit : + ((dl->dl_blimit * dl->dl_nrlmult) >> 10)) >= (dl->dl_bytes + number)); + if (!ret) + dprintk("ˇˇˇ dlimit_space_avail: %lld + %ld > %lld\n", + dl->dl_bytes, (long)number, dl->dl_blimit); + up_read(&dl->dl_sem); + dqhput(dqh); + return ret; +} + +#define dlimit_block_avail(sb, sr, nr) \ + dlimit_space_avail(sb, sr, ((unsigned long long)(nr)) << (sb)->s_blocksize_bits) + + +static __inline__ void dlimit_incr_inodes(struct dqhash *hash, unsigned long number) +{ + if (!dqhash_valid(hash)) + return; + down_write(&hash->dqh_dlimit.dl_sem); + hash->dqh_dlimit.dl_inodes += number; + up_write(&hash->dqh_dlimit.dl_sem); +} + +static __inline__ void dlimit_decr_inodes(struct dqhash *hash, unsigned long number) +{ + if (!dqhash_valid(hash)) + return; + down_write(&hash->dqh_dlimit.dl_sem); + if (hash->dqh_dlimit.dl_inodes > number) + hash->dqh_dlimit.dl_inodes -= number; + else + hash->dqh_dlimit.dl_inodes = 0; + up_write(&hash->dqh_dlimit.dl_sem); +} + +static __inline__ void dlimit_incr_space(struct dqhash *hash, qsize_t number) +{ + if (!dqhash_valid(hash)) + return; + down_write(&hash->dqh_dlimit.dl_sem); + hash->dqh_dlimit.dl_bytes += number; + up_write(&hash->dqh_dlimit.dl_sem); +} + +static __inline__ void dlimit_decr_space(struct dqhash *hash, qsize_t number) +{ + if (!dqhash_valid(hash)) + return; + down_write(&hash->dqh_dlimit.dl_sem); + if (hash->dqh_dlimit.dl_bytes > number) + hash->dqh_dlimit.dl_bytes -= number; + else + hash->dqh_dlimit.dl_bytes = 0; + up_write(&hash->dqh_dlimit.dl_sem); +} + /* * Operations supported for diskquotas. @@ -141,6 +234,7 @@ static __inline__ int DQUOT_PREALLOC_SPA } else inode_add_bytes(inode, nr); + dlimit_incr_space(inode->i_dqh, nr); unlock_kernel(); return 0; } @@ -166,6 +260,7 @@ static __inline__ int DQUOT_ALLOC_SPACE_ } else inode_add_bytes(inode, nr); + dlimit_incr_space(inode->i_dqh, nr); unlock_kernel(); return 0; } @@ -189,6 +284,7 @@ static __inline__ int DQUOT_ALLOC_INODE( return 1; } } + dlimit_incr_inodes(inode->i_dqh, 1); unlock_kernel(); return 0; } @@ -200,6 +296,7 @@ static __inline__ void DQUOT_FREE_SPACE_ inode->i_dqh->dqh_qop->free_space(inode, nr); else inode_sub_bytes(inode, nr); + dlimit_decr_space(inode->i_dqh, nr); unlock_kernel(); } @@ -212,6 +309,7 @@ static __inline__ void DQUOT_FREE_SPACE( static __inline__ void DQUOT_FREE_INODE(struct inode *inode) { lock_kernel(); + dlimit_decr_inodes(inode->i_dqh, 1); if (dqh_any_quota_enabled(inode->i_dqh)) inode->i_dqh->dqh_qop->free_inode(inode, 1); unlock_kernel();