From: Jan Kara Implement quota journaling and quota reading and writing functions for reiserfs. Solves also several other deadlocks possible for reiserfs due to the lock inversion on journal_begin and quota locks. Signed-off-by: Jan Kara Signed-off-by: Andrew Morton --- 25-akpm/fs/reiserfs/file.c | 6 25-akpm/fs/reiserfs/inode.c | 54 ++-- 25-akpm/fs/reiserfs/journal.c | 1 25-akpm/fs/reiserfs/namei.c | 60 ++-- 25-akpm/fs/reiserfs/super.c | 408 ++++++++++++++++++++++++++++++++- 25-akpm/include/linux/reiserfs_fs.h | 16 + 25-akpm/include/linux/reiserfs_fs_sb.h | 4 7 files changed, 491 insertions(+), 58 deletions(-) diff -puN fs/reiserfs/file.c~fix-of-quota-deadlock-on-pagelock-reiserfs fs/reiserfs/file.c --- 25/fs/reiserfs/file.c~fix-of-quota-deadlock-on-pagelock-reiserfs 2004-11-30 01:23:17.233147272 -0800 +++ 25-akpm/fs/reiserfs/file.c 2004-11-30 01:23:17.249144840 -0800 @@ -54,7 +54,7 @@ static int reiserfs_file_release (struct /* freeing preallocation only involves relogging blocks that * are already in the current transaction. preallocation gets * freed at the end of each transaction, so it is impossible for - * us to log any additional blocks + * us to log any additional blocks (including quota blocks) */ err = journal_begin(&th, inode->i_sb, 1); if (err) { @@ -201,7 +201,7 @@ int reiserfs_allocate_blocks_for_region( /* If we came here, it means we absolutely need to open a transaction, since we need to allocate some blocks */ reiserfs_write_lock(inode->i_sb); // Journaling stuff and we need that. - res = journal_begin(th, inode->i_sb, JOURNAL_PER_BALANCE_CNT * 3 + 1); // Wish I know if this number enough + res = journal_begin(th, inode->i_sb, JOURNAL_PER_BALANCE_CNT * 3 + 1 + 2 * REISERFS_QUOTA_TRANS_BLOCKS); // Wish I know if this number enough if (res) goto error_exit; reiserfs_update_inode_transaction(inode) ; @@ -576,7 +576,7 @@ error_exit: int err; // update any changes we made to blk count reiserfs_update_sd(th, inode); - err = journal_end(th, inode->i_sb, JOURNAL_PER_BALANCE_CNT * 3 + 1); + err = journal_end(th, inode->i_sb, JOURNAL_PER_BALANCE_CNT * 3 + 1 + 2 * REISERFS_QUOTA_TRANS_BLOCKS); if (err) res = err; } diff -puN fs/reiserfs/inode.c~fix-of-quota-deadlock-on-pagelock-reiserfs fs/reiserfs/inode.c --- 25/fs/reiserfs/inode.c~fix-of-quota-deadlock-on-pagelock-reiserfs 2004-11-30 01:23:17.235146968 -0800 +++ 25-akpm/fs/reiserfs/inode.c 2004-11-30 01:23:17.252144384 -0800 @@ -20,27 +20,17 @@ extern int reiserfs_default_io_size; /* default io size devuned in super.c */ -/* args for the create parameter of reiserfs_get_block */ -#define GET_BLOCK_NO_CREATE 0 /* don't create new blocks or convert tails */ -#define GET_BLOCK_CREATE 1 /* add anything you need to find block */ -#define GET_BLOCK_NO_HOLE 2 /* return -ENOENT for file holes */ -#define GET_BLOCK_READ_DIRECT 4 /* read the tail if indirect item not found */ -#define GET_BLOCK_NO_ISEM 8 /* i_sem is not held, don't preallocate */ -#define GET_BLOCK_NO_DANGLE 16 /* don't leave any transactions running */ - -static int reiserfs_get_block (struct inode * inode, sector_t block, - struct buffer_head * bh_result, int create); static int reiserfs_commit_write(struct file *f, struct page *page, unsigned from, unsigned to); void reiserfs_delete_inode (struct inode * inode) { - int jbegin_count = JOURNAL_PER_BALANCE_CNT * 2; + /* We need blocks for transaction + (user+group) quota update (possibly delete) */ + int jbegin_count = JOURNAL_PER_BALANCE_CNT * 2 + 2 * REISERFS_QUOTA_INIT_BLOCKS; struct reiserfs_transaction_handle th ; reiserfs_write_lock(inode->i_sb); - DQUOT_FREE_INODE(inode); /* The = 0 happens when we abort creating a new inode for some reason like lack of space.. */ if (!(inode->i_state & I_NEW) && INODE_PKEY(inode)->k_objectid != 0) { /* also handles bad_inode case */ down (&inode->i_sem); @@ -58,6 +48,11 @@ void reiserfs_delete_inode (struct inode goto out; } + /* Do quota update inside a transaction for journaled quotas. We must do that + * after delete_object so that quota updates go into the same transaction as + * stat data deletion */ + DQUOT_FREE_INODE(inode); + if (journal_end(&th, inode->i_sb, jbegin_count)) { up (&inode->i_sem); goto out; @@ -592,8 +587,9 @@ int reiserfs_get_block (struct inode * i . 3 balancings in direct->indirect conversion . 1 block involved into reiserfs_update_sd() XXX in practically impossible worst case direct2indirect() - can incur (much) more that 3 balancings. */ - int jbegin_count = JOURNAL_PER_BALANCE_CNT * 3 + 1; + can incur (much) more than 3 balancings. + quota update for user, group */ + int jbegin_count = JOURNAL_PER_BALANCE_CNT * 3 + 1 + 2 * REISERFS_QUOTA_TRANS_BLOCKS; int version; int dangle = 1; loff_t new_offset = (((loff_t)block) << inode->i_sb->s_blocksize_bits) + 1 ; @@ -1697,6 +1693,10 @@ int reiserfs_new_inode (struct reiserfs_ BUG_ON (!th->t_trans_id); + if (DQUOT_ALLOC_INODE(inode)) { + err = -EDQUOT; + goto out_end_trans; + } if (!dir || !dir->i_nlink) { err = -EPERM; goto out_bad_inode; @@ -1865,9 +1865,12 @@ out_bad_inode: /* Invalidate the object, nothing was inserted yet */ INODE_PKEY(inode)->k_objectid = 0; - /* dquot_drop must be done outside a transaction */ - journal_end(th, th->t_super, th->t_blocks_allocated) ; + /* Quota change must be inside a transaction for journaling */ DQUOT_FREE_INODE(inode); + +out_end_trans: + 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); inode->i_flags |= S_NOQUOTA; make_bad_inode(inode); @@ -2795,8 +2798,25 @@ int reiserfs_setattr(struct dentry *dent (ia_valid & ATTR_GID && attr->ia_gid != inode->i_gid)) { error = reiserfs_chown_xattrs (inode, attr); - if (!error) + if (!error) { + struct reiserfs_transaction_handle th; + + /* (user+group)*(old+new) structure - we count quota info and , inode write (sb, inode) */ + journal_begin(&th, inode->i_sb, 4*REISERFS_QUOTA_INIT_BLOCKS+2); error = DQUOT_TRANSFER(inode, attr) ? -EDQUOT : 0; + if (error) { + journal_end(&th, inode->i_sb, 4*REISERFS_QUOTA_INIT_BLOCKS+2); + goto out; + } + /* Update corresponding info in inode so that everything is in + * one transaction */ + if (attr->ia_valid & ATTR_UID) + inode->i_uid = attr->ia_uid; + if (attr->ia_valid & ATTR_GID) + inode->i_gid = attr->ia_gid; + mark_inode_dirty(inode); + journal_end(&th, inode->i_sb, 4*REISERFS_QUOTA_INIT_BLOCKS+2); + } } if (!error) error = inode_setattr(inode, attr) ; diff -puN fs/reiserfs/journal.c~fix-of-quota-deadlock-on-pagelock-reiserfs fs/reiserfs/journal.c --- 25/fs/reiserfs/journal.c~fix-of-quota-deadlock-on-pagelock-reiserfs 2004-11-30 01:23:17.237146664 -0800 +++ 25-akpm/fs/reiserfs/journal.c 2004-11-30 01:23:17.255143928 -0800 @@ -2647,6 +2647,7 @@ static int do_journal_begin_r(struct rei int retval; reiserfs_check_lock_depth(p_s_sb, "journal_begin") ; + RFALSE( SB_JOURNAL_MAX_BATCH(p_s_sb) < nblocks+2, "transaction too big (%u < %u)", SB_JOURNAL_MAX_BATCH(p_s_sb), nblocks+2); PROC_INFO_INC( p_s_sb, journal.journal_being ); /* set here for journal_join */ diff -puN fs/reiserfs/namei.c~fix-of-quota-deadlock-on-pagelock-reiserfs fs/reiserfs/namei.c --- 25/fs/reiserfs/namei.c~fix-of-quota-deadlock-on-pagelock-reiserfs 2004-11-30 01:23:17.239146360 -0800 +++ 25-akpm/fs/reiserfs/namei.c 2004-11-30 01:23:17.257143624 -0800 @@ -545,7 +545,7 @@ static int reiserfs_add_entry (struct re /* quota utility function, call if you've had to abort after calling ** new_inode_init, and have not called reiserfs_new_inode yet. -** This should only be called on inodes that do not hav stat data +** This should only be called on inodes that do not have stat data ** inserted into the tree yet. */ static int drop_new_inode(struct inode *inode) { @@ -557,10 +557,9 @@ static int drop_new_inode(struct inode * } /* utility function that does setup for reiserfs_new_inode. -** DQUOT_ALLOC_INODE cannot be called inside a transaction, so we had -** to pull some bits of reiserfs_new_inode out into this func. -** Yes, the actual quota calls are missing, they are part of the quota -** patch. +** DQUOT_INIT needs lots of credits so it's better to have it +** outside of a transaction, so we had to pull some bits of +** reiserfs_new_inode out into this func. */ static int new_inode_init(struct inode *inode, struct inode *dir, int mode) { @@ -578,10 +577,6 @@ static int new_inode_init(struct inode * inode->i_gid = current->fsgid; } DQUOT_INIT(inode); - if (DQUOT_ALLOC_INODE(inode)) { - drop_new_inode(inode); - return -EDQUOT; - } return 0 ; } @@ -590,16 +585,15 @@ static int reiserfs_create (struct inode { int retval; struct inode * inode; - int jbegin_count = JOURNAL_PER_BALANCE_CNT * 2 ; + /* We need blocks for transaction + (user+group)*(quotas for new inode + update of quota for directory owner) */ + int jbegin_count = JOURNAL_PER_BALANCE_CNT * 2 + 2 * (REISERFS_QUOTA_INIT_BLOCKS+REISERFS_QUOTA_TRANS_BLOCKS); struct reiserfs_transaction_handle th ; int locked; if (!(inode = new_inode(dir->i_sb))) { return -ENOMEM ; } - retval = new_inode_init(inode, dir, mode); - if (retval) - return retval; + new_inode_init(inode, dir, mode); locked = reiserfs_cache_default_acl (dir); @@ -658,7 +652,8 @@ static int reiserfs_mknod (struct inode int retval; struct inode * inode; struct reiserfs_transaction_handle th ; - int jbegin_count = JOURNAL_PER_BALANCE_CNT * 3; + /* We need blocks for transaction + (user+group)*(quotas for new inode + update of quota for directory owner) */ + int jbegin_count = JOURNAL_PER_BALANCE_CNT * 3 + 2 * (REISERFS_QUOTA_INIT_BLOCKS+REISERFS_QUOTA_TRANS_BLOCKS); int locked; if (!new_valid_dev(rdev)) @@ -667,9 +662,7 @@ static int reiserfs_mknod (struct inode if (!(inode = new_inode(dir->i_sb))) { return -ENOMEM ; } - retval = new_inode_init(inode, dir, mode); - if (retval) - return retval; + new_inode_init(inode, dir, mode); locked = reiserfs_cache_default_acl (dir); @@ -733,7 +726,8 @@ static int reiserfs_mkdir (struct inode int retval; struct inode * inode; struct reiserfs_transaction_handle th ; - int jbegin_count = JOURNAL_PER_BALANCE_CNT * 3; + /* We need blocks for transaction + (user+group)*(quotas for new inode + update of quota for directory owner) */ + int jbegin_count = JOURNAL_PER_BALANCE_CNT * 3 + 2 * (REISERFS_QUOTA_INIT_BLOCKS+REISERFS_QUOTA_TRANS_BLOCKS); int locked; #ifdef DISPLACE_NEW_PACKING_LOCALITIES @@ -744,9 +738,7 @@ static int reiserfs_mkdir (struct inode if (!(inode = new_inode(dir->i_sb))) { return -ENOMEM ; } - retval = new_inode_init(inode, dir, mode); - if (retval) - return retval; + new_inode_init(inode, dir, mode); locked = reiserfs_cache_default_acl (dir); @@ -836,8 +828,9 @@ static int reiserfs_rmdir (struct inode struct reiserfs_dir_entry de; - /* we will be doing 2 balancings and update 2 stat data */ - jbegin_count = JOURNAL_PER_BALANCE_CNT * 2 + 2; + /* we will be doing 2 balancings and update 2 stat data, we change quotas + * of the owner of the directory and of the owner of the parent directory */ + jbegin_count = JOURNAL_PER_BALANCE_CNT * 2 + 2 + 2 * (REISERFS_QUOTA_INIT_BLOCKS+REISERFS_QUOTA_TRANS_BLOCKS); reiserfs_write_lock(dir->i_sb); retval = journal_begin(&th, dir->i_sb, jbegin_count) ; @@ -920,8 +913,9 @@ static int reiserfs_unlink (struct inode inode = dentry->d_inode; /* in this transaction we can be doing at max two balancings and update - two stat datas */ - jbegin_count = JOURNAL_PER_BALANCE_CNT * 2 + 2; + two stat datas, we change quotas of the owner of the directory and of + the owner of the parent directory */ + jbegin_count = JOURNAL_PER_BALANCE_CNT * 2 + 2 + 2 * (REISERFS_QUOTA_INIT_BLOCKS+REISERFS_QUOTA_TRANS_BLOCKS); reiserfs_write_lock(dir->i_sb); retval = journal_begin(&th, dir->i_sb, jbegin_count) ; @@ -1005,15 +999,13 @@ static int reiserfs_symlink (struct inod int item_len; struct reiserfs_transaction_handle th ; int mode = S_IFLNK | S_IRWXUGO; - int jbegin_count = JOURNAL_PER_BALANCE_CNT * 3; + /* We need blocks for transaction + (user+group)*(quotas for new inode + update of quota for directory owner) */ + int jbegin_count = JOURNAL_PER_BALANCE_CNT * 3 + 2 * (REISERFS_QUOTA_INIT_BLOCKS+REISERFS_QUOTA_TRANS_BLOCKS); if (!(inode = new_inode(parent_dir->i_sb))) { return -ENOMEM ; } - retval = new_inode_init(inode, parent_dir, mode); - if (retval) { - return retval; - } + new_inode_init(inode, parent_dir, mode); reiserfs_write_lock(parent_dir->i_sb); item_len = ROUND_UP (strlen (symname)); @@ -1083,7 +1075,8 @@ static int reiserfs_link (struct dentry int retval; struct inode *inode = old_dentry->d_inode; struct reiserfs_transaction_handle th ; - int jbegin_count = JOURNAL_PER_BALANCE_CNT * 3; + /* We need blocks for transaction + update of quotas for the owners of the directory */ + int jbegin_count = JOURNAL_PER_BALANCE_CNT * 3 + 2 * REISERFS_QUOTA_TRANS_BLOCKS; reiserfs_write_lock(dir->i_sb); if (inode->i_nlink >= REISERFS_LINK_MAX) { @@ -1201,8 +1194,9 @@ static int reiserfs_rename (struct inode (2) new directory and (3) maybe old object stat data (when it is directory) and (4) maybe stat data of object to which new entry pointed initially and (5) maybe block containing ".." of - renamed directory */ - jbegin_count = JOURNAL_PER_BALANCE_CNT * 3 + 5; + renamed directory + quota updates: two parent directories */ + jbegin_count = JOURNAL_PER_BALANCE_CNT * 3 + 5 + 4 * REISERFS_QUOTA_TRANS_BLOCKS; old_inode = old_dentry->d_inode; new_dentry_inode = new_dentry->d_inode; diff -puN fs/reiserfs/super.c~fix-of-quota-deadlock-on-pagelock-reiserfs fs/reiserfs/super.c --- 25/fs/reiserfs/super.c~fix-of-quota-deadlock-on-pagelock-reiserfs 2004-11-30 01:23:17.241146056 -0800 +++ 25-akpm/fs/reiserfs/super.c 2004-11-30 01:23:17.262142864 -0800 @@ -25,6 +25,9 @@ #include #include #include +#include +#include +#include struct file_system_type reiserfs_fs_type; @@ -135,6 +138,9 @@ static int remove_save_link_only (struct return journal_end (&th, s, JOURNAL_PER_BALANCE_CNT); } +#ifdef CONFIG_QUOTA +static int reiserfs_quota_on_mount(struct super_block *, int); +#endif /* look for uncompleted unlinks and truncates and complete them */ static int finish_unfinished (struct super_block * s) @@ -150,12 +156,28 @@ static int finish_unfinished (struct sup int done; struct inode * inode; int truncate; +#ifdef CONFIG_QUOTA + int i; +#endif /* compose key to look for "save" links */ max_cpu_key.version = KEY_FORMAT_3_5; max_cpu_key.on_disk_key = MAX_KEY; max_cpu_key.key_length = 3; + +#ifdef CONFIG_QUOTA + /* Needed for iput() to work correctly and not trash data */ + s->s_flags |= MS_ACTIVE; + /* Turn on quotas so that they are updated correctly */ + for (i = 0; i < MAXQUOTAS; i++) { + if (REISERFS_SB(s)->s_qf_names[i]) { + int ret = reiserfs_quota_on_mount(s, i); + if (ret < 0) + reiserfs_warning(s, "reiserfs: cannot turn on journalled quota: error %d", ret); + } + } +#endif done = 0; REISERFS_SB(s)->s_is_unlinked_ok = 1; @@ -212,6 +234,7 @@ static int finish_unfinished (struct sup retval = remove_save_link_only (s, &save_link_key, 0); continue; } + DQUOT_INIT(inode); if (truncate && S_ISDIR (inode->i_mode) ) { /* We got a truncate request for a dir which is impossible. @@ -247,6 +270,15 @@ static int finish_unfinished (struct sup } REISERFS_SB(s)->s_is_unlinked_ok = 0; +#ifdef CONFIG_QUOTA + /* Turn quotas off */ + for (i = 0; i < MAXQUOTAS; i++) { + if (sb_dqopt(s)->files[i]) + vfs_quota_off_mount(s, i); + } + /* Restore the flag back */ + s->s_flags &= ~MS_ACTIVE; +#endif pathrelse (&path); if (done) reiserfs_info (s, "There were %d uncompleted unlinks/truncates. " @@ -517,6 +549,11 @@ static void reiserfs_clear_inode (struct REISERFS_I(inode)->i_acl_default = NULL; } +#ifdef CONFIG_QUOTA +static ssize_t reiserfs_quota_write(struct super_block *, int, const char *, size_t, loff_t); +static ssize_t reiserfs_quota_read(struct super_block *, int, char *, size_t, loff_t); +#endif + struct super_operations reiserfs_sops = { .alloc_inode = reiserfs_alloc_inode, @@ -532,9 +569,52 @@ struct super_operations reiserfs_sops = .unlockfs = reiserfs_unlockfs, .statfs = reiserfs_statfs, .remount_fs = reiserfs_remount, +#ifdef CONFIG_QUOTA + .quota_read = reiserfs_quota_read, + .quota_write = reiserfs_quota_write, +#endif +}; + +#ifdef CONFIG_QUOTA +#define QTYPE2NAME(t) ((t)==USRQUOTA?"user":"group") +static int reiserfs_dquot_initialize(struct inode *, int); +static int reiserfs_dquot_drop(struct inode *); +static int reiserfs_write_dquot(struct dquot *); +static int reiserfs_acquire_dquot(struct dquot *); +static int reiserfs_release_dquot(struct dquot *); +static int reiserfs_mark_dquot_dirty(struct dquot *); +static int reiserfs_write_info(struct super_block *, int); +static int reiserfs_quota_on(struct super_block *, int, int, char *); + +static struct dquot_operations reiserfs_quota_operations = +{ + .initialize = reiserfs_dquot_initialize, + .drop = reiserfs_dquot_drop, + .alloc_space = dquot_alloc_space, + .alloc_inode = dquot_alloc_inode, + .free_space = dquot_free_space, + .free_inode = dquot_free_inode, + .transfer = dquot_transfer, + .write_dquot = reiserfs_write_dquot, + .acquire_dquot = reiserfs_acquire_dquot, + .release_dquot = reiserfs_release_dquot, + .mark_dirty = reiserfs_mark_dquot_dirty, + .write_info = reiserfs_write_info, }; +static struct quotactl_ops reiserfs_qctl_operations = +{ + .quota_on = reiserfs_quota_on, + .quota_off = vfs_quota_off, + .quota_sync = vfs_quota_sync, + .get_info = vfs_get_dqinfo, + .set_info = vfs_set_dqinfo, + .get_dqblk = vfs_get_dqblk, + .set_dqblk = vfs_set_dqblk, +}; +#endif + static struct export_operations reiserfs_export_ops = { .encode_fh = reiserfs_encode_fh, .decode_fh = reiserfs_decode_fh, @@ -553,6 +633,8 @@ typedef struct { applied BEFORE setmask */ } arg_desc_t; +/* Set this bit in arg_required to allow empty arguments */ +#define REISERFS_OPT_ALLOWEMPTY 31 /* this struct is used in reiserfs_getopt() for describing the set of reiserfs mount options */ @@ -705,8 +787,8 @@ static int reiserfs_getopt ( struct supe /* move to the argument, or to next option if argument is not required */ p ++; - if ( opt->arg_required && !strlen (p) ) { - /* this catches "option=," */ + if ( opt->arg_required && !(opt->arg_required & (1<option_name); return -1; } @@ -714,7 +796,7 @@ static int reiserfs_getopt ( struct supe if (!opt->values) { /* *=NULLopt_arg contains pointer to argument */ *opt_arg = p; - return opt->arg_required; + return opt->arg_required & ~(1<values */ @@ -778,6 +860,9 @@ static int reiserfs_parse_options (struc {"usrquota",}, {"grpquota",}, {"errors", .arg_required = 'e', .values = error_actions}, + {"usrjquota", .arg_required = 'u'|(1<s_qf_names[qtype] && strcmp(REISERFS_SB(s)->s_qf_names[qtype], arg)) { + reiserfs_warning(s, "reiserfs_parse_options: %s quota file already specified.", QTYPE2NAME(qtype)); + return 0; + } + if (strchr(arg, '/')) { + reiserfs_warning(s, "reiserfs_parse_options: quotafile must be on filesystem root."); + return 0; + } + REISERFS_SB(s)->s_qf_names[qtype] = kmalloc(strlen(arg)+1, GFP_KERNEL); + if (!REISERFS_SB(s)->s_qf_names[qtype]) { + reiserfs_warning(s, "reiserfs_parse_options: not enough memory for storing quotafile name."); + return 0; + } + strcpy(REISERFS_SB(s)->s_qf_names[qtype], arg); + } + else { + if (REISERFS_SB(s)->s_qf_names[qtype]) { + kfree(REISERFS_SB(s)->s_qf_names[qtype]); + REISERFS_SB(s)->s_qf_names[qtype] = NULL; + } + } + } + if (c == 'f') { + if (!strcmp(arg, "vfsold")) + REISERFS_SB(s)->s_jquota_fmt = QFMT_VFS_OLD; + else if (!strcmp(arg, "vfsv0")) + REISERFS_SB(s)->s_jquota_fmt = QFMT_VFS_V0; + else { + reiserfs_warning(s, "reiserfs_parse_options: unknown quota format specified."); + return 0; + } + } +#else + if (c == 'u' || c == 'g' || c == 'f') { + reiserfs_warning(s, "reiserfs_parse_options: journalled quota options not supported."); + return 0; + } +#endif } +#ifdef CONFIG_QUOTA + if (!REISERFS_SB(s)->s_jquota_fmt && (REISERFS_SB(s)->s_qf_names[USRQUOTA] || REISERFS_SB(s)->s_qf_names[GRPQUOTA])) { + reiserfs_warning(s, "reiserfs_parse_options: journalled quota format not specified."); + return 0; + } +#endif return 1; } @@ -916,11 +1055,22 @@ static int reiserfs_remount (struct supe unsigned int commit_max_age = (unsigned int)-1; struct reiserfs_journal *journal = SB_JOURNAL(s); int err; +#ifdef CONFIG_QUOTA + int i; +#endif rs = SB_DISK_SUPER_BLOCK (s); - if (!reiserfs_parse_options(s, arg, &mount_options, &blocks, NULL, &commit_max_age)) + if (!reiserfs_parse_options(s, arg, &mount_options, &blocks, NULL, &commit_max_age)) { +#ifdef CONFIG_QUOTA + for (i = 0; i < MAXQUOTAS; i++) + if (REISERFS_SB(s)->s_qf_names[i]) { + kfree(REISERFS_SB(s)->s_qf_names[i]); + REISERFS_SB(s)->s_qf_names[i] = NULL; + } +#endif return -EINVAL; + } handle_attrs(s); @@ -1225,6 +1375,10 @@ static int read_super_block (struct supe s->s_op = &reiserfs_sops; s->s_export_op = &reiserfs_export_ops; +#ifdef CONFIG_QUOTA + s->s_qcop = &reiserfs_qctl_operations; + s->dq_op = &reiserfs_quota_operations; +#endif /* new format is limited by the 32 bit wide i_blocks field, want to ** be one full block below that. @@ -1656,7 +1810,12 @@ static int reiserfs_fill_super (struct s } if (SB_BUFFER_WITH_SB (s)) brelse(SB_BUFFER_WITH_SB (s)); - +#ifdef CONFIG_QUOTA + for (j = 0; j < MAXQUOTAS; j++) { + if (sbi->s_qf_names[j]) + kfree(sbi->s_qf_names[j]); + } +#endif if (sbi != NULL) { kfree(sbi); } @@ -1680,6 +1839,245 @@ static int reiserfs_statfs (struct super return 0; } +#ifdef CONFIG_QUOTA +static int reiserfs_dquot_initialize(struct inode *inode, int type) +{ + struct reiserfs_transaction_handle th; + int ret; + + /* We may create quota structure so we need to reserve enough blocks */ + journal_begin(&th, inode->i_sb, 2*REISERFS_QUOTA_INIT_BLOCKS); + ret = dquot_initialize(inode, type); + journal_end(&th, inode->i_sb, 2*REISERFS_QUOTA_INIT_BLOCKS); + return ret; +} + +static int reiserfs_dquot_drop(struct inode *inode) +{ + struct reiserfs_transaction_handle th; + int ret; + + /* We may delete quota structure so we need to reserve enough blocks */ + journal_begin(&th, inode->i_sb, 2*REISERFS_QUOTA_INIT_BLOCKS); + ret = dquot_drop(inode); + journal_end(&th, inode->i_sb, 2*REISERFS_QUOTA_INIT_BLOCKS); + return ret; +} + +static int reiserfs_write_dquot(struct dquot *dquot) +{ + struct reiserfs_transaction_handle th; + int ret; + + journal_begin(&th, dquot->dq_sb, REISERFS_QUOTA_TRANS_BLOCKS); + ret = dquot_commit(dquot); + journal_end(&th, dquot->dq_sb, REISERFS_QUOTA_TRANS_BLOCKS); + return ret; +} + +static int reiserfs_acquire_dquot(struct dquot *dquot) +{ + struct reiserfs_transaction_handle th; + int ret; + + journal_begin(&th, dquot->dq_sb, REISERFS_QUOTA_INIT_BLOCKS); + ret = dquot_acquire(dquot); + journal_end(&th, dquot->dq_sb, REISERFS_QUOTA_INIT_BLOCKS); + return ret; +} + +static int reiserfs_release_dquot(struct dquot *dquot) +{ + struct reiserfs_transaction_handle th; + int ret; + + journal_begin(&th, dquot->dq_sb, REISERFS_QUOTA_INIT_BLOCKS); + ret = dquot_release(dquot); + journal_end(&th, dquot->dq_sb, REISERFS_QUOTA_INIT_BLOCKS); + return ret; +} + +static int reiserfs_mark_dquot_dirty(struct dquot *dquot) +{ + /* Are we journalling quotas? */ + if (REISERFS_SB(dquot->dq_sb)->s_qf_names[USRQUOTA] || + REISERFS_SB(dquot->dq_sb)->s_qf_names[GRPQUOTA]) { + dquot_mark_dquot_dirty(dquot); + return reiserfs_write_dquot(dquot); + } + else + return dquot_mark_dquot_dirty(dquot); +} + +static int reiserfs_write_info(struct super_block *sb, int type) +{ + struct reiserfs_transaction_handle th; + int ret; + + /* Data block + inode block */ + journal_begin(&th, sb, 2); + ret = dquot_commit_info(sb, type); + journal_end(&th, sb, 2); + return ret; +} + +/* + * Turn on quotas during mount time - we need to find + * the quota file and such... + */ +static int reiserfs_quota_on_mount(struct super_block *sb, int type) +{ + int err; + struct dentry *dentry; + struct qstr name = { .name = REISERFS_SB(sb)->s_qf_names[type], + .hash = 0, + .len = strlen(REISERFS_SB(sb)->s_qf_names[type])}; + + dentry = lookup_hash(&name, sb->s_root); + if (IS_ERR(dentry)) + return PTR_ERR(dentry); + err = vfs_quota_on_mount(type, REISERFS_SB(sb)->s_jquota_fmt, dentry); + /* Now invalidate and put the dentry - quota got its own reference + * to inode and dentry has at least wrong hash so we had better + * throw it away */ + d_invalidate(dentry); + dput(dentry); + return err; +} + +/* + * Standard function to be called on quota_on + */ +static int reiserfs_quota_on(struct super_block *sb, int type, int format_id, char *path) +{ + int err; + struct nameidata nd; + + err = path_lookup(path, LOOKUP_FOLLOW, &nd); + if (err) + return err; + /* Quotafile not on the same filesystem? */ + if (nd.mnt->mnt_sb != sb) { + path_release(&nd); + return -EXDEV; + } + /* We must not pack tails for quota files on reiserfs for quota IO to work */ + if (!REISERFS_I(nd.dentry->d_inode)->i_flags & i_nopack_mask) { + reiserfs_warning(sb, "reiserfs: Quota file must have tail packing disabled."); + path_release(&nd); + return -EINVAL; + } + /* Not journalling quota? No more tests needed... */ + if (!REISERFS_SB(sb)->s_qf_names[USRQUOTA] && + !REISERFS_SB(sb)->s_qf_names[GRPQUOTA]) { + path_release(&nd); + return vfs_quota_on(sb, type, format_id, path); + } + /* Quotafile not of fs root? */ + if (nd.dentry->d_parent->d_inode != sb->s_root->d_inode) + reiserfs_warning(sb, "reiserfs: Quota file not on filesystem root. " + "Journalled quota will not work."); + path_release(&nd); + return vfs_quota_on(sb, type, format_id, path); +} + +/* Read data from quotafile - avoid pagecache and such because we cannot afford + * acquiring the locks... As quota files are never truncated and quota code + * itself serializes the operations (and noone else should touch the files) + * we don't have to be afraid of races */ +static ssize_t reiserfs_quota_read(struct super_block *sb, int type, char *data, + size_t len, loff_t off) +{ + struct inode *inode = sb_dqopt(sb)->files[type]; + unsigned long blk = off >> sb->s_blocksize_bits; + int err = 0, offset = off & (sb->s_blocksize - 1), tocopy; + size_t toread; + struct buffer_head tmp_bh, *bh; + loff_t i_size = i_size_read(inode); + + if (off > i_size) + return 0; + if (off+len > i_size) + len = i_size-off; + toread = len; + while (toread > 0) { + tocopy = sb->s_blocksize - offset < toread ? sb->s_blocksize - offset : toread; + tmp_bh.b_state = 0; + /* Quota files are without tails so we can safely use this function */ + err = reiserfs_get_block(inode, blk, &tmp_bh, 0); + if (err) + return err; + if (!buffer_mapped(&tmp_bh)) /* A hole? */ + memset(data, 0, tocopy); + else { + bh = sb_bread(sb, tmp_bh.b_blocknr); + if (!bh) + return -EIO; + memcpy(data, bh->b_data+offset, tocopy); + brelse(bh); + } + offset = 0; + toread -= tocopy; + data += tocopy; + blk++; + } + return len; +} + +/* Write to quotafile (we know the transaction is already started and has + * enough credits) */ +static ssize_t reiserfs_quota_write(struct super_block *sb, int type, + const char *data, size_t len, loff_t off) +{ + struct inode *inode = sb_dqopt(sb)->files[type]; + unsigned long blk = off >> sb->s_blocksize_bits; + int err = 0, offset = off & (sb->s_blocksize - 1), tocopy; + int journal_quota = REISERFS_SB(sb)->s_qf_names[type] != NULL; + size_t towrite = len; + struct buffer_head tmp_bh, *bh; + + down(&inode->i_sem); + while (towrite > 0) { + tocopy = sb->s_blocksize - offset < towrite ? + sb->s_blocksize - offset : towrite; + tmp_bh.b_state = 0; + err = reiserfs_get_block(inode, blk, &tmp_bh, GET_BLOCK_CREATE); + if (err) + goto out; + if (offset || tocopy != sb->s_blocksize) + bh = sb_bread(sb, tmp_bh.b_blocknr); + else + bh = sb_getblk(sb, tmp_bh.b_blocknr); + if (!bh) { + err = -EIO; + goto out; + } + memcpy(bh->b_data+offset, data, tocopy); + set_buffer_uptodate(bh); + reiserfs_prepare_for_journal(sb, bh, 1); + journal_mark_dirty(current->journal_info, sb, bh); + if (!journal_quota) + reiserfs_add_ordered_list(inode, bh); + brelse(bh); + offset = 0; + towrite -= tocopy; + data += tocopy; + blk++; + } +out: + if (len == towrite) + return err; + if (inode->i_size < off+len-towrite) + i_size_write(inode, off+len-towrite); + inode->i_version++; + inode->i_mtime = inode->i_ctime = CURRENT_TIME; + mark_inode_dirty(inode); + up(&inode->i_sem); + return len - towrite; +} + +#endif + static struct super_block* get_super_block (struct file_system_type *fs_type, int flags, const char *dev_name, void *data) diff -puN include/linux/reiserfs_fs.h~fix-of-quota-deadlock-on-pagelock-reiserfs include/linux/reiserfs_fs.h --- 25/include/linux/reiserfs_fs.h~fix-of-quota-deadlock-on-pagelock-reiserfs 2004-11-30 01:23:17.243145752 -0800 +++ 25-akpm/include/linux/reiserfs_fs.h 2004-11-30 01:23:17.264142560 -0800 @@ -1688,6 +1688,13 @@ struct reiserfs_journal_header { #define JOURNAL_MAX_COMMIT_AGE 30 #define JOURNAL_MAX_TRANS_AGE 30 #define JOURNAL_PER_BALANCE_CNT (3 * (MAX_HEIGHT-2) + 9) +#ifdef CONFIG_QUOTA +#define REISERFS_QUOTA_TRANS_BLOCKS 2 /* We need to update data and inode (atime) */ +#define REISERFS_QUOTA_INIT_BLOCKS (DQUOT_MAX_WRITES*(JOURNAL_PER_BALANCE_CNT+2)+1) /* 1 balancing, 1 bitmap, 1 data per write + stat data update */ +#else +#define REISERFS_QUOTA_TRANS_BLOCKS 0 +#define REISERFS_QUOTA_INIT_BLOCKS 0 +#endif /* both of these can be as low as 1, or as high as you want. The min is the ** number of 4k bitmap nodes preallocated on mount. New nodes are allocated @@ -1930,12 +1937,21 @@ int reiserfs_do_truncate (struct reiserf void padd_item (char * item, int total_length, int length); /* inode.c */ +/* args for the create parameter of reiserfs_get_block */ +#define GET_BLOCK_NO_CREATE 0 /* don't create new blocks or convert tails */ +#define GET_BLOCK_CREATE 1 /* add anything you need to find block */ +#define GET_BLOCK_NO_HOLE 2 /* return -ENOENT for file holes */ +#define GET_BLOCK_READ_DIRECT 4 /* read the tail if indirect item not found */ +#define GET_BLOCK_NO_ISEM 8 /* i_sem is not held, don't preallocate */ +#define GET_BLOCK_NO_DANGLE 16 /* don't leave any transactions running */ + int restart_transaction(struct reiserfs_transaction_handle *th, struct inode *inode, struct path *path); void reiserfs_read_locked_inode(struct inode * inode, struct reiserfs_iget_args *args) ; int reiserfs_find_actor(struct inode * inode, void *p) ; int reiserfs_init_locked_inode(struct inode * inode, void *p) ; void reiserfs_delete_inode (struct inode * inode); int reiserfs_write_inode (struct inode * inode, int) ; +int reiserfs_get_block (struct inode * inode, sector_t block, struct buffer_head * bh_result, int create); struct dentry *reiserfs_get_dentry(struct super_block *, void *) ; struct dentry *reiserfs_decode_fh(struct super_block *sb, __u32 *data, int len, int fhtype, diff -puN include/linux/reiserfs_fs_sb.h~fix-of-quota-deadlock-on-pagelock-reiserfs include/linux/reiserfs_fs_sb.h --- 25/include/linux/reiserfs_fs_sb.h~fix-of-quota-deadlock-on-pagelock-reiserfs 2004-11-30 01:23:17.244145600 -0800 +++ 25-akpm/include/linux/reiserfs_fs_sb.h 2004-11-30 01:23:17.265142408 -0800 @@ -410,6 +410,10 @@ struct reiserfs_sb_info struct rw_semaphore xattr_dir_sem; int j_errno; +#ifdef CONFIG_QUOTA + char *s_qf_names[MAXQUOTAS]; + int s_jquota_fmt; +#endif }; /* Definitions of reiserfs on-disk properties: */ _