--- linux-2.6.18.2/fs/ext2/balloc.c 2006-09-20 16:58:34 +0200 +++ linux-2.6.18.2-vs2.1.1/fs/ext2/balloc.c 2006-10-18 01:54:49 +0200 @@ -102,8 +104,8 @@ static int reserve_blocks(struct super_b { struct ext2_sb_info *sbi = EXT2_SB(sb); struct ext2_super_block *es = sbi->s_es; - unsigned free_blocks; - unsigned root_blocks; + unsigned long free_blocks; + unsigned long root_blocks; free_blocks = percpu_counter_read_positive(&sbi->s_freeblocks_counter); root_blocks = le32_to_cpu(es->s_r_blocks_count); --- linux-2.6.18.2/fs/ext2/balloc.c 2006-09-20 16:58:34 +0200 +++ linux-2.6.18.2-vs2.1.1/fs/ext2/balloc.c 2006-10-18 01:54:49 +0200 @@ -108,6 +110,8 @@ ***** free_blocks = percpu_counter_read_positive(&sbi->s_freeblocks_counter); root_blocks = le32_to_cpu(es->s_r_blocks_count); + DLIMIT_ADJUST_BLOCK(sb, dx_current_tag(), &free_blocks, &root_blocks); + if (free_blocks < count) count = free_blocks; --- linux-2.6.18.2/fs/ext2/balloc.c 2006-09-20 16:58:34 +0200 +++ linux-2.6.18.2-vs2.1.1/fs/ext2/balloc.c 2006-10-18 01:54:49 +0200 @@ -258,6 +262,7 @@ do_more: } error_return: brelse(bitmap_bh); + DLIMIT_FREE_BLOCK(inode, freed); release_blocks(sb, freed); DQUOT_FREE_BLOCK(inode, freed); } --- linux-2.6.18.2/fs/ext2/balloc.c 2006-09-20 16:58:34 +0200 +++ linux-2.6.18.2-vs2.1.1/fs/ext2/balloc.c 2006-10-18 01:54:49 +0200 @@ -361,6 +366,10 @@ int ext2_new_block(struct inode *inode, *err = -ENOSPC; goto out_dquot; } + if (DLIMIT_ALLOC_BLOCK(inode, es_alloc)) { + *err = -ENOSPC; + goto out_dlimit; + } ext2_debug ("goal=%lu.\n", goal); --- linux-2.6.18.2/fs/ext2/balloc.c 2006-09-20 16:58:34 +0200 +++ linux-2.6.18.2-vs2.1.1/fs/ext2/balloc.c 2006-10-18 01:54:49 +0200 @@ -508,6 +517,8 @@ got_block: *err = 0; out_release: group_release_blocks(sb, group_no, desc, gdp_bh, group_alloc); + DLIMIT_FREE_BLOCK(inode, es_alloc); +out_dlimit: release_blocks(sb, es_alloc); out_dquot: DQUOT_FREE_BLOCK(inode, dq_alloc); --- linux-2.6.18.2/fs/ext2/ialloc.c 2006-09-20 16:58:34 +0200 +++ linux-2.6.18.2-vs2.1.1/fs/ext2/ialloc.c 2006-09-25 15:40:02 +0200 @@ -125,6 +127,7 @@ void ext2_free_inode (struct inode * ino ext2_xattr_delete_inode(inode); DQUOT_FREE_INODE(inode); DQUOT_DROP(inode); + DLIMIT_FREE_INODE(inode); } es = EXT2_SB(sb)->s_es; --- linux-2.6.18.2/fs/ext2/ialloc.c 2006-09-20 16:58:34 +0200 +++ linux-2.6.18.2-vs2.1.1/fs/ext2/ialloc.c 2006-09-25 15:40:02 +0200 @@ -464,6 +467,11 @@ struct inode *ext2_new_inode(struct inod if (!inode) return ERR_PTR(-ENOMEM); + inode->i_tag = dx_current_fstag(sb); + if (DLIMIT_ALLOC_INODE(inode)) { + err = -ENOSPC; + goto fail_dlim; + } ei = EXT2_I(inode); sbi = EXT2_SB(sb); es = sbi->s_es; --- linux-2.6.18.2/fs/ext2/ialloc.c 2006-09-20 16:58:34 +0200 +++ linux-2.6.18.2-vs2.1.1/fs/ext2/ialloc.c 2006-09-25 15:40:02 +0200 @@ -626,6 +635,7 @@ fail_free_drop: fail_drop: DQUOT_DROP(inode); + DLIMIT_FREE_INODE(inode); inode->i_flags |= S_NOQUOTA; inode->i_nlink = 0; iput(inode); --- linux-2.6.18.2/fs/ext2/ialloc.c 2006-09-20 16:58:34 +0200 +++ linux-2.6.18.2-vs2.1.1/fs/ext2/ialloc.c 2006-09-25 15:40:02 +0200 @@ -632,6 +642,8 @@ ***** return ERR_PTR(err); fail: + DLIMIT_FREE_INODE(inode); +fail_dlim: make_bad_inode(inode); iput(inode); return ERR_PTR(err); --- linux-2.6.18.2/fs/ext2/xattr.c 2006-02-18 14:40:21 +0100 +++ linux-2.6.18.2-vs2.1.1/fs/ext2/xattr.c 2006-09-20 17:01:44 +0200 @@ -645,8 +646,12 @@ ext2_xattr_set2(struct inode *inode, str the inode. */ ea_bdebug(new_bh, "reusing block"); + error = -ENOSPC; + if (DLIMIT_ALLOC_BLOCK(inode, 1)) + goto cleanup; error = -EDQUOT; if (DQUOT_ALLOC_BLOCK(inode, 1)) { + DLIMIT_FREE_BLOCK(inode, 1); unlock_buffer(new_bh); goto cleanup; } --- linux-2.6.18.2/fs/ext2/xattr.c 2006-02-18 14:40:21 +0100 +++ linux-2.6.18.2-vs2.1.1/fs/ext2/xattr.c 2006-09-20 17:01:44 +0200 @@ -740,6 +745,7 @@ ext2_xattr_set2(struct inode *inode, str le32_to_cpu(HDR(old_bh)->h_refcount) - 1); if (ce) mb_cache_entry_release(ce); + DLIMIT_FREE_BLOCK(inode, 1); DQUOT_FREE_BLOCK(inode, 1); mark_buffer_dirty(old_bh); ea_bdebug(old_bh, "refcount now=%d", --- linux-2.6.18.2/fs/ext2/xattr.c 2006-02-18 14:40:21 +0100 +++ linux-2.6.18.2-vs2.1.1/fs/ext2/xattr.c 2006-09-20 17:01:44 +0200 @@ -804,6 +810,7 @@ ext2_xattr_delete_inode(struct inode *in mark_buffer_dirty(bh); if (IS_SYNC(inode)) sync_dirty_buffer(bh); + DLIMIT_FREE_BLOCK(inode, 1); DQUOT_FREE_BLOCK(inode, 1); } EXT2_I(inode)->i_file_acl = 0; --- linux-2.6.18.2/fs/ext3/balloc.c 2006-09-20 16:58:34 +0200 +++ linux-2.6.18.2-vs2.1.1/fs/ext3/balloc.c 2006-10-18 01:54:49 +0200 @@ -503,8 +505,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(inode, dquot_freed_blocks); DQUOT_FREE_BLOCK(inode, dquot_freed_blocks); + } return; } --- linux-2.6.18.2/fs/ext3/balloc.c 2006-09-20 16:58:34 +0200 +++ linux-2.6.18.2-vs2.1.1/fs/ext3/balloc.c 2006-10-18 01:54:49 +0200 @@ -1161,9 +1165,11 @@ out: return ret; } -static int ext3_has_free_blocks(struct ext3_sb_info *sbi) +static int ext3_has_free_blocks(struct super_block *sb) { + struct ext3_sb_info *sbi = EXT3_SB(sb); ext3_fsblk_t free_blocks, root_blocks; + int cond; free_blocks = percpu_counter_read_positive(&sbi->s_freeblocks_counter); root_blocks = le32_to_cpu(sbi->s_es->s_r_blocks_count); --- linux-2.6.18.2/fs/ext3/balloc.c 2006-09-20 16:58:34 +0200 +++ linux-2.6.18.2-vs2.1.1/fs/ext3/balloc.c 2006-10-18 01:54:49 +0200 @@ -1167,12 +1173,25 @@ ***** 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) && - sbi->s_resuid != current->fsuid && - (sbi->s_resgid == 0 || !in_group_p (sbi->s_resgid))) { - return 0; - } - return 1; + + vxdprintk(VXD_CBIT(dlim, 3), + "ext3_has_free_blocks(%p): free=%lu, root=%lu", + sb, free_blocks, root_blocks); + + DLIMIT_ADJUST_BLOCK(sb, dx_current_tag(), &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))); + + vxdprintk(VXD_CBIT(dlim, 3), + "ext3_has_free_blocks(%p): %lu<%lu+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.18.2/fs/ext3/balloc.c 2006-09-20 16:58:34 +0200 +++ linux-2.6.18.2-vs2.1.1/fs/ext3/balloc.c 2006-10-18 01:54:49 +0200 @@ -1183,7 +1202,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.18.2/fs/ext3/balloc.c 2006-09-20 16:58:34 +0200 +++ linux-2.6.18.2-vs2.1.1/fs/ext3/balloc.c 2006-10-18 01:54:49 +0200 @@ -1240,6 +1259,8 @@ ext3_fsblk_t ext3_new_blocks(handle_t *h *errp = -EDQUOT; return 0; } + if (DLIMIT_ALLOC_BLOCK(inode, 1)) + goto out_dlimit; sbi = EXT3_SB(sb); es = EXT3_SB(sb)->s_es; --- linux-2.6.18.2/fs/ext3/balloc.c 2006-09-20 16:58:34 +0200 +++ linux-2.6.18.2-vs2.1.1/fs/ext3/balloc.c 2006-10-18 01:54:49 +0200 @@ -1256,7 +1277,7 @@ ext3_fsblk_t ext3_new_blocks(handle_t *h if (block_i && ((windowsz = block_i->rsv_window_node.rsv_goal_size) > 0)) my_rsv = &block_i->rsv_window_node; - if (!ext3_has_free_blocks(sbi)) { + if (!ext3_has_free_blocks(sb)) { *errp = -ENOSPC; goto out; } --- linux-2.6.18.2/fs/ext3/balloc.c 2006-09-20 16:58:34 +0200 +++ linux-2.6.18.2-vs2.1.1/fs/ext3/balloc.c 2006-10-18 01:54:49 +0200 @@ -1450,6 +1471,9 @@ allocated: io_error: *errp = -EIO; out: + if (!performed_allocation) + DLIMIT_FREE_BLOCK(inode, 1); +out_dlimit: if (fatal) { *errp = fatal; ext3_std_error(sb, fatal); --- linux-2.6.18.2/fs/ext3/ialloc.c 2006-09-20 16:58:34 +0200 +++ linux-2.6.18.2-vs2.1.1/fs/ext3/ialloc.c 2006-09-25 15:40:02 +0200 @@ -127,6 +129,7 @@ void ext3_free_inode (handle_t *handle, ext3_xattr_delete_inode(handle, inode); DQUOT_FREE_INODE(inode); DQUOT_DROP(inode); + DLIMIT_FREE_INODE(inode); is_directory = S_ISDIR(inode->i_mode); --- linux-2.6.18.2/fs/ext3/ialloc.c 2006-09-20 16:58:34 +0200 +++ linux-2.6.18.2-vs2.1.1/fs/ext3/ialloc.c 2006-09-25 15:40:02 +0200 @@ -445,6 +448,12 @@ struct inode *ext3_new_inode(handle_t *h inode = new_inode(sb); if (!inode) return ERR_PTR(-ENOMEM); + + inode->i_tag = dx_current_fstag(sb); + if (DLIMIT_ALLOC_INODE(inode)) { + err = -ENOSPC; + goto out_dlimit; + } ei = EXT3_I(inode); sbi = EXT3_SB(sb); --- linux-2.6.18.2/fs/ext3/ialloc.c 2006-09-20 16:58:34 +0200 +++ linux-2.6.18.2-vs2.1.1/fs/ext3/ialloc.c 2006-09-25 15:40:02 +0200 @@ -622,6 +632,8 @@ got: fail: ext3_std_error(sb, err); out: + DLIMIT_FREE_INODE(inode); +out_dlimit: iput(inode); ret = ERR_PTR(err); really_out: --- linux-2.6.18.2/fs/ext3/ialloc.c 2006-09-20 16:58:34 +0200 +++ linux-2.6.18.2-vs2.1.1/fs/ext3/ialloc.c 2006-09-25 15:40:02 +0200 @@ -633,6 +645,7 @@ fail_free_drop: fail_drop: DQUOT_DROP(inode); + DLIMIT_FREE_INODE(inode); inode->i_flags |= S_NOQUOTA; inode->i_nlink = 0; iput(inode); --- linux-2.6.18.2/fs/ext3/xattr.c 2006-09-20 16:58:34 +0200 +++ linux-2.6.18.2-vs2.1.1/fs/ext3/xattr.c 2006-09-20 17:01:44 +0200 @@ -495,6 +496,7 @@ ext3_xattr_release_block(handle_t *handl ext3_journal_dirty_metadata(handle, bh); if (IS_SYNC(inode)) handle->h_sync = 1; + DLIMIT_FREE_BLOCK(inode, 1); DQUOT_FREE_BLOCK(inode, 1); unlock_buffer(bh); ea_bdebug(bh, "refcount now=%d; releasing", --- linux-2.6.18.2/fs/ext3/xattr.c 2006-09-20 16:58:34 +0200 +++ linux-2.6.18.2-vs2.1.1/fs/ext3/xattr.c 2006-09-20 17:01:44 +0200 @@ -763,6 +765,9 @@ inserted: if (new_bh == bs->bh) ea_bdebug(new_bh, "keeping"); else { + error = -ENOSPC; + if (DLIMIT_ALLOC_BLOCK(inode, 1)) + goto cleanup; /* The old block is released after updating the inode. */ error = -EDQUOT; --- linux-2.6.18.2/fs/ext3/xattr.c 2006-09-20 16:58:34 +0200 +++ linux-2.6.18.2-vs2.1.1/fs/ext3/xattr.c 2006-09-20 17:01:44 +0200 @@ -767,7 +772,7 @@ ***** the inode. */ error = -EDQUOT; if (DQUOT_ALLOC_BLOCK(inode, 1)) - goto cleanup; + goto cleanup_dlimit; error = ext3_journal_get_write_access(handle, new_bh); if (error) --- linux-2.6.18.2/fs/ext3/xattr.c 2006-09-20 16:58:34 +0200 +++ linux-2.6.18.2-vs2.1.1/fs/ext3/xattr.c 2006-09-20 17:01:44 +0200 @@ -844,6 +849,8 @@ cleanup: cleanup_dquot: DQUOT_FREE_BLOCK(inode, 1); +cleanup_dlimit: + DLIMIT_FREE_BLOCK(inode, 1); goto cleanup; bad_block: --- linux-2.6.18.2/fs/jfs/inode.c 2006-09-20 16:58:35 +0200 +++ linux-2.6.18.2-vs2.1.1/fs/jfs/inode.c 2006-09-20 17:01:44 +0200 @@ -144,6 +145,7 @@ void jfs_delete_inode(struct inode *inod DQUOT_INIT(inode); DQUOT_FREE_INODE(inode); DQUOT_DROP(inode); + DLIMIT_FREE_INODE(inode); } clear_inode(inode); --- linux-2.6.18.2/fs/jfs/jfs_dtree.c 2006-06-18 04:54:36 +0200 +++ linux-2.6.18.2-vs2.1.1/fs/jfs/jfs_dtree.c 2006-09-25 15:40:02 +0200 @@ -383,10 +384,10 @@ static u32 add_index(tid_t tid, struct i */ if (DQUOT_ALLOC_BLOCK(ip, sbi->nbperpage)) goto clean_up; - if (dbAlloc(ip, 0, sbi->nbperpage, &xaddr)) { - DQUOT_FREE_BLOCK(ip, sbi->nbperpage); - goto clean_up; - } + if (DLIMIT_ALLOC_BLOCK(ip, sbi->nbperpage)) + goto clean_up_dquot; + if (dbAlloc(ip, 0, sbi->nbperpage, &xaddr)) + goto clean_up_dlimit; /* * Save the table, we're going to overwrite it with the --- linux-2.6.18.2/fs/jfs/jfs_dtree.c 2006-06-18 04:54:36 +0200 +++ linux-2.6.18.2-vs2.1.1/fs/jfs/jfs_dtree.c 2006-09-25 15:40:02 +0200 @@ -479,6 +480,12 @@ static u32 add_index(tid_t tid, struct i return index; + clean_up_dlimit: + DLIMIT_FREE_BLOCK(ip, sbi->nbperpage); + + clean_up_dquot: + DQUOT_FREE_BLOCK(ip, sbi->nbperpage); + clean_up: jfs_ip->next_index--; --- linux-2.6.18.2/fs/jfs/jfs_dtree.c 2006-06-18 04:54:36 +0200 +++ linux-2.6.18.2-vs2.1.1/fs/jfs/jfs_dtree.c 2006-09-25 15:40:02 +0200 @@ -952,6 +959,7 @@ static int dtSplitUp(tid_t tid, struct tlock *tlck; struct lv *lv; int quota_allocation = 0; + int dlimit_allocation = 0; /* get split page */ smp = split->mp; --- linux-2.6.18.2/fs/jfs/jfs_dtree.c 2006-06-18 04:54:36 +0200 +++ linux-2.6.18.2-vs2.1.1/fs/jfs/jfs_dtree.c 2006-09-25 15:40:02 +0200 @@ -1036,6 +1044,12 @@ static int dtSplitUp(tid_t tid, } quota_allocation += n; + if (DLIMIT_ALLOC_BLOCK(ip, n)) { + rc = -ENOSPC; + goto extendOut; + } + dlimit_allocation += n; + if ((rc = dbReAlloc(sbi->ipbmap, xaddr, (s64) xlen, (s64) n, &nxaddr))) goto extendOut; --- linux-2.6.18.2/fs/jfs/jfs_dtree.c 2006-06-18 04:54:36 +0200 +++ linux-2.6.18.2-vs2.1.1/fs/jfs/jfs_dtree.c 2006-09-25 15:40:02 +0200 @@ -1309,6 +1323,9 @@ static int dtSplitUp(tid_t tid, freeKeyName: kfree(key.name); + /* Rollback dlimit allocation */ + if (rc && dlimit_allocation) + DLIMIT_FREE_BLOCK(ip, dlimit_allocation); /* Rollback quota allocation */ if (rc && quota_allocation) DQUOT_FREE_BLOCK(ip, quota_allocation); --- linux-2.6.18.2/fs/jfs/jfs_dtree.c 2006-06-18 04:54:36 +0200 +++ linux-2.6.18.2-vs2.1.1/fs/jfs/jfs_dtree.c 2006-09-25 15:40:02 +0200 @@ -1376,6 +1393,12 @@ static int dtSplitPage(tid_t tid, struct release_metapage(rmp); return -EDQUOT; } + /* Allocate blocks to dlimit. */ + if (DLIMIT_ALLOC_BLOCK(ip, lengthPXD(pxd))) { + DQUOT_FREE_BLOCK(ip, lengthPXD(pxd)); + release_metapage(rmp); + return -ENOSPC; + } jfs_info("dtSplitPage: ip:0x%p smp:0x%p rmp:0x%p", ip, smp, rmp); --- linux-2.6.18.2/fs/jfs/jfs_dtree.c 2006-06-18 04:54:36 +0200 +++ linux-2.6.18.2-vs2.1.1/fs/jfs/jfs_dtree.c 2006-09-25 15:40:02 +0200 @@ -1926,6 +1949,12 @@ static int dtSplitRoot(tid_t tid, release_metapage(rmp); return -EDQUOT; } + /* Allocate blocks to dlimit. */ + if (DLIMIT_ALLOC_BLOCK(ip, lengthPXD(pxd))) { + DQUOT_FREE_BLOCK(ip, lengthPXD(pxd)); + release_metapage(rmp); + return -ENOSPC; + } BT_MARK_DIRTY(rmp, ip); /* --- linux-2.6.18.2/fs/jfs/jfs_dtree.c 2006-06-18 04:54:36 +0200 +++ linux-2.6.18.2-vs2.1.1/fs/jfs/jfs_dtree.c 2006-09-25 15:40:02 +0200 @@ -2292,6 +2321,8 @@ static int dtDeleteUp(tid_t tid, struct xlen = lengthPXD(&fp->header.self); + /* Free dlimit allocation. */ + DLIMIT_FREE_BLOCK(ip, xlen); /* Free quota allocation. */ DQUOT_FREE_BLOCK(ip, xlen); --- linux-2.6.18.2/fs/jfs/jfs_dtree.c 2006-06-18 04:54:36 +0200 +++ linux-2.6.18.2-vs2.1.1/fs/jfs/jfs_dtree.c 2006-09-25 15:40:02 +0200 @@ -2368,6 +2399,8 @@ static int dtDeleteUp(tid_t tid, struct xlen = lengthPXD(&p->header.self); + /* Free dlimit allocation */ + DLIMIT_FREE_BLOCK(ip, xlen); /* Free quota allocation */ DQUOT_FREE_BLOCK(ip, xlen); --- linux-2.6.18.2/fs/jfs/jfs_extent.c 2006-09-20 16:58:35 +0200 +++ linux-2.6.18.2-vs2.1.1/fs/jfs/jfs_extent.c 2006-09-20 17:01:44 +0200 @@ -146,6 +147,13 @@ extAlloc(struct inode *ip, s64 xlen, s64 mutex_unlock(&JFS_IP(ip)->commit_mutex); return -EDQUOT; } + /* Allocate blocks to dlimit. */ + if (DLIMIT_ALLOC_BLOCK(ip, nxlen)) { + DQUOT_FREE_BLOCK(ip, nxlen); + dbFree(ip, nxaddr, (s64) nxlen); + mutex_unlock(&JFS_IP(ip)->commit_mutex); + return -ENOSPC; + } /* determine the value of the extent flag */ xflag = (abnr == TRUE) ? XAD_NOTRECORDED : 0; --- linux-2.6.18.2/fs/jfs/jfs_extent.c 2006-09-20 16:58:35 +0200 +++ linux-2.6.18.2-vs2.1.1/fs/jfs/jfs_extent.c 2006-09-20 17:01:44 +0200 @@ -164,6 +172,7 @@ extAlloc(struct inode *ip, s64 xlen, s64 */ if (rc) { dbFree(ip, nxaddr, nxlen); + DLIMIT_FREE_BLOCK(ip, nxlen); DQUOT_FREE_BLOCK(ip, nxlen); mutex_unlock(&JFS_IP(ip)->commit_mutex); return (rc); --- linux-2.6.18.2/fs/jfs/jfs_extent.c 2006-09-20 16:58:35 +0200 +++ linux-2.6.18.2-vs2.1.1/fs/jfs/jfs_extent.c 2006-09-20 17:01:44 +0200 @@ -261,6 +270,13 @@ int extRealloc(struct inode *ip, s64 nxl mutex_unlock(&JFS_IP(ip)->commit_mutex); return -EDQUOT; } + /* Allocate blocks to dlimit. */ + if (DLIMIT_ALLOC_BLOCK(ip, nxlen)) { + DQUOT_FREE_BLOCK(ip, nxlen); + dbFree(ip, nxaddr, (s64) nxlen); + up(&JFS_IP(ip)->commit_sem); + return -ENOSPC; + } delta = nxlen - xlen; --- linux-2.6.18.2/fs/jfs/jfs_extent.c 2006-09-20 16:58:35 +0200 +++ linux-2.6.18.2-vs2.1.1/fs/jfs/jfs_extent.c 2006-09-20 17:01:44 +0200 @@ -297,6 +313,7 @@ int extRealloc(struct inode *ip, s64 nxl /* extend the extent */ if ((rc = xtExtend(0, ip, xoff + xlen, (int) nextend, 0))) { dbFree(ip, xaddr + xlen, delta); + DLIMIT_FREE_BLOCK(ip, nxlen); DQUOT_FREE_BLOCK(ip, nxlen); goto exit; } --- linux-2.6.18.2/fs/jfs/jfs_extent.c 2006-09-20 16:58:35 +0200 +++ linux-2.6.18.2-vs2.1.1/fs/jfs/jfs_extent.c 2006-09-20 17:01:44 +0200 @@ -308,6 +325,7 @@ int extRealloc(struct inode *ip, s64 nxl */ if ((rc = xtTailgate(0, ip, xoff, (int) ntail, nxaddr, 0))) { dbFree(ip, nxaddr, nxlen); + DLIMIT_FREE_BLOCK(ip, nxlen); DQUOT_FREE_BLOCK(ip, nxlen); goto exit; } --- linux-2.6.18.2/fs/jfs/jfs_inode.c 2006-06-18 04:54:36 +0200 +++ linux-2.6.18.2-vs2.1.1/fs/jfs/jfs_inode.c 2006-09-25 15:40:02 +0200 @@ -89,6 +131,12 @@ struct inode *ialloc(struct inode *paren jfs_inode->saved_uid = inode->i_uid; jfs_inode->saved_gid = inode->i_gid; + inode->i_tag = dx_current_fstag(sb); + if (DLIMIT_ALLOC_INODE(inode)) { + iput(inode); + return NULL; + } + /* * Allocate inode to quota. */ --- linux-2.6.18.2/fs/jfs/jfs_inode.c 2006-06-18 04:54:36 +0200 +++ linux-2.6.18.2-vs2.1.1/fs/jfs/jfs_inode.c 2006-09-25 15:40:02 +0200 @@ -93,6 +141,7 @@ ***** * Allocate inode to quota. */ if (DQUOT_ALLOC_INODE(inode)) { + DLIMIT_FREE_INODE(inode); DQUOT_DROP(inode); inode->i_flags |= S_NOQUOTA; inode->i_nlink = 0; --- linux-2.6.18.2/fs/jfs/jfs_xtree.c 2006-01-03 17:29:57 +0100 +++ linux-2.6.18.2-vs2.1.1/fs/jfs/jfs_xtree.c 2006-09-20 17:01:44 +0200 @@ -841,7 +842,12 @@ int xtInsert(tid_t tid, /* transaction hint = 0; if ((rc = DQUOT_ALLOC_BLOCK(ip, xlen))) goto out; + if ((rc = DLIMIT_ALLOC_BLOCK(ip, xlen))) { + DQUOT_FREE_BLOCK(ip, xlen); + goto out; + } if ((rc = dbAlloc(ip, hint, (s64) xlen, &xaddr))) { + DLIMIT_FREE_BLOCK(ip, xlen); DQUOT_FREE_BLOCK(ip, xlen); goto out; } --- linux-2.6.18.2/fs/jfs/jfs_xtree.c 2006-01-03 17:29:57 +0100 +++ linux-2.6.18.2-vs2.1.1/fs/jfs/jfs_xtree.c 2006-09-20 17:01:44 +0200 @@ -871,6 +877,7 @@ int xtInsert(tid_t tid, /* transaction /* undo data extent allocation */ if (*xaddrp == 0) { dbFree(ip, xaddr, (s64) xlen); + DLIMIT_FREE_BLOCK(ip, xlen); DQUOT_FREE_BLOCK(ip, xlen); } return rc; --- linux-2.6.18.2/fs/jfs/jfs_xtree.c 2006-01-03 17:29:57 +0100 +++ linux-2.6.18.2-vs2.1.1/fs/jfs/jfs_xtree.c 2006-09-20 17:01:44 +0200 @@ -1231,6 +1238,7 @@ xtSplitPage(tid_t tid, struct inode *ip, struct tlock *tlck; struct xtlock *sxtlck = NULL, *rxtlck = NULL; int quota_allocation = 0; + int dlimit_allocation = 0; smp = split->mp; sp = XT_PAGE(ip, smp); --- linux-2.6.18.2/fs/jfs/jfs_xtree.c 2006-01-03 17:29:57 +0100 +++ linux-2.6.18.2-vs2.1.1/fs/jfs/jfs_xtree.c 2006-09-20 17:01:44 +0200 @@ -1250,6 +1258,13 @@ xtSplitPage(tid_t tid, struct inode *ip, quota_allocation += lengthPXD(pxd); + /* Allocate blocks to dlimit. */ + if (DLIMIT_ALLOC_BLOCK(ip, lengthPXD(pxd))) { + rc = -ENOSPC; + goto clean_up; + } + dlimit_allocation += lengthPXD(pxd); + /* * allocate the new right page for the split */ --- linux-2.6.18.2/fs/jfs/jfs_xtree.c 2006-01-03 17:29:57 +0100 +++ linux-2.6.18.2-vs2.1.1/fs/jfs/jfs_xtree.c 2006-09-20 17:01:44 +0200 @@ -1451,6 +1466,9 @@ xtSplitPage(tid_t tid, struct inode *ip, clean_up: + /* Rollback dlimit allocation. */ + if (dlimit_allocation) + DLIMIT_FREE_BLOCK(ip, dlimit_allocation); /* Rollback quota allocation. */ if (quota_allocation) DQUOT_FREE_BLOCK(ip, quota_allocation); --- linux-2.6.18.2/fs/jfs/jfs_xtree.c 2006-01-03 17:29:57 +0100 +++ linux-2.6.18.2-vs2.1.1/fs/jfs/jfs_xtree.c 2006-09-20 17:01:44 +0200 @@ -1515,6 +1533,12 @@ xtSplitRoot(tid_t tid, release_metapage(rmp); return -EDQUOT; } + /* Allocate blocks to dlimit. */ + if (DLIMIT_ALLOC_BLOCK(ip, lengthPXD(pxd))) { + DQUOT_FREE_BLOCK(ip, lengthPXD(pxd)); + release_metapage(rmp); + return -ENOSPC; + } jfs_info("xtSplitRoot: ip:0x%p rmp:0x%p", ip, rmp); --- linux-2.6.18.2/fs/jfs/jfs_xtree.c 2006-01-03 17:29:57 +0100 +++ linux-2.6.18.2-vs2.1.1/fs/jfs/jfs_xtree.c 2006-09-20 17:01:44 +0200 @@ -3941,6 +3965,8 @@ s64 xtTruncate(tid_t tid, struct inode * else ip->i_size = newsize; + /* update dlimit allocation to reflect freed blocks */ + DLIMIT_FREE_BLOCK(ip, nfreed); /* update quota allocation to reflect freed blocks */ DQUOT_FREE_BLOCK(ip, nfreed); --- linux-2.6.18.2/fs/jfs/xattr.c 2006-06-18 04:54:36 +0200 +++ linux-2.6.18.2-vs2.1.1/fs/jfs/xattr.c 2006-09-20 17:01:44 +0200 @@ -263,6 +264,11 @@ static int ea_write(struct inode *ip, st if (DQUOT_ALLOC_BLOCK(ip, nblocks)) { return -EDQUOT; } + /* Allocate new blocks to dlimit. */ + if (DLIMIT_ALLOC_BLOCK(ip, nblocks)) { + DQUOT_FREE_BLOCK(ip, nblocks); + return -ENOSPC; + } rc = dbAlloc(ip, INOHINT(ip), nblocks, &blkno); if (rc) { --- linux-2.6.18.2/fs/jfs/xattr.c 2006-06-18 04:54:36 +0200 +++ linux-2.6.18.2-vs2.1.1/fs/jfs/xattr.c 2006-09-20 17:01:44 +0200 @@ -266,6 +272,8 @@ ***** rc = dbAlloc(ip, INOHINT(ip), nblocks, &blkno); if (rc) { + /*Rollback dlimit allocation. */ + DLIMIT_FREE_BLOCK(ip, nblocks); /*Rollback quota allocation. */ DQUOT_FREE_BLOCK(ip, nblocks); return rc; --- linux-2.6.18.2/fs/jfs/xattr.c 2006-06-18 04:54:36 +0200 +++ linux-2.6.18.2-vs2.1.1/fs/jfs/xattr.c 2006-09-20 17:01:44 +0200 @@ -332,6 +340,8 @@ static int ea_write(struct inode *ip, st failed: /* Rollback quota allocation. */ + DLIMIT_FREE_BLOCK(ip, nblocks); + /* Rollback quota allocation. */ DQUOT_FREE_BLOCK(ip, nblocks); dbFree(ip, blkno, nblocks); --- linux-2.6.18.2/fs/jfs/xattr.c 2006-06-18 04:54:36 +0200 +++ linux-2.6.18.2-vs2.1.1/fs/jfs/xattr.c 2006-09-20 17:01:44 +0200 @@ -468,6 +478,7 @@ static int ea_get(struct inode *inode, s s64 blkno; int rc; int quota_allocation = 0; + int dlimit_allocation = 0; /* When fsck.jfs clears a bad ea, it doesn't clear the size */ if (ji->ea.flag == 0) --- linux-2.6.18.2/fs/jfs/xattr.c 2006-06-18 04:54:36 +0200 +++ linux-2.6.18.2-vs2.1.1/fs/jfs/xattr.c 2006-09-20 17:01:44 +0200 @@ -543,6 +554,12 @@ static int ea_get(struct inode *inode, s quota_allocation = blocks_needed; + /* Allocate new blocks to dlimit. */ + rc = -ENOSPC; + if (DLIMIT_ALLOC_BLOCK(inode, blocks_needed)) + goto clean_up; + dlimit_allocation = blocks_needed; + rc = dbAlloc(inode, INOHINT(inode), (s64) blocks_needed, &blkno); if (rc) --- linux-2.6.18.2/fs/jfs/xattr.c 2006-06-18 04:54:36 +0200 +++ linux-2.6.18.2-vs2.1.1/fs/jfs/xattr.c 2006-09-20 17:01:44 +0200 @@ -599,6 +616,9 @@ static int ea_get(struct inode *inode, s return ea_size; clean_up: + /* Rollback dlimit allocation */ + if (dlimit_allocation) + DLIMIT_FREE_BLOCK(inode, dlimit_allocation); /* Rollback quota allocation */ if (quota_allocation) DQUOT_FREE_BLOCK(inode, quota_allocation); --- linux-2.6.18.2/fs/jfs/xattr.c 2006-06-18 04:54:36 +0200 +++ linux-2.6.18.2-vs2.1.1/fs/jfs/xattr.c 2006-09-20 17:01:44 +0200 @@ -675,8 +695,10 @@ static int ea_put(tid_t tid, struct inod } /* If old blocks exist, they must be removed from quota allocation. */ - if (old_blocks) + if (old_blocks) { + DLIMIT_FREE_BLOCK(inode, old_blocks); DQUOT_FREE_BLOCK(inode, old_blocks); + } inode->i_ctime = CURRENT_TIME; --- linux-2.6.18.2/fs/reiserfs/bitmap.c 2006-09-20 16:58:35 +0200 +++ linux-2.6.18.2-vs2.1.1/fs/reiserfs/bitmap.c 2006-09-20 17:01:44 +0200 @@ -410,8 +411,10 @@ static void _reiserfs_free_block(struct set_sb_free_blocks(rs, sb_free_blocks(rs) + 1); journal_mark_dirty(th, s, sbh); - if (for_unformatted) + if (for_unformatted) { + DLIMIT_FREE_BLOCK(inode, 1); DQUOT_FREE_BLOCK_NODIRTY(inode, 1); + } } void reiserfs_free_block(struct reiserfs_transaction_handle *th, --- linux-2.6.18.2/fs/reiserfs/bitmap.c 2006-09-20 16:58:35 +0200 +++ linux-2.6.18.2-vs2.1.1/fs/reiserfs/bitmap.c 2006-09-20 17:01:44 +0200 @@ -1020,6 +1023,7 @@ static inline int blocknrs_and_prealloc_ int passno = 0; int nr_allocated = 0; int bigalloc = 0; + int blocks; determine_prealloc_size(hint); if (!hint->formatted_node) { --- linux-2.6.18.2/fs/reiserfs/bitmap.c 2006-09-20 16:58:35 +0200 +++ linux-2.6.18.2-vs2.1.1/fs/reiserfs/bitmap.c 2006-09-20 17:01:44 +0200 @@ -1029,10 +1033,16 @@ static inline int blocknrs_and_prealloc_ "reiserquota: allocating %d blocks id=%u", amount_needed, hint->inode->i_uid); #endif - quota_ret = - DQUOT_ALLOC_BLOCK_NODIRTY(hint->inode, amount_needed); - if (quota_ret) /* Quota exceeded? */ + quota_ret = DQUOT_ALLOC_BLOCK_NODIRTY(hint->inode, + amount_needed); + if (quota_ret) return QUOTA_EXCEEDED; + if (DLIMIT_ALLOC_BLOCK(hint->inode, amount_needed)) { + DQUOT_FREE_BLOCK_NODIRTY(hint->inode, + amount_needed); + return NO_DISK_SPACE; + } + if (hint->preallocate && hint->prealloc_size) { #ifdef REISERQUOTA_DEBUG reiserfs_debug(s, REISERFS_DEBUG_CODE, --- linux-2.6.18.2/fs/reiserfs/bitmap.c 2006-09-20 16:58:35 +0200 +++ linux-2.6.18.2-vs2.1.1/fs/reiserfs/bitmap.c 2006-09-20 17:01:44 +0200 @@ -1039,9 +1049,14 @@ ***** "reiserquota: allocating (prealloc) %d blocks id=%u", hint->prealloc_size, hint->inode->i_uid); #endif - quota_ret = - DQUOT_PREALLOC_BLOCK_NODIRTY(hint->inode, - hint->prealloc_size); + quota_ret = DQUOT_PREALLOC_BLOCK_NODIRTY(hint->inode, + hint->prealloc_size); + if (!quota_ret && + DLIMIT_ALLOC_BLOCK(hint->inode, hint->prealloc_size)) { + DQUOT_FREE_BLOCK_NODIRTY(hint->inode, + hint->prealloc_size); + quota_ret = 1; + } if (quota_ret) hint->preallocate = hint->prealloc_size = 0; } --- linux-2.6.18.2/fs/reiserfs/bitmap.c 2006-09-20 16:58:35 +0200 +++ linux-2.6.18.2-vs2.1.1/fs/reiserfs/bitmap.c 2006-09-20 17:01:44 +0200 @@ -1092,7 +1107,10 @@ static inline int blocknrs_and_prealloc_ nr_allocated, hint->inode->i_uid); #endif - DQUOT_FREE_BLOCK_NODIRTY(hint->inode, amount_needed + hint->prealloc_size - nr_allocated); /* Free not allocated blocks */ + /* Free not allocated blocks */ + blocks = amount_needed + hint->prealloc_size - nr_allocated; + DLIMIT_FREE_BLOCK(hint->inode, blocks); + DQUOT_FREE_BLOCK_NODIRTY(hint->inode, blocks); } while (nr_allocated--) reiserfs_free_block(hint->th, hint->inode, --- linux-2.6.18.2/fs/reiserfs/bitmap.c 2006-09-20 16:58:35 +0200 +++ linux-2.6.18.2-vs2.1.1/fs/reiserfs/bitmap.c 2006-09-20 17:01:44 +0200 @@ -1124,10 +1142,10 @@ static inline int blocknrs_and_prealloc_ REISERFS_I(hint->inode)->i_prealloc_count, hint->inode->i_uid); #endif - DQUOT_FREE_BLOCK_NODIRTY(hint->inode, amount_needed + - hint->prealloc_size - nr_allocated - - REISERFS_I(hint->inode)-> - i_prealloc_count); + blocks = amount_needed + hint->prealloc_size - nr_allocated - + REISERFS_I(hint->inode)->i_prealloc_count; + DLIMIT_FREE_BLOCK(hint->inode, blocks); + DQUOT_FREE_BLOCK_NODIRTY(hint->inode, blocks); } return CARRY_ON; --- linux-2.6.18.2/fs/reiserfs/inode.c 2006-09-20 16:58:35 +0200 +++ linux-2.6.18.2-vs2.1.1/fs/reiserfs/inode.c 2006-09-25 15:40:02 +0200 @@ -52,6 +54,7 @@ void reiserfs_delete_inode(struct inode * stat data deletion */ if (!err) DQUOT_FREE_INODE(inode); + DLIMIT_FREE_INODE(inode); if (journal_end(&th, inode->i_sb, jbegin_count)) goto out; --- linux-2.6.18.2/fs/reiserfs/inode.c 2006-09-20 16:58:35 +0200 +++ linux-2.6.18.2-vs2.1.1/fs/reiserfs/inode.c 2006-09-25 15:40:02 +0200 @@ -1779,6 +1792,10 @@ int reiserfs_new_inode(struct reiserfs_t BUG_ON(!th->t_trans_id); + if (DLIMIT_ALLOC_INODE(inode)) { + err = -ENOSPC; + goto out_bad_dlimit; + } if (DQUOT_ALLOC_INODE(inode)) { err = -EDQUOT; goto out_end_trans; --- linux-2.6.18.2/fs/reiserfs/inode.c 2006-09-20 16:58:35 +0200 +++ linux-2.6.18.2-vs2.1.1/fs/reiserfs/inode.c 2006-09-25 15:40:02 +0200 @@ -1964,6 +1981,9 @@ int reiserfs_new_inode(struct reiserfs_t DQUOT_FREE_INODE(inode); out_end_trans: + DLIMIT_FREE_INODE(inode); + + out_bad_dlimit: journal_end(th, th->t_super, th->t_blocks_allocated); /* Drop can be outside and it needs more credits so it's better to have it outside */ DQUOT_DROP(inode); --- linux-2.6.18.2/fs/reiserfs/stree.c 2006-09-20 16:58:35 +0200 +++ linux-2.6.18.2-vs2.1.1/fs/reiserfs/stree.c 2006-09-25 15:40:02 +0200 @@ -1297,6 +1298,7 @@ int reiserfs_delete_item(struct reiserfs "reiserquota delete_item(): freeing %u, id=%u type=%c", quota_cut_bytes, p_s_inode->i_uid, head2type(&s_ih)); #endif + DLIMIT_FREE_SPACE(p_s_inode, quota_cut_bytes); DQUOT_FREE_SPACE_NODIRTY(p_s_inode, quota_cut_bytes); /* Return deleted body length */ --- linux-2.6.18.2/fs/reiserfs/stree.c 2006-09-20 16:58:35 +0200 +++ linux-2.6.18.2-vs2.1.1/fs/reiserfs/stree.c 2006-09-25 15:40:02 +0200 @@ -1385,6 +1387,7 @@ void reiserfs_delete_solid_item(struct r #endif DQUOT_FREE_SPACE_NODIRTY(inode, quota_cut_bytes); + DLIMIT_FREE_SPACE(inode, quota_cut_bytes); } break; } --- linux-2.6.18.2/fs/reiserfs/stree.c 2006-09-20 16:58:35 +0200 +++ linux-2.6.18.2-vs2.1.1/fs/reiserfs/stree.c 2006-09-25 15:40:02 +0200 @@ -1740,6 +1743,7 @@ int reiserfs_cut_from_item(struct reiser "reiserquota cut_from_item(): freeing %u id=%u type=%c", quota_cut_bytes, p_s_inode->i_uid, '?'); #endif + DLIMIT_FREE_SPACE(p_s_inode, quota_cut_bytes); DQUOT_FREE_SPACE_NODIRTY(p_s_inode, quota_cut_bytes); return n_ret_value; } --- linux-2.6.18.2/fs/reiserfs/stree.c 2006-09-20 16:58:35 +0200 +++ linux-2.6.18.2-vs2.1.1/fs/reiserfs/stree.c 2006-09-25 15:40:02 +0200 @@ -1981,6 +1985,11 @@ int reiserfs_paste_into_item(struct reis pathrelse(p_s_search_path); return -EDQUOT; } + if (DLIMIT_ALLOC_SPACE(inode, n_pasted_size)) { + DQUOT_FREE_SPACE_NODIRTY(inode, n_pasted_size); + pathrelse(p_s_search_path); + return -ENOSPC; + } init_tb_struct(th, &s_paste_balance, th->t_super, p_s_search_path, n_pasted_size); #ifdef DISPLACE_NEW_PACKING_LOCALITIES --- linux-2.6.18.2/fs/reiserfs/stree.c 2006-09-20 16:58:35 +0200 +++ linux-2.6.18.2-vs2.1.1/fs/reiserfs/stree.c 2006-09-25 15:40:02 +0200 @@ -2033,6 +2042,7 @@ int reiserfs_paste_into_item(struct reis n_pasted_size, inode->i_uid, key2type(&(p_s_key->on_disk_key))); #endif + DLIMIT_FREE_SPACE(inode, n_pasted_size); DQUOT_FREE_SPACE_NODIRTY(inode, n_pasted_size); return retval; } --- linux-2.6.18.2/fs/reiserfs/stree.c 2006-09-20 16:58:35 +0200 +++ linux-2.6.18.2-vs2.1.1/fs/reiserfs/stree.c 2006-09-25 15:40:02 +0200 @@ -2070,6 +2080,11 @@ int reiserfs_insert_item(struct reiserfs pathrelse(p_s_path); return -EDQUOT; } + if (DLIMIT_ALLOC_SPACE(inode, quota_bytes)) { + DQUOT_FREE_SPACE_NODIRTY(inode, quota_bytes); + pathrelse(p_s_path); + return -ENOSPC; + } } init_tb_struct(th, &s_ins_balance, th->t_super, p_s_path, IH_SIZE + ih_item_len(p_s_ih)); --- linux-2.6.18.2/fs/reiserfs/stree.c 2006-09-20 16:58:35 +0200 +++ linux-2.6.18.2-vs2.1.1/fs/reiserfs/stree.c 2006-09-25 15:40:02 +0200 @@ -2117,7 +2132,9 @@ int reiserfs_insert_item(struct reiserfs "reiserquota insert_item(): freeing %u id=%u type=%c", quota_bytes, inode->i_uid, head2type(p_s_ih)); #endif - if (inode) + if (inode) { + DLIMIT_FREE_SPACE(inode, quota_bytes); DQUOT_FREE_SPACE_NODIRTY(inode, quota_bytes); + } return retval; } --- linux-2.6.18.2/include/linux/vserver/dlimit.h 1970-01-01 01:00:00 +0100 +++ linux-2.6.18.2-vs2.1.1/include/linux/vserver/dlimit.h 2006-09-25 15:40:02 +0200 @@ -0,0 +1,53 @@ +#ifndef _VX_DLIMIT_H +#define _VX_DLIMIT_H + +#include "switch.h" + + +#ifdef __KERNEL__ + +/* keep in sync with CDLIM_INFINITY */ + +#define DLIM_INFINITY (~0ULL) + +#include + +struct super_block; + +struct dl_info { + struct hlist_node dl_hlist; /* linked list of contexts */ + struct rcu_head dl_rcu; /* the rcu head */ + tag_t dl_tag; /* context tag */ + atomic_t dl_usecnt; /* usage count */ + atomic_t dl_refcnt; /* reference count */ + + struct super_block *dl_sb; /* associated superblock */ + + spinlock_t dl_lock; /* protect the values */ + + unsigned long long dl_space_used; /* used space in bytes */ + unsigned long long dl_space_total; /* maximum space in bytes */ + unsigned long dl_inodes_used; /* used inodes */ + unsigned long 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 *, tag_t); + + +struct kstatfs; + +extern void vx_vsi_statfs(struct super_block *, struct kstatfs *); + +typedef uint64_t dlsize_t; + +#endif /* __KERNEL__ */ +#else /* _VX_DLIMIT_H */ +#warning duplicate inclusion +#endif /* _VX_DLIMIT_H */ --- linux-2.6.18.2/kernel/vserver/dlimit.c 1970-01-01 01:00:00 +0100 +++ linux-2.6.18.2-vs2.1.1/kernel/vserver/dlimit.c 2006-09-25 15:40:02 +0200 @@ -0,0 +1,527 @@ +/* + * linux/kernel/vserver/dlimit.c + * + * Virtual Server: Context Disk Limits + * + * Copyright (C) 2004-2005 Herbert Pötzl + * + * V0.01 initial version + * V0.02 compat32 splitup + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +/* __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, tag_t tag) +{ + struct dl_info *new = NULL; + + vxdprintk(VXD_CBIT(dlim, 5), + "alloc_dl_info(%p,%d)*", sb, tag); + + /* 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_tag = tag; + 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, tag, 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_tag = -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, tag_t tag) +{ + return ((tag ^ (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_tag); + get_dl_info(dli); + head = &dl_info_hash[__hashval(dli->dl_sb, dli->dl_tag)]; + 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_tag); + 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, tag_t tag) +{ + struct hlist_head *head = &dl_info_hash[__hashval(sb, tag)]; + struct hlist_node *pos; + struct dl_info *dli; + + hlist_for_each_entry_rcu(dli, pos, head, dl_hlist) { + + if (dli->dl_tag == tag && dli->dl_sb == sb) { + return dli; + } + } + return NULL; +} + + +struct dl_info *locate_dl_info(struct super_block *sb, tag_t tag) +{ + struct dl_info *dli; + + rcu_read_lock(); + dli = get_dl_info(__lookup_dl_info(sb, tag)); + vxdprintk(VXD_CBIT(dlim, 7), + "locate_dl_info(%p,#%d) = %p", sb, tag, 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"); +} + + + + +static int do_addrem_dlimit(uint32_t id, const char __user *name, + uint32_t flags, int add) +{ + struct nameidata nd; + int ret; + + ret = user_path_walk_link(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 (add) { + 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; + } else { + 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); + if (add && dli) + __dealloc_dl_info(dli); + out_release: + path_release(&nd); + } + return ret; +} + +int vc_add_dlimit(uint32_t id, void __user *data) +{ + struct vcmd_ctx_dlimit_base_v0 vc_data; + + if (copy_from_user (&vc_data, data, sizeof(vc_data))) + return -EFAULT; + + return do_addrem_dlimit(id, vc_data.name, vc_data.flags, 1); +} + +int vc_rem_dlimit(uint32_t id, void __user *data) +{ + struct vcmd_ctx_dlimit_base_v0 vc_data; + + if (copy_from_user (&vc_data, data, sizeof(vc_data))) + return -EFAULT; + + return do_addrem_dlimit(id, vc_data.name, vc_data.flags, 0); +} + +#ifdef CONFIG_COMPAT + +int vc_add_dlimit_x32(uint32_t id, void __user *data) +{ + struct vcmd_ctx_dlimit_base_v0_x32 vc_data; + + if (copy_from_user (&vc_data, data, sizeof(vc_data))) + return -EFAULT; + + return do_addrem_dlimit(id, + compat_ptr(vc_data.name_ptr), vc_data.flags, 1); +} + +int vc_rem_dlimit_x32(uint32_t id, void __user *data) +{ + struct vcmd_ctx_dlimit_base_v0_x32 vc_data; + + if (copy_from_user (&vc_data, data, sizeof(vc_data))) + return -EFAULT; + + return do_addrem_dlimit(id, + compat_ptr(vc_data.name_ptr), vc_data.flags, 0); +} + +#endif /* CONFIG_COMPAT */ + + +static inline +int do_set_dlimit(uint32_t id, const char __user *name, + uint32_t space_used, uint32_t space_total, + uint32_t inodes_used, uint32_t inodes_total, + uint32_t reserved, uint32_t flags) +{ + struct nameidata nd; + int ret; + + ret = user_path_walk_link(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 ((reserved != CDLIM_KEEP && + reserved > 100) || + (inodes_used != CDLIM_KEEP && + inodes_used > inodes_total) || + (space_used != CDLIM_KEEP && + space_used > space_total)) + goto out_release; + + ret = -ESRCH; + dli = locate_dl_info(sb, id); + if (!dli) + goto out_release; + + spin_lock(&dli->dl_lock); + + if (inodes_used != CDLIM_KEEP) + dli->dl_inodes_used = inodes_used; + if (inodes_total != CDLIM_KEEP) + dli->dl_inodes_total = inodes_total; + if (space_used != CDLIM_KEEP) { + dli->dl_space_used = space_used; + dli->dl_space_used <<= 10; + } + if (space_total == CDLIM_INFINITY) + dli->dl_space_total = DLIM_INFINITY; + else if (space_total != CDLIM_KEEP) { + dli->dl_space_total = space_total; + dli->dl_space_total <<= 10; + } + if (reserved != CDLIM_KEEP) + dli->dl_nrlmult = (1 << 10) * (100 - reserved) / 100; + + spin_unlock(&dli->dl_lock); + + put_dl_info(dli); + ret = 0; + + out_release: + path_release(&nd); + } + return ret; +} + +int vc_set_dlimit(uint32_t id, void __user *data) +{ + struct vcmd_ctx_dlimit_v0 vc_data; + + if (copy_from_user (&vc_data, data, sizeof(vc_data))) + return -EFAULT; + + return do_set_dlimit(id, vc_data.name, + vc_data.space_used, vc_data.space_total, + vc_data.inodes_used, vc_data.inodes_total, + vc_data.reserved, vc_data.flags); +} + +#ifdef CONFIG_COMPAT + +int vc_set_dlimit_x32(uint32_t id, void __user *data) +{ + struct vcmd_ctx_dlimit_v0_x32 vc_data; + + if (copy_from_user (&vc_data, data, sizeof(vc_data))) + return -EFAULT; + + return do_set_dlimit(id, compat_ptr(vc_data.name_ptr), + vc_data.space_used, vc_data.space_total, + vc_data.inodes_used, vc_data.inodes_total, + vc_data.reserved, vc_data.flags); +} + +#endif /* CONFIG_COMPAT */ + + +static inline +int do_get_dlimit(uint32_t id, const char __user *name, + uint32_t *space_used, uint32_t *space_total, + uint32_t *inodes_used, uint32_t *inodes_total, + uint32_t *reserved, uint32_t *flags) +{ + struct nameidata nd; + int ret; + + ret = user_path_walk_link(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; + + ret = -ESRCH; + dli = locate_dl_info(sb, id); + if (!dli) + goto out_release; + + spin_lock(&dli->dl_lock); + *inodes_used = dli->dl_inodes_used; + *inodes_total = dli->dl_inodes_total; + *space_used = dli->dl_space_used >> 10; + if (dli->dl_space_total == DLIM_INFINITY) + *space_total = CDLIM_INFINITY; + else + *space_total = dli->dl_space_total >> 10; + + *reserved = 100 - ((dli->dl_nrlmult * 100 + 512) >> 10); + spin_unlock(&dli->dl_lock); + + put_dl_info(dli); + ret = -EFAULT; + + ret = 0; + out_release: + path_release(&nd); + } + return ret; +} + + +int vc_get_dlimit(uint32_t id, void __user *data) +{ + struct vcmd_ctx_dlimit_v0 vc_data; + int ret; + + if (copy_from_user (&vc_data, data, sizeof(vc_data))) + return -EFAULT; + + ret = do_get_dlimit(id, vc_data.name, + &vc_data.space_used, &vc_data.space_total, + &vc_data.inodes_used, &vc_data.inodes_total, + &vc_data.reserved, &vc_data.flags); + if (ret) + return ret; + + if (copy_to_user(data, &vc_data, sizeof(vc_data))) + return -EFAULT; + return 0; +} + +#ifdef CONFIG_COMPAT + +int vc_get_dlimit_x32(uint32_t id, void __user *data) +{ + struct vcmd_ctx_dlimit_v0_x32 vc_data; + int ret; + + if (copy_from_user (&vc_data, data, sizeof(vc_data))) + return -EFAULT; + + ret = do_get_dlimit(id, compat_ptr(vc_data.name_ptr), + &vc_data.space_used, &vc_data.space_total, + &vc_data.inodes_used, &vc_data.inodes_total, + &vc_data.reserved, &vc_data.flags); + if (ret) + return ret; + + if (copy_to_user(data, &vc_data, sizeof(vc_data))) + return -EFAULT; + return 0; +} + +#endif /* CONFIG_COMPAT */ + + +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, dx_current_tag()); + if (!dli) + return; + + spin_lock(&dli->dl_lock); + if (dli->dl_inodes_total == (unsigned long)DLIM_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 == DLIM_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 + +EXPORT_SYMBOL_GPL(locate_dl_info); +EXPORT_SYMBOL_GPL(rcu_free_dl_info); +