[Ocfs2-devel] [PATCH 25/29] ocfs2: Implementation of local and global quota file handling
Joel Becker
Joel.Becker at oracle.com
Tue Oct 28 12:07:08 PDT 2008
On Sat, Oct 25, 2008 at 12:08:18AM +0200, Jan Kara wrote:
> For each quota type each node has local quota file. In this file it stores
> changes users have made to disk usage via this node. Once in a while this
> information is synced to global file (and thus with other nodes) so that
> limits enforcement at least aproximately works.
>
> Global quota files contain all the information about usage and limits. It's
> mostly handled by the generic VFS code (which implements a trie of structures
> inside a quota file). We only have to provide functions to convert structures
> from on-disk format to in-memory one. We also have to provide wrappers for
> various quota functions starting transactions and acquiring necessary cluster
> locks before the actual IO is really started.
Thanks for the description, it makes it a lot more
understandable.
Joel
> Signed-off-by: Jan Kara <jack at suse.cz>
> ---
> fs/ocfs2/Makefile | 2 +
> fs/ocfs2/cluster/masklog.h | 1 +
> fs/ocfs2/dir.c | 4 +-
> fs/ocfs2/dlmglue.c | 137 +++++++
> fs/ocfs2/dlmglue.h | 17 +
> fs/ocfs2/file.c | 6 +-
> fs/ocfs2/file.h | 3 +
> fs/ocfs2/inode.h | 2 +
> fs/ocfs2/ocfs2_fs.h | 95 +++++
> fs/ocfs2/ocfs2_lockid.h | 5 +
> fs/ocfs2/quota.h | 97 +++++
> fs/ocfs2/quota_global.c | 863 ++++++++++++++++++++++++++++++++++++++++++++
> fs/ocfs2/quota_local.c | 833 ++++++++++++++++++++++++++++++++++++++++++
> fs/ocfs2/super.c | 38 ++-
> 14 files changed, 2096 insertions(+), 7 deletions(-)
> create mode 100644 fs/ocfs2/quota.h
> create mode 100644 fs/ocfs2/quota_global.c
> create mode 100644 fs/ocfs2/quota_local.c
>
> diff --git a/fs/ocfs2/Makefile b/fs/ocfs2/Makefile
> index 589dcdf..11f14e1 100644
> --- a/fs/ocfs2/Makefile
> +++ b/fs/ocfs2/Makefile
> @@ -35,6 +35,8 @@ ocfs2-objs := \
> sysfile.o \
> uptodate.o \
> ver.o \
> + quota_local.o \
> + quota_global.o \
> xattr.o
>
> ocfs2_stackglue-objs := stackglue.o
> diff --git a/fs/ocfs2/cluster/masklog.h b/fs/ocfs2/cluster/masklog.h
> index 57670c6..7e72a81 100644
> --- a/fs/ocfs2/cluster/masklog.h
> +++ b/fs/ocfs2/cluster/masklog.h
> @@ -113,6 +113,7 @@
> #define ML_QUORUM 0x0000000008000000ULL /* net connection quorum */
> #define ML_EXPORT 0x0000000010000000ULL /* ocfs2 export operations */
> #define ML_XATTR 0x0000000020000000ULL /* ocfs2 extended attributes */
> +#define ML_QUOTA 0x0000000040000000ULL /* ocfs2 quota operations */
> /* bits that are infrequently given and frequently matched in the high word */
> #define ML_ERROR 0x0000000100000000ULL /* sent to KERN_ERR */
> #define ML_NOTICE 0x0000000200000000ULL /* setn to KERN_NOTICE */
> diff --git a/fs/ocfs2/dir.c b/fs/ocfs2/dir.c
> index 026e6eb..084ba9c 100644
> --- a/fs/ocfs2/dir.c
> +++ b/fs/ocfs2/dir.c
> @@ -82,8 +82,8 @@ static int ocfs2_do_extend_dir(struct super_block *sb,
> struct ocfs2_alloc_context *meta_ac,
> struct buffer_head **new_bh);
>
> -static struct buffer_head *ocfs2_bread(struct inode *inode,
> - int block, int *err, int reada)
> +struct buffer_head *ocfs2_bread(struct inode *inode,
> + int block, int *err, int reada)
> {
> struct buffer_head *bh = NULL;
> int tmperr;
> diff --git a/fs/ocfs2/dlmglue.c b/fs/ocfs2/dlmglue.c
> index ec68442..eab90e8 100644
> --- a/fs/ocfs2/dlmglue.c
> +++ b/fs/ocfs2/dlmglue.c
> @@ -32,6 +32,7 @@
> #include <linux/debugfs.h>
> #include <linux/seq_file.h>
> #include <linux/time.h>
> +#include <linux/quotaops.h>
>
> #define MLOG_MASK_PREFIX ML_DLM_GLUE
> #include <cluster/masklog.h>
> @@ -51,6 +52,7 @@
> #include "slot_map.h"
> #include "super.h"
> #include "uptodate.h"
> +#include "quota.h"
>
> #include "buffer_head_io.h"
>
> @@ -68,6 +70,7 @@ struct ocfs2_mask_waiter {
> static struct ocfs2_super *ocfs2_get_dentry_osb(struct ocfs2_lock_res *lockres);
> static struct ocfs2_super *ocfs2_get_inode_osb(struct ocfs2_lock_res *lockres);
> static struct ocfs2_super *ocfs2_get_file_osb(struct ocfs2_lock_res *lockres);
> +static struct ocfs2_super *ocfs2_get_qinfo_osb(struct ocfs2_lock_res *lockres);
>
> /*
> * Return value from ->downconvert_worker functions.
> @@ -102,6 +105,7 @@ static int ocfs2_dentry_convert_worker(struct ocfs2_lock_res *lockres,
> static void ocfs2_dentry_post_unlock(struct ocfs2_super *osb,
> struct ocfs2_lock_res *lockres);
>
> +static void ocfs2_set_qinfo_lvb(struct ocfs2_lock_res *lockres);
>
> #define mlog_meta_lvb(__level, __lockres) ocfs2_dump_meta_lvb_info(__level, __PRETTY_FUNCTION__, __LINE__, __lockres)
>
> @@ -258,6 +262,12 @@ static struct ocfs2_lock_res_ops ocfs2_flock_lops = {
> .flags = 0,
> };
>
> +static struct ocfs2_lock_res_ops ocfs2_qinfo_lops = {
> + .set_lvb = ocfs2_set_qinfo_lvb,
> + .get_osb = ocfs2_get_qinfo_osb,
> + .flags = LOCK_TYPE_REQUIRES_REFRESH | LOCK_TYPE_USES_LVB,
> +};
> +
> static inline int ocfs2_is_inode_lock(struct ocfs2_lock_res *lockres)
> {
> return lockres->l_type == OCFS2_LOCK_TYPE_META ||
> @@ -279,6 +289,13 @@ static inline struct ocfs2_dentry_lock *ocfs2_lock_res_dl(struct ocfs2_lock_res
> return (struct ocfs2_dentry_lock *)lockres->l_priv;
> }
>
> +static inline struct ocfs2_mem_dqinfo *ocfs2_lock_res_qinfo(struct ocfs2_lock_res *lockres)
> +{
> + BUG_ON(lockres->l_type != OCFS2_LOCK_TYPE_QINFO);
> +
> + return (struct ocfs2_mem_dqinfo *)lockres->l_priv;
> +}
> +
> static inline struct ocfs2_super *ocfs2_get_lockres_osb(struct ocfs2_lock_res *lockres)
> {
> if (lockres->l_ops->get_osb)
> @@ -507,6 +524,13 @@ static struct ocfs2_super *ocfs2_get_inode_osb(struct ocfs2_lock_res *lockres)
> return OCFS2_SB(inode->i_sb);
> }
>
> +static struct ocfs2_super *ocfs2_get_qinfo_osb(struct ocfs2_lock_res *lockres)
> +{
> + struct ocfs2_mem_dqinfo *info = lockres->l_priv;
> +
> + return OCFS2_SB(info->dqi_gi.dqi_sb);
> +}
> +
> static struct ocfs2_super *ocfs2_get_file_osb(struct ocfs2_lock_res *lockres)
> {
> struct ocfs2_file_private *fp = lockres->l_priv;
> @@ -609,6 +633,17 @@ void ocfs2_file_lock_res_init(struct ocfs2_lock_res *lockres,
> lockres->l_flags |= OCFS2_LOCK_NOCACHE;
> }
>
> +void ocfs2_qinfo_lock_res_init(struct ocfs2_lock_res *lockres,
> + struct ocfs2_mem_dqinfo *info)
> +{
> + ocfs2_lock_res_init_once(lockres);
> + ocfs2_build_lock_name(OCFS2_LOCK_TYPE_QINFO, info->dqi_gi.dqi_type,
> + 0, lockres->l_name);
> + ocfs2_lock_res_init_common(OCFS2_SB(info->dqi_gi.dqi_sb), lockres,
> + OCFS2_LOCK_TYPE_QINFO, &ocfs2_qinfo_lops,
> + info);
> +}
> +
> void ocfs2_lock_res_free(struct ocfs2_lock_res *res)
> {
> mlog_entry_void();
> @@ -3450,6 +3485,108 @@ static int ocfs2_dentry_convert_worker(struct ocfs2_lock_res *lockres,
> return UNBLOCK_CONTINUE_POST;
> }
>
> +static void ocfs2_set_qinfo_lvb(struct ocfs2_lock_res *lockres)
> +{
> + struct ocfs2_qinfo_lvb *lvb;
> + struct ocfs2_mem_dqinfo *oinfo = ocfs2_lock_res_qinfo(lockres);
> + struct mem_dqinfo *info = sb_dqinfo(oinfo->dqi_gi.dqi_sb,
> + oinfo->dqi_gi.dqi_type);
> +
> + mlog_entry_void();
> +
> + lvb = (struct ocfs2_qinfo_lvb *)ocfs2_dlm_lvb(&lockres->l_lksb);
> + lvb->lvb_version = OCFS2_LVB_VERSION;
> + lvb->lvb_bgrace = cpu_to_be32(info->dqi_bgrace);
> + lvb->lvb_igrace = cpu_to_be32(info->dqi_igrace);
> + lvb->lvb_syncms = cpu_to_be32(oinfo->dqi_syncms);
> + lvb->lvb_blocks = cpu_to_be32(oinfo->dqi_gi.dqi_blocks);
> + lvb->lvb_free_blk = cpu_to_be32(oinfo->dqi_gi.dqi_free_blk);
> + lvb->lvb_free_entry = cpu_to_be32(oinfo->dqi_gi.dqi_free_entry);
> +
> + mlog_exit_void();
> +}
> +
> +void ocfs2_qinfo_unlock(struct ocfs2_mem_dqinfo *oinfo, int ex)
> +{
> + struct ocfs2_lock_res *lockres = &oinfo->dqi_gqlock;
> + struct ocfs2_super *osb = OCFS2_SB(oinfo->dqi_gi.dqi_sb);
> + int level = ex ? DLM_LOCK_EX : DLM_LOCK_PR;
> +
> + mlog_entry_void();
> + if (!ocfs2_is_hard_readonly(osb) && !ocfs2_mount_local(osb))
> + ocfs2_cluster_unlock(osb, lockres, level);
> + mlog_exit_void();
> +}
> +
> +/* Lock quota info, this function expects at least shared lock on the quota file
> + * so that we can safely refresh quota info from disk. */
> +int ocfs2_qinfo_lock(struct ocfs2_mem_dqinfo *oinfo, int ex)
> +{
> + struct mem_dqinfo *info = sb_dqinfo(oinfo->dqi_gi.dqi_sb,
> + oinfo->dqi_gi.dqi_type);
> + struct ocfs2_lock_res *lockres = &oinfo->dqi_gqlock;
> + struct ocfs2_super *osb = OCFS2_SB(oinfo->dqi_gi.dqi_sb);
> + struct ocfs2_qinfo_lvb *lvb;
> + int level = ex ? DLM_LOCK_EX : DLM_LOCK_PR;
> + int status = 0;
> + struct buffer_head *bh;
> + struct ocfs2_global_disk_dqinfo *gdinfo;
> +
> + mlog_entry_void();
> +
> + /* We'll allow faking a readonly metadata lock for
> + * rodevices. */
> + if (ocfs2_is_hard_readonly(osb)) {
> + if (ex)
> + status = -EROFS;
> + goto bail;
> + }
> + if (ocfs2_mount_local(osb))
> + goto bail;
> +
> + status = ocfs2_cluster_lock(osb, lockres, level, 0, 0);
> + if (status < 0) {
> + mlog_errno(status);
> + goto bail;
> + }
> + if (!ocfs2_should_refresh_lock_res(lockres))
> + goto bail;
> + /* OK, we have the lock but we need to refresh the quota info */
> + lvb = ocfs2_dlm_lvb(&lockres->l_lksb);
> + if (lvb->lvb_version == OCFS2_LVB_VERSION) {
> + info->dqi_bgrace = be32_to_cpu(lvb->lvb_bgrace);
> + info->dqi_igrace = be32_to_cpu(lvb->lvb_igrace);
> + oinfo->dqi_syncms = be32_to_cpu(lvb->lvb_syncms);
> + oinfo->dqi_gi.dqi_blocks = be32_to_cpu(lvb->lvb_blocks);
> + oinfo->dqi_gi.dqi_free_blk = be32_to_cpu(lvb->lvb_free_blk);
> + oinfo->dqi_gi.dqi_free_entry =
> + be32_to_cpu(lvb->lvb_free_entry);
> + } else {
> + bh = ocfs2_read_quota_block(oinfo->dqi_gqinode, 0, &status);
> + if (!bh) {
> + ocfs2_qinfo_unlock(oinfo, ex);
> + mlog_errno(status);
> + goto bail_refresh;
> + }
> + gdinfo = (struct ocfs2_global_disk_dqinfo *)
> + (bh->b_data + OCFS2_GLOBAL_INFO_OFF);
> + info->dqi_bgrace = le32_to_cpu(gdinfo->dqi_bgrace);
> + info->dqi_igrace = le32_to_cpu(gdinfo->dqi_igrace);
> + oinfo->dqi_syncms = le32_to_cpu(gdinfo->dqi_syncms);
> + oinfo->dqi_gi.dqi_blocks = le32_to_cpu(gdinfo->dqi_blocks);
> + oinfo->dqi_gi.dqi_free_blk = le32_to_cpu(gdinfo->dqi_free_blk);
> + oinfo->dqi_gi.dqi_free_entry =
> + le32_to_cpu(gdinfo->dqi_free_entry);
> + brelse(bh);
> + ocfs2_track_lock_refresh(lockres);
> + }
> +bail_refresh:
> + ocfs2_complete_lock_res_refresh(lockres, status);
> +bail:
> + mlog_exit(status);
> + return status;
> +}
> +
> /*
> * This is the filesystem locking protocol. It provides the lock handling
> * hooks for the underlying DLM. It has a maximum version number.
> diff --git a/fs/ocfs2/dlmglue.h b/fs/ocfs2/dlmglue.h
> index 2bb01f0..6a34048 100644
> --- a/fs/ocfs2/dlmglue.h
> +++ b/fs/ocfs2/dlmglue.h
> @@ -49,6 +49,17 @@ struct ocfs2_meta_lvb {
> __be32 lvb_reserved2;
> };
>
> +struct ocfs2_qinfo_lvb {
> + __u8 lvb_version;
> + __u8 lvb_reserved[3];
> + __be32 lvb_bgrace;
> + __be32 lvb_igrace;
> + __be32 lvb_syncms;
> + __be32 lvb_blocks;
> + __be32 lvb_free_blk;
> + __be32 lvb_free_entry;
> +};
> +
> /* ocfs2_inode_lock_full() 'arg_flags' flags */
> /* don't wait on recovery. */
> #define OCFS2_META_LOCK_RECOVERY (0x01)
> @@ -69,6 +80,9 @@ void ocfs2_dentry_lock_res_init(struct ocfs2_dentry_lock *dl,
> struct ocfs2_file_private;
> void ocfs2_file_lock_res_init(struct ocfs2_lock_res *lockres,
> struct ocfs2_file_private *fp);
> +struct ocfs2_mem_dqinfo;
> +void ocfs2_qinfo_lock_res_init(struct ocfs2_lock_res *lockres,
> + struct ocfs2_mem_dqinfo *info);
> void ocfs2_lock_res_free(struct ocfs2_lock_res *res);
> int ocfs2_create_new_inode_locks(struct inode *inode);
> int ocfs2_drop_inode_locks(struct inode *inode);
> @@ -103,6 +117,9 @@ int ocfs2_dentry_lock(struct dentry *dentry, int ex);
> void ocfs2_dentry_unlock(struct dentry *dentry, int ex);
> int ocfs2_file_lock(struct file *file, int ex, int trylock);
> void ocfs2_file_unlock(struct file *file);
> +int ocfs2_qinfo_lock(struct ocfs2_mem_dqinfo *oinfo, int ex);
> +void ocfs2_qinfo_unlock(struct ocfs2_mem_dqinfo *oinfo, int ex);
> +
>
> void ocfs2_mark_lockres_freeing(struct ocfs2_lock_res *lockres);
> void ocfs2_simple_drop_lockres(struct ocfs2_super *osb,
> diff --git a/fs/ocfs2/file.c b/fs/ocfs2/file.c
> index e135da1..9af16e0 100644
> --- a/fs/ocfs2/file.c
> +++ b/fs/ocfs2/file.c
> @@ -303,9 +303,9 @@ bail:
> return status;
> }
>
> -static int ocfs2_simple_size_update(struct inode *inode,
> - struct buffer_head *di_bh,
> - u64 new_i_size)
> +int ocfs2_simple_size_update(struct inode *inode,
> + struct buffer_head *di_bh,
> + u64 new_i_size)
> {
> int ret;
> struct ocfs2_super *osb = OCFS2_SB(inode->i_sb);
> diff --git a/fs/ocfs2/file.h b/fs/ocfs2/file.h
> index e92382c..172f9fb 100644
> --- a/fs/ocfs2/file.h
> +++ b/fs/ocfs2/file.h
> @@ -51,6 +51,9 @@ int ocfs2_add_inode_data(struct ocfs2_super *osb,
> struct ocfs2_alloc_context *data_ac,
> struct ocfs2_alloc_context *meta_ac,
> enum ocfs2_alloc_restarted *reason_ret);
> +int ocfs2_simple_size_update(struct inode *inode,
> + struct buffer_head *di_bh,
> + u64 new_i_size);
> int ocfs2_extend_no_holes(struct inode *inode, u64 new_i_size,
> u64 zero_to);
> int ocfs2_setattr(struct dentry *dentry, struct iattr *attr);
> diff --git a/fs/ocfs2/inode.h b/fs/ocfs2/inode.h
> index 2f37af9..e90a159 100644
> --- a/fs/ocfs2/inode.h
> +++ b/fs/ocfs2/inode.h
> @@ -142,6 +142,8 @@ int ocfs2_mark_inode_dirty(handle_t *handle,
> struct buffer_head *bh);
> int ocfs2_aio_read(struct file *file, struct kiocb *req, struct iocb *iocb);
> int ocfs2_aio_write(struct file *file, struct kiocb *req, struct iocb *iocb);
> +struct buffer_head *ocfs2_bread(struct inode *inode,
> + int block, int *err, int reada);
>
> void ocfs2_set_inode_flags(struct inode *inode);
> void ocfs2_get_inode_flags(struct ocfs2_inode_info *oi);
> diff --git a/fs/ocfs2/ocfs2_fs.h b/fs/ocfs2/ocfs2_fs.h
> index dd17137..04bebd2 100644
> --- a/fs/ocfs2/ocfs2_fs.h
> +++ b/fs/ocfs2/ocfs2_fs.h
> @@ -878,6 +878,101 @@ static inline int ocfs2_xattr_get_type(struct ocfs2_xattr_entry *xe)
> return xe->xe_type & OCFS2_XATTR_TYPE_MASK;
> }
>
> +/*
> + * On disk structures for global quota file
> + */
> +
> +/* Magic numbers and known versions for global quota files */
> +#define OCFS2_GLOBAL_QMAGICS {\
> + 0x0cf52470, /* USRQUOTA */ \
> + 0x0cf52471 /* GRPQUOTA */ \
> +}
> +
> +#define OCFS2_GLOBAL_QVERSIONS {\
> + 0, \
> + 0, \
> +}
> +
> +/* Generic header of all quota files */
> +struct ocfs2_disk_dqheader {
> + __le32 dqh_magic; /* Magic number identifying file */
> + __le32 dqh_version; /* Quota format version */
> +};
> +
> +#define OCFS2_GLOBAL_INFO_OFF (sizeof(struct ocfs2_disk_dqheader))
> +
> +/* Information header of global quota file (immediately follows the generic
> + * header) */
> +struct ocfs2_global_disk_dqinfo {
> +/*00*/ __le32 dqi_bgrace;
> + __le32 dqi_igrace;
> + __le32 dqi_syncms;
> + __le32 dqi_blocks;
> +/*10*/ __le32 dqi_free_blk;
> + __le32 dqi_free_entry;
> +};
> +
> +/* Structure with global user / group information. We reserve some space
> + * for future use. */
> +struct ocfs2_global_disk_dqblk {
> +/*00*/ __le32 dqb_id; /* ID the structure belongs to */
> + __le32 dqb_use_count; /* Number of nodes having reference to this structure */
> + __le64 dqb_ihardlimit; /* absolute limit on allocated inodes */
> +/*10*/ __le64 dqb_isoftlimit; /* preferred inode limit */
> + __le64 dqb_curinodes; /* current # allocated inodes */
> +/*20*/ __le64 dqb_bhardlimit; /* absolute limit on disk space */
> + __le64 dqb_bsoftlimit; /* preferred limit on disk space */
> +/*30*/ __le64 dqb_curspace; /* current space occupied */
> + __le64 dqb_btime; /* time limit for excessive disk use */
> +/*40*/ __le64 dqb_itime; /* time limit for excessive inode use */
> + __le64 dqb_pad1;
> +/*50*/ __le64 dqb_pad2;
> +};
> +
> +/*
> + * On-disk structures for local quota file
> + */
> +
> +/* Magic numbers and known versions for local quota files */
> +#define OCFS2_LOCAL_QMAGICS {\
> + 0x0cf524c0, /* USRQUOTA */ \
> + 0x0cf524c1 /* GRPQUOTA */ \
> +}
> +
> +#define OCFS2_LOCAL_QVERSIONS {\
> + 0, \
> + 0, \
> +}
> +
> +/* Quota flags in dqinfo header */
> +#define OLQF_CLEAN 0x0001 /* Quota file is empty (this should be after\
> + * quota has been cleanly turned off) */
> +
> +#define OCFS2_LOCAL_INFO_OFF (sizeof(struct ocfs2_disk_dqheader))
> +
> +/* Information header of local quota file (immediately follows the generic
> + * header) */
> +struct ocfs2_local_disk_dqinfo {
> + __le32 dqi_flags; /* Flags for quota file */
> + __le32 dqi_chunks; /* Number of chunks of quota structures
> + * with a bitmap */
> + __le32 dqi_blocks; /* Number of blocks allocated for quota file */
> +};
> +
> +/* Header of one chunk of a quota file */
> +struct ocfs2_local_disk_chunk {
> + __le32 dqc_free; /* Number of free entries in the bitmap */
> + u8 dqc_bitmap[0]; /* Bitmap of entries in the corresponding
> + * chunk of quota file */
> +};
> +
> +/* One entry in local quota file */
> +struct ocfs2_local_disk_dqblk {
> +/*00*/ __le64 dqb_id; /* id this quota applies to */
> + __le64 dqb_spacemod; /* Change in the amount of used space */
> +/*10*/ __le64 dqb_inodemod; /* Change in the amount of used inodes */
> +};
> +
> #ifdef __KERNEL__
> static inline int ocfs2_fast_symlink_chars(struct super_block *sb)
> {
> diff --git a/fs/ocfs2/ocfs2_lockid.h b/fs/ocfs2/ocfs2_lockid.h
> index 82c200f..eb6f50c 100644
> --- a/fs/ocfs2/ocfs2_lockid.h
> +++ b/fs/ocfs2/ocfs2_lockid.h
> @@ -46,6 +46,7 @@ enum ocfs2_lock_type {
> OCFS2_LOCK_TYPE_DENTRY,
> OCFS2_LOCK_TYPE_OPEN,
> OCFS2_LOCK_TYPE_FLOCK,
> + OCFS2_LOCK_TYPE_QINFO,
> OCFS2_NUM_LOCK_TYPES
> };
>
> @@ -77,6 +78,9 @@ static inline char ocfs2_lock_type_char(enum ocfs2_lock_type type)
> case OCFS2_LOCK_TYPE_FLOCK:
> c = 'F';
> break;
> + case OCFS2_LOCK_TYPE_QINFO:
> + c = 'Q';
> + break;
> default:
> c = '\0';
> }
> @@ -95,6 +99,7 @@ static char *ocfs2_lock_type_strings[] = {
> [OCFS2_LOCK_TYPE_DENTRY] = "Dentry",
> [OCFS2_LOCK_TYPE_OPEN] = "Open",
> [OCFS2_LOCK_TYPE_FLOCK] = "Flock",
> + [OCFS2_LOCK_TYPE_QINFO] = "Quota",
> };
>
> static inline const char *ocfs2_lock_type_string(enum ocfs2_lock_type type)
> diff --git a/fs/ocfs2/quota.h b/fs/ocfs2/quota.h
> new file mode 100644
> index 0000000..87545ca
> --- /dev/null
> +++ b/fs/ocfs2/quota.h
> @@ -0,0 +1,97 @@
> +/*
> + * quota.h for OCFS2
> + *
> + * On disk quota structures for local and global quota file, in-memory
> + * structures.
> + *
> + */
> +
> +#ifndef _OCFS2_QUOTA_H
> +#define _OCFS2_QUOTA_H
> +
> +#include <linux/types.h>
> +#include <linux/slab.h>
> +#include <linux/quota.h>
> +#include <linux/list.h>
> +#include <linux/dqblk_qtree.h>
> +
> +#include "ocfs2.h"
> +
> +/* Common stuff */
> +/* id number of quota format */
> +#define QFMT_OCFS2 3
> +
> +/* How many bytes to we reserve in each quota file block for our internal
> + * purposes? E.g. checksums... */
> +#define OCFS2_QBLK_RESERVED_SPACE 8
> +
> +/*
> + * In-memory structures
> + */
> +struct ocfs2_dquot {
> + struct dquot dq_dquot; /* Generic VFS dquot */
> + loff_t dq_local_off; /* Offset in the local quota file */
> + struct ocfs2_quota_chunk *dq_chunk; /* Chunk dquot is in */
> + unsigned int dq_use_count; /* Number of nodes having reference to this entry in global quota file */
> + s64 dq_origspace; /* Last globally synced space usage */
> + s64 dq_originodes; /* Last globally synced inode usage */
> +};
> +
> +/* In-memory structure with quota header information */
> +struct ocfs2_mem_dqinfo {
> + unsigned int dqi_type; /* Quota type this structure describes */
> + unsigned int dqi_chunks; /* Number of chunks in local quota file */
> + unsigned int dqi_blocks; /* Number of blocks allocated for local quota file */
> + unsigned int dqi_syncms; /* How often should we sync with other nodes */
> + struct list_head dqi_chunk; /* List of chunks */
> + struct inode *dqi_gqinode; /* Global quota file inode */
> + struct ocfs2_lock_res dqi_gqlock; /* Lock protecting quota information structure */
> + struct buffer_head *dqi_gqi_bh; /* Buffer head with global quota file inode - set only if inode lock is obtained */
> + int dqi_gqi_count; /* Number of holders of dqi_gqi_bh */
> + struct buffer_head *dqi_lqi_bh; /* Buffer head with local quota file inode */
> + struct buffer_head *dqi_ibh; /* Buffer with information header */
> + struct qtree_mem_dqinfo dqi_gi; /* Info about global file */
> +};
> +
> +static inline struct ocfs2_dquot *OCFS2_DQUOT(struct dquot *dquot)
> +{
> + return container_of(dquot, struct ocfs2_dquot, dq_dquot);
> +}
> +
> +struct ocfs2_quota_chunk {
> + struct list_head qc_chunk; /* List of quotafile chunks */
> + int qc_num; /* Number of quota chunk */
> + struct buffer_head *qc_headerbh; /* Buffer head with chunk header */
> +};
> +
> +extern struct kmem_cache *ocfs2_dquot_cachep;
> +extern struct kmem_cache *ocfs2_qf_chunk_cachep;
> +
> +extern struct qtree_fmt_operations ocfs2_global_ops;
> +
> +ssize_t ocfs2_quota_read(struct super_block *sb, int type, char *data,
> + size_t len, loff_t off);
> +ssize_t ocfs2_quota_write(struct super_block *sb, int type,
> + const char *data, size_t len, loff_t off);
> +int ocfs2_global_read_info(struct super_block *sb, int type);
> +int ocfs2_global_write_info(struct super_block *sb, int type);
> +int ocfs2_global_read_dquot(struct dquot *dquot);
> +int __ocfs2_sync_dquot(struct dquot *dquot, int freeing);
> +static inline int ocfs2_sync_dquot(struct dquot *dquot)
> +{
> + return __ocfs2_sync_dquot(dquot, 0);
> +}
> +static inline int ocfs2_global_release_dquot(struct dquot *dquot)
> +{
> + return __ocfs2_sync_dquot(dquot, 1);
> +}
> +
> +int ocfs2_lock_global_qf(struct ocfs2_mem_dqinfo *oinfo, int ex);
> +void ocfs2_unlock_global_qf(struct ocfs2_mem_dqinfo *oinfo, int ex);
> +struct buffer_head *ocfs2_read_quota_block(struct inode *inode,
> + int block, int *err);
> +
> +extern struct dquot_operations ocfs2_quota_operations;
> +extern struct quota_format_type ocfs2_quota_format;
> +
> +#endif /* _OCFS2_QUOTA_H */
> diff --git a/fs/ocfs2/quota_global.c b/fs/ocfs2/quota_global.c
> new file mode 100644
> index 0000000..b937f07
> --- /dev/null
> +++ b/fs/ocfs2/quota_global.c
> @@ -0,0 +1,863 @@
> +/*
> + * Implementation of operations over global quota file
> + */
> +#include <linux/fs.h>
> +#include <linux/quota.h>
> +#include <linux/quotaops.h>
> +#include <linux/dqblk_qtree.h>
> +
> +#define MLOG_MASK_PREFIX ML_QUOTA
> +#include <cluster/masklog.h>
> +
> +#include "ocfs2_fs.h"
> +#include "ocfs2.h"
> +#include "alloc.h"
> +#include "inode.h"
> +#include "journal.h"
> +#include "file.h"
> +#include "sysfile.h"
> +#include "dlmglue.h"
> +#include "quota.h"
> +
> +static void ocfs2_global_disk2memdqb(struct dquot *dquot, void *dp)
> +{
> + struct ocfs2_global_disk_dqblk *d = dp;
> + struct mem_dqblk *m = &dquot->dq_dqb;
> +
> + /* Update from disk only entries not set by the admin */
> + if (!test_bit(DQ_LASTSET_B + QIF_ILIMITS_B, &dquot->dq_flags)) {
> + m->dqb_ihardlimit = le64_to_cpu(d->dqb_ihardlimit);
> + m->dqb_isoftlimit = le64_to_cpu(d->dqb_isoftlimit);
> + }
> + if (!test_bit(DQ_LASTSET_B + QIF_INODES_B, &dquot->dq_flags))
> + m->dqb_curinodes = le64_to_cpu(d->dqb_curinodes);
> + if (!test_bit(DQ_LASTSET_B + QIF_BLIMITS_B, &dquot->dq_flags)) {
> + m->dqb_bhardlimit = le64_to_cpu(d->dqb_bhardlimit);
> + m->dqb_bsoftlimit = le64_to_cpu(d->dqb_bsoftlimit);
> + }
> + if (!test_bit(DQ_LASTSET_B + QIF_SPACE_B, &dquot->dq_flags))
> + m->dqb_curspace = le64_to_cpu(d->dqb_curspace);
> + if (!test_bit(DQ_LASTSET_B + QIF_BTIME_B, &dquot->dq_flags))
> + m->dqb_btime = le64_to_cpu(d->dqb_btime);
> + if (!test_bit(DQ_LASTSET_B + QIF_ITIME_B, &dquot->dq_flags))
> + m->dqb_itime = le64_to_cpu(d->dqb_itime);
> + OCFS2_DQUOT(dquot)->dq_use_count = le32_to_cpu(d->dqb_use_count);
> +}
> +
> +static void ocfs2_global_mem2diskdqb(void *dp, struct dquot *dquot)
> +{
> + struct ocfs2_global_disk_dqblk *d = dp;
> + struct mem_dqblk *m = &dquot->dq_dqb;
> +
> + d->dqb_id = cpu_to_le32(dquot->dq_id);
> + d->dqb_use_count = cpu_to_le32(OCFS2_DQUOT(dquot)->dq_use_count);
> + d->dqb_ihardlimit = cpu_to_le64(m->dqb_ihardlimit);
> + d->dqb_isoftlimit = cpu_to_le64(m->dqb_isoftlimit);
> + d->dqb_curinodes = cpu_to_le64(m->dqb_curinodes);
> + d->dqb_bhardlimit = cpu_to_le64(m->dqb_bhardlimit);
> + d->dqb_bsoftlimit = cpu_to_le64(m->dqb_bsoftlimit);
> + d->dqb_curspace = cpu_to_le64(m->dqb_curspace);
> + d->dqb_btime = cpu_to_le64(m->dqb_btime);
> + d->dqb_itime = cpu_to_le64(m->dqb_itime);
> + d->dqb_pad1 = d->dqb_pad2 = 0;
> +}
> +
> +static int ocfs2_global_is_id(void *dp, struct dquot *dquot)
> +{
> + struct ocfs2_global_disk_dqblk *d = dp;
> + struct ocfs2_mem_dqinfo *oinfo =
> + sb_dqinfo(dquot->dq_sb, dquot->dq_type)->dqi_priv;
> +
> + if (qtree_entry_unused(&oinfo->dqi_gi, dp))
> + return 0;
> + return le32_to_cpu(d->dqb_id) == dquot->dq_id;
> +}
> +
> +struct qtree_fmt_operations ocfs2_global_ops = {
> + .mem2disk_dqblk = ocfs2_global_mem2diskdqb,
> + .disk2mem_dqblk = ocfs2_global_disk2memdqb,
> + .is_id = ocfs2_global_is_id,
> +};
> +
> +
> +struct buffer_head *ocfs2_read_quota_block(struct inode *inode,
> + int block, int *err)
> +{
> + return ocfs2_bread(inode, block, err, 0);
> +}
> +
> +/* Read data from global quotafile - avoid pagecache and such because we cannot
> + * afford acquiring the locks... We use quota cluster lock to serialize
> + * operations. Caller is responsible for acquiring it. */
> +ssize_t ocfs2_quota_read(struct super_block *sb, int type, char *data,
> + size_t len, loff_t off)
> +{
> + struct ocfs2_mem_dqinfo *oinfo = sb_dqinfo(sb, type)->dqi_priv;
> + struct inode *gqinode = oinfo->dqi_gqinode;
> + loff_t i_size = i_size_read(gqinode);
> + int offset = off & (sb->s_blocksize - 1);
> + sector_t blk = off >> sb->s_blocksize_bits;
> + int err = 0;
> + struct buffer_head *bh;
> + size_t toread, tocopy;
> +
> + if (off > i_size)
> + return 0;
> + if (off + len > i_size)
> + len = i_size - off;
> + toread = len;
> + while (toread > 0) {
> + tocopy = min((size_t)(sb->s_blocksize - offset), toread);
> + bh = ocfs2_read_quota_block(gqinode, blk, &err);
> + if (!bh) {
> + mlog_errno(err);
> + return err;
> + }
> + 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) */
> +ssize_t ocfs2_quota_write(struct super_block *sb, int type,
> + const char *data, size_t len, loff_t off)
> +{
> + struct mem_dqinfo *info = sb_dqinfo(sb, type);
> + struct ocfs2_mem_dqinfo *oinfo = info->dqi_priv;
> + struct inode *gqinode = oinfo->dqi_gqinode;
> + int offset = off & (sb->s_blocksize - 1);
> + sector_t blk = off >> sb->s_blocksize_bits;
> + int err = 0;
> + struct buffer_head *bh;
> + handle_t *handle = journal_current_handle();
> + size_t tocopy, towrite = len;
> +
> + if (!handle) {
> + mlog(ML_ERROR, "Quota write (off=%llu, len=%llu) cancelled "
> + "because transaction was not started.\n",
> + (unsigned long long)off, (unsigned long long)len);
> + return -EIO;
> + }
> + mutex_lock_nested(&gqinode->i_mutex, I_MUTEX_QUOTA);
> + if (gqinode->i_size < off + len) {
> + down_write(&OCFS2_I(gqinode)->ip_alloc_sem);
> + err = ocfs2_extend_no_holes(gqinode, off + len, off);
> + up_write(&OCFS2_I(gqinode)->ip_alloc_sem);
> + if (err < 0)
> + goto out;
> + err = ocfs2_simple_size_update(gqinode,
> + oinfo->dqi_gqi_bh,
> + off + len);
> + if (err < 0)
> + goto out;
> + }
> + WARN_ON(off >> sb->s_blocksize_bits != \
> + (off + len) >> sb->s_blocksize_bits);
> + WARN_ON(((off + len) & ((1 << sb->s_blocksize_bits) - 1)) >
> + sb->s_blocksize - OCFS2_QBLK_RESERVED_SPACE);
> + for (towrite = len; towrite > 0; towrite -= tocopy) {
> + tocopy = min(towrite, (size_t)(sb->s_blocksize - offset));
> + bh = ocfs2_read_quota_block(gqinode, blk, &err);
> + if (!bh) {
> + mlog_errno(err);
> + return err;
> + }
> + err = ocfs2_journal_access(handle, gqinode, bh,
> + OCFS2_JOURNAL_ACCESS_WRITE);
> + if (err < 0) {
> + brelse(bh);
> + goto out;
> + }
> + lock_buffer(bh);
> + memcpy(bh->b_data + offset, data, tocopy);
> + flush_dcache_page(bh->b_page);
> + unlock_buffer(bh);
> + err = ocfs2_journal_dirty(handle, bh);
> + brelse(bh);
> + if (err < 0)
> + goto out;
> + offset = 0;
> + data += tocopy;
> + blk++;
> + }
> +out:
> + /* Nothing written? */
> + if (len == towrite) {
> + mutex_unlock(&gqinode->i_mutex);
> + mlog_errno(err);
> + return err;
> + }
> + gqinode->i_version++;
> + ocfs2_mark_inode_dirty(handle, gqinode, oinfo->dqi_gqi_bh);
> + mutex_unlock(&gqinode->i_mutex);
> + return len - towrite;
> +}
> +
> +int ocfs2_lock_global_qf(struct ocfs2_mem_dqinfo *oinfo, int ex)
> +{
> + int status;
> + struct buffer_head *bh = NULL;
> +
> + status = ocfs2_inode_lock(oinfo->dqi_gqinode, &bh, ex);
> + if (status < 0)
> + return status;
> + spin_lock(&dq_data_lock);
> + if (!oinfo->dqi_gqi_count++)
> + oinfo->dqi_gqi_bh = bh;
> + else
> + WARN_ON(bh != oinfo->dqi_gqi_bh);
> + spin_unlock(&dq_data_lock);
> + return 0;
> +}
> +
> +void ocfs2_unlock_global_qf(struct ocfs2_mem_dqinfo *oinfo, int ex)
> +{
> + ocfs2_inode_unlock(oinfo->dqi_gqinode, ex);
> + brelse(oinfo->dqi_gqi_bh);
> + spin_lock(&dq_data_lock);
> + if (!--oinfo->dqi_gqi_count)
> + oinfo->dqi_gqi_bh = NULL;
> + spin_unlock(&dq_data_lock);
> +}
> +
> +/* Read information header from global quota file */
> +int ocfs2_global_read_info(struct super_block *sb, int type)
> +{
> + struct inode *gqinode = NULL;
> + unsigned int ino[MAXQUOTAS] = { USER_QUOTA_SYSTEM_INODE,
> + GROUP_QUOTA_SYSTEM_INODE };
> + struct ocfs2_global_disk_dqinfo dinfo;
> + struct mem_dqinfo *info = sb_dqinfo(sb, type);
> + struct ocfs2_mem_dqinfo *oinfo = info->dqi_priv;
> + int status;
> +
> + mlog_entry_void();
> +
> + /* Read global header */
> + gqinode = ocfs2_get_system_file_inode(OCFS2_SB(sb), ino[type],
> + OCFS2_INVALID_SLOT);
> + if (!gqinode) {
> + mlog(ML_ERROR, "failed to get global quota inode (type=%d)\n",
> + type);
> + status = -EINVAL;
> + goto out_err;
> + }
> + oinfo->dqi_gi.dqi_sb = sb;
> + oinfo->dqi_gi.dqi_type = type;
> + ocfs2_qinfo_lock_res_init(&oinfo->dqi_gqlock, oinfo);
> + oinfo->dqi_gi.dqi_entry_size = sizeof(struct ocfs2_global_disk_dqblk);
> + oinfo->dqi_gi.dqi_ops = &ocfs2_global_ops;
> + oinfo->dqi_gqi_bh = NULL;
> + oinfo->dqi_gqi_count = 0;
> + oinfo->dqi_gqinode = gqinode;
> + status = ocfs2_lock_global_qf(oinfo, 0);
> + if (status < 0) {
> + mlog_errno(status);
> + goto out_err;
> + }
> + status = sb->s_op->quota_read(sb, type, (char *)&dinfo,
> + sizeof(struct ocfs2_global_disk_dqinfo),
> + OCFS2_GLOBAL_INFO_OFF);
> + ocfs2_unlock_global_qf(oinfo, 0);
> + if (status != sizeof(struct ocfs2_global_disk_dqinfo)) {
> + mlog(ML_ERROR, "Cannot read global quota info (%d).\n",
> + status);
> + if (status >= 0)
> + status = -EIO;
> + mlog_errno(status);
> + goto out_err;
> + }
> + info->dqi_bgrace = le32_to_cpu(dinfo.dqi_bgrace);
> + info->dqi_igrace = le32_to_cpu(dinfo.dqi_igrace);
> + oinfo->dqi_syncms = le32_to_cpu(dinfo.dqi_syncms);
> + oinfo->dqi_gi.dqi_blocks = le32_to_cpu(dinfo.dqi_blocks);
> + oinfo->dqi_gi.dqi_free_blk = le32_to_cpu(dinfo.dqi_free_blk);
> + oinfo->dqi_gi.dqi_free_entry = le32_to_cpu(dinfo.dqi_free_entry);
> + oinfo->dqi_gi.dqi_blocksize_bits = sb->s_blocksize_bits;
> + oinfo->dqi_gi.dqi_usable_bs = sb->s_blocksize -
> + OCFS2_QBLK_RESERVED_SPACE;
> + oinfo->dqi_gi.dqi_qtree_depth = qtree_depth(&oinfo->dqi_gi);
> +out_err:
> + mlog_exit(status);
> + return status;
> +}
> +
> +/* Write information to global quota file. Expects exlusive lock on quota
> + * file inode and quota info */
> +static int __ocfs2_global_write_info(struct super_block *sb, int type)
> +{
> + struct mem_dqinfo *info = sb_dqinfo(sb, type);
> + struct ocfs2_mem_dqinfo *oinfo = info->dqi_priv;
> + struct ocfs2_global_disk_dqinfo dinfo;
> + ssize_t size;
> +
> + spin_lock(&dq_data_lock);
> + info->dqi_flags &= ~DQF_INFO_DIRTY;
> + dinfo.dqi_bgrace = cpu_to_le32(info->dqi_bgrace);
> + dinfo.dqi_igrace = cpu_to_le32(info->dqi_igrace);
> + spin_unlock(&dq_data_lock);
> + dinfo.dqi_syncms = cpu_to_le32(oinfo->dqi_syncms);
> + dinfo.dqi_blocks = cpu_to_le32(oinfo->dqi_gi.dqi_blocks);
> + dinfo.dqi_free_blk = cpu_to_le32(oinfo->dqi_gi.dqi_free_blk);
> + dinfo.dqi_free_entry = cpu_to_le32(oinfo->dqi_gi.dqi_free_entry);
> + size = sb->s_op->quota_write(sb, type, (char *)&dinfo,
> + sizeof(struct ocfs2_global_disk_dqinfo),
> + OCFS2_GLOBAL_INFO_OFF);
> + if (size != sizeof(struct ocfs2_global_disk_dqinfo)) {
> + mlog(ML_ERROR, "Cannot write global quota info structure\n");
> + if (size >= 0)
> + size = -EIO;
> + return size;
> + }
> + return 0;
> +}
> +
> +int ocfs2_global_write_info(struct super_block *sb, int type)
> +{
> + int err;
> + struct ocfs2_mem_dqinfo *info = sb_dqinfo(sb, type)->dqi_priv;
> +
> + err = ocfs2_qinfo_lock(info, 1);
> + if (err < 0)
> + return err;
> + err = __ocfs2_global_write_info(sb, type);
> + ocfs2_qinfo_unlock(info, 1);
> + return err;
> +}
> +
> +/* Read in information from global quota file and acquire a reference to it.
> + * dquot_acquire() has already started the transaction and locked quota file */
> +int ocfs2_global_read_dquot(struct dquot *dquot)
> +{
> + int err, err2, ex = 0;
> + struct ocfs2_mem_dqinfo *info =
> + sb_dqinfo(dquot->dq_sb, dquot->dq_type)->dqi_priv;
> +
> + err = ocfs2_qinfo_lock(info, 0);
> + if (err < 0)
> + goto out;
> + err = qtree_read_dquot(&info->dqi_gi, dquot);
> + if (err < 0)
> + goto out_qlock;
> + OCFS2_DQUOT(dquot)->dq_use_count++;
> + OCFS2_DQUOT(dquot)->dq_origspace = dquot->dq_dqb.dqb_curspace;
> + OCFS2_DQUOT(dquot)->dq_originodes = dquot->dq_dqb.dqb_curinodes;
> + if (!dquot->dq_off) { /* No real quota entry? */
> + /* Upgrade to exclusive lock for allocation */
> + err = ocfs2_qinfo_lock(info, 1);
> + if (err < 0)
> + goto out_qlock;
> + ex = 1;
> + }
> + err = qtree_write_dquot(&info->dqi_gi, dquot);
> + if (ex && info_dirty(sb_dqinfo(dquot->dq_sb, dquot->dq_type))) {
> + err2 = __ocfs2_global_write_info(dquot->dq_sb, dquot->dq_type);
> + if (!err)
> + err = err2;
> + }
> +out_qlock:
> + if (ex)
> + ocfs2_qinfo_unlock(info, 1);
> + ocfs2_qinfo_unlock(info, 0);
> +out:
> + if (err < 0)
> + mlog_errno(err);
> + return err;
> +}
> +
> +/* Sync local information about quota modifications with global quota file.
> + * Caller must have started the transaction and obtained exclusive lock for
> + * global quota file inode */
> +int __ocfs2_sync_dquot(struct dquot *dquot, int freeing)
> +{
> + int err, err2;
> + struct super_block *sb = dquot->dq_sb;
> + int type = dquot->dq_type;
> + struct ocfs2_mem_dqinfo *info = sb_dqinfo(sb, type)->dqi_priv;
> + struct ocfs2_global_disk_dqblk dqblk;
> + s64 spacechange, inodechange;
> + time_t olditime, oldbtime;
> +
> + err = sb->s_op->quota_read(sb, type, (char *)&dqblk,
> + sizeof(struct ocfs2_global_disk_dqblk),
> + dquot->dq_off);
> + if (err != sizeof(struct ocfs2_global_disk_dqblk)) {
> + if (err >= 0) {
> + mlog(ML_ERROR, "Short read from global quota file "
> + "(%u read)\n", err);
> + err = -EIO;
> + }
> + goto out;
> + }
> +
> + /* Update space and inode usage. Get also other information from
> + * global quota file so that we don't overwrite any changes there.
> + * We are */
> + spin_lock(&dq_data_lock);
> + spacechange = dquot->dq_dqb.dqb_curspace -
> + OCFS2_DQUOT(dquot)->dq_origspace;
> + inodechange = dquot->dq_dqb.dqb_curinodes -
> + OCFS2_DQUOT(dquot)->dq_originodes;
> + olditime = dquot->dq_dqb.dqb_itime;
> + oldbtime = dquot->dq_dqb.dqb_btime;
> + ocfs2_global_disk2memdqb(dquot, &dqblk);
> + mlog(0, "Syncing global dquot %d space %lld+%lld, inodes %lld+%lld\n",
> + dquot->dq_id, dquot->dq_dqb.dqb_curspace, spacechange,
> + dquot->dq_dqb.dqb_curinodes, inodechange);
> + if (!test_bit(DQ_LASTSET_B + QIF_SPACE_B, &dquot->dq_flags))
> + dquot->dq_dqb.dqb_curspace += spacechange;
> + if (!test_bit(DQ_LASTSET_B + QIF_INODES_B, &dquot->dq_flags))
> + dquot->dq_dqb.dqb_curinodes += inodechange;
> + /* Now merge grace time changes... */
> + if (!test_bit(DQ_LASTSET_B + QIF_BTIME_B, &dquot->dq_flags) &&
> + oldbtime > 0) {
> + if (dquot->dq_dqb.dqb_btime > 0)
> + dquot->dq_dqb.dqb_btime =
> + min(dquot->dq_dqb.dqb_btime, oldbtime);
> + else
> + dquot->dq_dqb.dqb_btime = oldbtime;
> + }
> + if (!test_bit(DQ_LASTSET_B + QIF_ITIME_B, &dquot->dq_flags) &&
> + olditime > 0) {
> + if (dquot->dq_dqb.dqb_itime > 0)
> + dquot->dq_dqb.dqb_itime =
> + min(dquot->dq_dqb.dqb_itime, olditime);
> + else
> + dquot->dq_dqb.dqb_itime = olditime;
> + }
> + /* All information is properly updated, clear the flags */
> + __clear_bit(DQ_LASTSET_B + QIF_SPACE_B, &dquot->dq_flags);
> + __clear_bit(DQ_LASTSET_B + QIF_INODES_B, &dquot->dq_flags);
> + __clear_bit(DQ_LASTSET_B + QIF_BLIMITS_B, &dquot->dq_flags);
> + __clear_bit(DQ_LASTSET_B + QIF_ILIMITS_B, &dquot->dq_flags);
> + __clear_bit(DQ_LASTSET_B + QIF_BTIME_B, &dquot->dq_flags);
> + __clear_bit(DQ_LASTSET_B + QIF_ITIME_B, &dquot->dq_flags);
> + OCFS2_DQUOT(dquot)->dq_origspace = dquot->dq_dqb.dqb_curspace;
> + OCFS2_DQUOT(dquot)->dq_originodes = dquot->dq_dqb.dqb_curinodes;
> + spin_unlock(&dq_data_lock);
> + err = ocfs2_qinfo_lock(info, freeing);
> + if (err < 0) {
> + mlog(ML_ERROR, "Failed to lock quota info, loosing quota write"
> + " (type=%d, id=%u)\n", dquot->dq_type,
> + (unsigned)dquot->dq_id);
> + goto out;
> + }
> + if (freeing)
> + OCFS2_DQUOT(dquot)->dq_use_count--;
> + err = qtree_write_dquot(&info->dqi_gi, dquot);
> + if (err < 0)
> + goto out_qlock;
> + if (freeing && !OCFS2_DQUOT(dquot)->dq_use_count) {
> + err = qtree_release_dquot(&info->dqi_gi, dquot);
> + if (info_dirty(sb_dqinfo(sb, type))) {
> + err2 = __ocfs2_global_write_info(sb, type);
> + if (!err)
> + err = err2;
> + }
> + }
> +out_qlock:
> + ocfs2_qinfo_unlock(info, freeing);
> +out:
> + if (err < 0)
> + mlog_errno(err);
> + return err;
> +}
> +
> +/*
> + * Wrappers for generic quota functions
> + */
> +
> +static int ocfs2_write_dquot(struct dquot *dquot)
> +{
> + handle_t *handle;
> + struct ocfs2_super *osb = OCFS2_SB(dquot->dq_sb);
> + int status = 0;
> +
> + mlog_entry("id=%u, type=%d", dquot->dq_id, dquot->dq_type);
> +
> + handle = ocfs2_start_trans(osb, OCFS2_QWRITE_CREDITS);
> + if (IS_ERR(handle)) {
> + status = PTR_ERR(handle);
> + mlog_errno(status);
> + goto out;
> + }
> + status = dquot_commit(dquot);
> + ocfs2_commit_trans(osb, handle);
> +out:
> + mlog_exit(status);
> + return status;
> +}
> +
> +int ocfs2_calc_qdel_credits(struct super_block *sb, int type)
> +{
> + struct ocfs2_mem_dqinfo *oinfo;
> + int features[MAXQUOTAS] = { OCFS2_FEATURE_RO_COMPAT_USRQUOTA,
> + OCFS2_FEATURE_RO_COMPAT_GRPQUOTA };
> +
> + if (!OCFS2_HAS_RO_COMPAT_FEATURE(sb, features[type]))
> + return 0;
> +
> + oinfo = sb_dqinfo(sb, type)->dqi_priv;
> + /* We modify tree, leaf block, global info, local chunk header,
> + * global and local inode */
> + return oinfo->dqi_gi.dqi_qtree_depth + 2 + 1 +
> + 2 * OCFS2_INODE_UPDATE_CREDITS;
> +}
> +
> +static int ocfs2_release_dquot(struct dquot *dquot)
> +{
> + handle_t *handle;
> + struct ocfs2_mem_dqinfo *oinfo =
> + sb_dqinfo(dquot->dq_sb, dquot->dq_type)->dqi_priv;
> + struct ocfs2_super *osb = OCFS2_SB(dquot->dq_sb);
> + int status = 0;
> +
> + mlog_entry("id=%u, type=%d", dquot->dq_id, dquot->dq_type);
> +
> + status = ocfs2_lock_global_qf(oinfo, 1);
> + if (status < 0)
> + goto out;
> + handle = ocfs2_start_trans(osb,
> + ocfs2_calc_qdel_credits(dquot->dq_sb, dquot->dq_type));
> + if (IS_ERR(handle)) {
> + status = PTR_ERR(handle);
> + mlog_errno(status);
> + goto out_ilock;
> + }
> + status = dquot_release(dquot);
> + ocfs2_commit_trans(osb, handle);
> +out_ilock:
> + ocfs2_unlock_global_qf(oinfo, 1);
> +out:
> + mlog_exit(status);
> + return status;
> +}
> +
> +int ocfs2_calc_qinit_credits(struct super_block *sb, int type)
> +{
> + struct ocfs2_mem_dqinfo *oinfo;
> + int features[MAXQUOTAS] = { OCFS2_FEATURE_RO_COMPAT_USRQUOTA,
> + OCFS2_FEATURE_RO_COMPAT_GRPQUOTA };
> + struct ocfs2_dinode *lfe, *gfe;
> +
> + if (!OCFS2_HAS_RO_COMPAT_FEATURE(sb, features[type]))
> + return 0;
> +
> + oinfo = sb_dqinfo(sb, type)->dqi_priv;
> + gfe = (struct ocfs2_dinode *)oinfo->dqi_gqi_bh->b_data;
> + lfe = (struct ocfs2_dinode *)oinfo->dqi_lqi_bh->b_data;
> + /* We can extend local file + global file. In local file we
> + * can modify info, chunk header block and dquot block. In
> + * global file we can modify info, tree and leaf block */
> + return ocfs2_calc_extend_credits(sb, &lfe->id2.i_list, 0) +
> + ocfs2_calc_extend_credits(sb, &gfe->id2.i_list, 0) +
> + 3 + oinfo->dqi_gi.dqi_qtree_depth + 2;
> +}
> +
> +static int ocfs2_acquire_dquot(struct dquot *dquot)
> +{
> + handle_t *handle;
> + struct ocfs2_mem_dqinfo *oinfo =
> + sb_dqinfo(dquot->dq_sb, dquot->dq_type)->dqi_priv;
> + struct ocfs2_super *osb = OCFS2_SB(dquot->dq_sb);
> + int status = 0;
> +
> + mlog_entry("id=%u, type=%d", dquot->dq_id, dquot->dq_type);
> + /* We need an exclusive lock, because we're going to update use count
> + * and instantiate possibly new dquot structure */
> + status = ocfs2_lock_global_qf(oinfo, 1);
> + if (status < 0)
> + goto out;
> + handle = ocfs2_start_trans(osb,
> + ocfs2_calc_qinit_credits(dquot->dq_sb, dquot->dq_type));
> + if (IS_ERR(handle)) {
> + status = PTR_ERR(handle);
> + mlog_errno(status);
> + goto out_ilock;
> + }
> + status = dquot_acquire(dquot);
> + ocfs2_commit_trans(osb, handle);
> +out_ilock:
> + ocfs2_unlock_global_qf(oinfo, 1);
> +out:
> + mlog_exit(status);
> + return status;
> +}
> +
> +static int ocfs2_mark_dquot_dirty(struct dquot *dquot)
> +{
> + unsigned long mask = (1 << (DQ_LASTSET_B + QIF_ILIMITS_B)) |
> + (1 << (DQ_LASTSET_B + QIF_BLIMITS_B)) |
> + (1 << (DQ_LASTSET_B + QIF_INODES_B)) |
> + (1 << (DQ_LASTSET_B + QIF_SPACE_B)) |
> + (1 << (DQ_LASTSET_B + QIF_BTIME_B)) |
> + (1 << (DQ_LASTSET_B + QIF_ITIME_B));
> + int sync = 0;
> + int status;
> + struct super_block *sb = dquot->dq_sb;
> + int type = dquot->dq_type;
> + struct ocfs2_mem_dqinfo *oinfo = sb_dqinfo(sb, type)->dqi_priv;
> + handle_t *handle;
> + struct ocfs2_super *osb = OCFS2_SB(sb);
> +
> + mlog_entry("id=%u, type=%d", dquot->dq_id, type);
> + dquot_mark_dquot_dirty(dquot);
> +
> + /* In case user set some limits, sync dquot immediately to global
> + * quota file so that information propagates quicker */
> + spin_lock(&dq_data_lock);
> + if (dquot->dq_flags & mask)
> + sync = 1;
> + spin_unlock(&dq_data_lock);
> + if (!sync) {
> + status = ocfs2_write_dquot(dquot);
> + goto out;
> + }
> + status = ocfs2_lock_global_qf(oinfo, 1);
> + if (status < 0)
> + goto out;
> + handle = ocfs2_start_trans(osb, OCFS2_QSYNC_CREDITS);
> + if (IS_ERR(handle)) {
> + status = PTR_ERR(handle);
> + mlog_errno(status);
> + goto out_ilock;
> + }
> + status = ocfs2_sync_dquot(dquot);
> + if (status < 0) {
> + mlog_errno(status);
> + goto out_trans;
> + }
> + /* Now write updated local dquot structure */
> + status = dquot_commit(dquot);
> +out_trans:
> + ocfs2_commit_trans(osb, handle);
> +out_ilock:
> + ocfs2_unlock_global_qf(oinfo, 1);
> +out:
> + mlog_exit(status);
> + return status;
> +}
> +
> +/* This should happen only after set_dqinfo(). */
> +static int ocfs2_write_info(struct super_block *sb, int type)
> +{
> + handle_t *handle;
> + int status = 0;
> + struct ocfs2_mem_dqinfo *oinfo = sb_dqinfo(sb, type)->dqi_priv;
> +
> + mlog_entry_void();
> +
> + status = ocfs2_lock_global_qf(oinfo, 1);
> + if (status < 0)
> + goto out;
> + handle = ocfs2_start_trans(OCFS2_SB(sb), OCFS2_QINFO_WRITE_CREDITS);
> + if (IS_ERR(handle)) {
> + status = PTR_ERR(handle);
> + mlog_errno(status);
> + goto out_ilock;
> + }
> + status = dquot_commit_info(sb, type);
> + ocfs2_commit_trans(OCFS2_SB(sb), handle);
> +out_ilock:
> + ocfs2_unlock_global_qf(oinfo, 1);
> +out:
> + mlog_exit(status);
> + return status;
> +}
> +
> +/* This is difficult. We have to lock quota inode and start transaction
> + * in this function but we don't want to take the penalty of exlusive
> + * quota file lock when we are just going to use cached structures. So
> + * we just take read lock check whether we have dquot cached and if so,
> + * we don't have to take the write lock... */
> +static int ocfs2_dquot_initialize(struct inode *inode, int type)
> +{
> + handle_t *handle = NULL;
> + int status = 0;
> + struct super_block *sb = inode->i_sb;
> + struct ocfs2_mem_dqinfo *oinfo;
> + int exclusive = 0;
> + int cnt;
> + qid_t id;
> +
> + mlog_entry_void();
> +
> + for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
> + if (type != -1 && cnt != type)
> + continue;
> + oinfo = sb_dqinfo(sb, cnt)->dqi_priv;
> + status = ocfs2_lock_global_qf(oinfo, 0);
> + if (status < 0)
> + goto out;
> + /* This is just a performance optimization not a reliable test.
> + * Since we hold an inode lock, noone can actually release
> + * the structure until we are finished with initialization. */
> + if (inode->i_dquot[cnt] != NODQUOT) {
> + ocfs2_unlock_global_qf(oinfo, 0);
> + continue;
> + }
> + /* When we have inode lock, we know that no dquot_release() can
> + * run and thus we can safely check whether we need to
> + * read+modify global file to get quota information or whether
> + * our node already has it. */
> + if (cnt == USRQUOTA)
> + id = inode->i_uid;
> + else if (cnt == GRPQUOTA)
> + id = inode->i_gid;
> + else
> + BUG();
> + /* Obtain exclusion from quota off... */
> + down_write(&sb_dqopt(sb)->dqptr_sem);
> + exclusive = !dquot_is_cached(sb, id, cnt);
> + up_write(&sb_dqopt(sb)->dqptr_sem);
> + if (exclusive) {
> + status = ocfs2_lock_global_qf(oinfo, 1);
> + if (status < 0) {
> + exclusive = 0;
> + mlog_errno(status);
> + goto out_ilock;
> + }
> + handle = ocfs2_start_trans(OCFS2_SB(sb),
> + ocfs2_calc_qinit_credits(sb, cnt));
> + if (IS_ERR(handle)) {
> + status = PTR_ERR(handle);
> + mlog_errno(status);
> + goto out_ilock;
> + }
> + }
> + dquot_initialize(inode, cnt);
> + if (exclusive) {
> + ocfs2_commit_trans(OCFS2_SB(sb), handle);
> + ocfs2_unlock_global_qf(oinfo, 1);
> + }
> + ocfs2_unlock_global_qf(oinfo, 0);
> + }
> + mlog_exit(0);
> + return 0;
> +out_ilock:
> + if (exclusive)
> + ocfs2_unlock_global_qf(oinfo, 1);
> + ocfs2_unlock_global_qf(oinfo, 0);
> +out:
> + mlog_exit(status);
> + return status;
> +}
> +
> +static int ocfs2_dquot_drop_slow(struct inode *inode)
> +{
> + int status;
> + int cnt;
> + int got_lock[MAXQUOTAS] = {0, 0};
> + handle_t *handle;
> + struct super_block *sb = inode->i_sb;
> + struct ocfs2_mem_dqinfo *oinfo;
> +
> + for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
> + oinfo = sb_dqinfo(sb, cnt)->dqi_priv;
> + status = ocfs2_lock_global_qf(oinfo, 1);
> + if (status < 0)
> + goto out;
> + got_lock[cnt] = 1;
> + }
> + handle = ocfs2_start_trans(OCFS2_SB(sb),
> + ocfs2_calc_qinit_credits(sb, USRQUOTA) +
> + ocfs2_calc_qinit_credits(sb, GRPQUOTA));
> + if (IS_ERR(handle)) {
> + status = PTR_ERR(handle);
> + mlog_errno(status);
> + goto out;
> + }
> + dquot_drop(inode);
> + ocfs2_commit_trans(OCFS2_SB(sb), handle);
> +out:
> + for (cnt = 0; cnt < MAXQUOTAS; cnt++)
> + if (got_lock[cnt]) {
> + oinfo = sb_dqinfo(sb, cnt)->dqi_priv;
> + ocfs2_unlock_global_qf(oinfo, 1);
> + }
> + return status;
> +}
> +
> +/* See the comment before ocfs2_dquot_initialize. */
> +static int ocfs2_dquot_drop(struct inode *inode)
> +{
> + int status = 0;
> + struct super_block *sb = inode->i_sb;
> + struct ocfs2_mem_dqinfo *oinfo;
> + int exclusive = 0;
> + int cnt;
> + int got_lock[MAXQUOTAS] = {0, 0};
> +
> + mlog_entry_void();
> + for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
> + oinfo = sb_dqinfo(sb, cnt)->dqi_priv;
> + status = ocfs2_lock_global_qf(oinfo, 0);
> + if (status < 0)
> + goto out;
> + got_lock[cnt] = 1;
> + }
> + /* Lock against anyone releasing references so that when when we check
> + * we know we are not going to be last ones to release dquot */
> + down_write(&sb_dqopt(sb)->dqptr_sem);
> + /* Urgh, this is a terrible hack :( */
> + for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
> + if (inode->i_dquot[cnt] != NODQUOT &&
> + atomic_read(&inode->i_dquot[cnt]->dq_count) > 1) {
> + exclusive = 1;
> + break;
> + }
> + }
> + if (!exclusive)
> + dquot_drop_locked(inode);
> + up_write(&sb_dqopt(sb)->dqptr_sem);
> +out:
> + for (cnt = 0; cnt < MAXQUOTAS; cnt++)
> + if (got_lock[cnt]) {
> + oinfo = sb_dqinfo(sb, cnt)->dqi_priv;
> + ocfs2_unlock_global_qf(oinfo, 0);
> + }
> + /* In case we bailed out because we had to do expensive locking
> + * do it now... */
> + if (exclusive)
> + status = ocfs2_dquot_drop_slow(inode);
> + mlog_exit(status);
> + return status;
> +}
> +
> +static struct dquot *ocfs2_alloc_dquot(struct super_block *sb, int type)
> +{
> + struct ocfs2_dquot *dquot =
> + kmem_cache_zalloc(ocfs2_dquot_cachep, GFP_NOFS);
> +
> + if (!dquot)
> + return NULL;
> + return &dquot->dq_dquot;
> +}
> +
> +static void ocfs2_destroy_dquot(struct dquot *dquot)
> +{
> + kmem_cache_free(ocfs2_dquot_cachep, dquot);
> +}
> +
> +struct dquot_operations ocfs2_quota_operations = {
> + .initialize = ocfs2_dquot_initialize,
> + .drop = ocfs2_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 = ocfs2_write_dquot,
> + .acquire_dquot = ocfs2_acquire_dquot,
> + .release_dquot = ocfs2_release_dquot,
> + .mark_dirty = ocfs2_mark_dquot_dirty,
> + .write_info = ocfs2_write_info,
> + .alloc_dquot = ocfs2_alloc_dquot,
> + .destroy_dquot = ocfs2_destroy_dquot,
> +};
> diff --git a/fs/ocfs2/quota_local.c b/fs/ocfs2/quota_local.c
> new file mode 100644
> index 0000000..55c3f2f
> --- /dev/null
> +++ b/fs/ocfs2/quota_local.c
> @@ -0,0 +1,833 @@
> +/*
> + * Implementation of operations over local quota file
> + */
> +
> +#include <linux/fs.h>
> +#include <linux/quota.h>
> +#include <linux/quotaops.h>
> +#include <linux/module.h>
> +
> +#define MLOG_MASK_PREFIX ML_QUOTA
> +#include <cluster/masklog.h>
> +
> +#include "ocfs2_fs.h"
> +#include "ocfs2.h"
> +#include "inode.h"
> +#include "alloc.h"
> +#include "file.h"
> +#include "buffer_head_io.h"
> +#include "journal.h"
> +#include "sysfile.h"
> +#include "dlmglue.h"
> +#include "quota.h"
> +
> +/* Number of local quota structures per block */
> +static inline unsigned int ol_quota_entries_per_block(struct super_block *sb)
> +{
> + return ((sb->s_blocksize - OCFS2_QBLK_RESERVED_SPACE) /
> + sizeof(struct ocfs2_local_disk_dqblk));
> +}
> +
> +/* Number of blocks with entries in one chunk */
> +static inline unsigned int ol_chunk_blocks(struct super_block *sb)
> +{
> + return ((sb->s_blocksize - sizeof(struct ocfs2_local_disk_chunk) -
> + OCFS2_QBLK_RESERVED_SPACE) << 3) /
> + ol_quota_entries_per_block(sb);
> +}
> +
> +/* Number of entries in a chunk bitmap */
> +static unsigned int ol_chunk_entries(struct super_block *sb)
> +{
> + return ol_chunk_blocks(sb) * ol_quota_entries_per_block(sb);
> +}
> +
> +/* Offset of the chunk in quota file */
> +static unsigned int ol_quota_chunk_block(struct super_block *sb, int c)
> +{
> + /* 1 block for local quota file info, 1 block per chunk for chunk info */
> + return 1 + (ol_chunk_blocks(sb) + 1) * c;
> +}
> +
> +/* Offset of the dquot structure in the quota file */
> +static loff_t ol_dqblk_off(struct super_block *sb, int c, int off)
> +{
> + int epb = ol_quota_entries_per_block(sb);
> +
> + return ((ol_quota_chunk_block(sb, c) + 1 + off / epb)
> + << sb->s_blocksize_bits) +
> + (off % epb) * sizeof(struct ocfs2_local_disk_dqblk);
> +}
> +
> +/* Compute block number from given offset */
> +static inline unsigned int ol_dqblk_file_block(struct super_block *sb, loff_t off)
> +{
> + return off >> sb->s_blocksize_bits;
> +}
> +
> +static inline unsigned int ol_dqblk_block_offset(struct super_block *sb, loff_t off)
> +{
> + return off & ((1 << sb->s_blocksize_bits) - 1);
> +}
> +
> +/* Compute offset in the chunk of a structure with the given offset */
> +static int ol_dqblk_chunk_off(struct super_block *sb, int c, loff_t off)
> +{
> + int epb = ol_quota_entries_per_block(sb);
> +
> + return ((off >> sb->s_blocksize_bits) -
> + ol_quota_chunk_block(sb, c) - 1) * epb
> + + ((unsigned int)(off & ((1 << sb->s_blocksize_bits) - 1))) /
> + sizeof(struct ocfs2_local_disk_dqblk);
> +}
> +
> +/* Write bufferhead into the fs */
> +static int ocfs2_modify_bh(struct inode *inode, struct buffer_head *bh,
> + void (*modify)(struct buffer_head *, void *), void *private)
> +{
> + struct super_block *sb = inode->i_sb;
> + handle_t *handle;
> + int status;
> +
> + handle = ocfs2_start_trans(OCFS2_SB(sb), 1);
> + if (IS_ERR(handle)) {
> + status = PTR_ERR(handle);
> + mlog_errno(status);
> + return status;
> + }
> + status = ocfs2_journal_access(handle, inode, bh,
> + OCFS2_JOURNAL_ACCESS_WRITE);
> + if (status < 0) {
> + mlog_errno(status);
> + ocfs2_commit_trans(OCFS2_SB(sb), handle);
> + return status;
> + }
> + lock_buffer(bh);
> + modify(bh, private);
> + unlock_buffer(bh);
> + status = ocfs2_journal_dirty(handle, bh);
> + if (status < 0) {
> + mlog_errno(status);
> + ocfs2_commit_trans(OCFS2_SB(sb), handle);
> + return status;
> + }
> + status = ocfs2_commit_trans(OCFS2_SB(sb), handle);
> + if (status < 0) {
> + mlog_errno(status);
> + return status;
> + }
> + return 0;
> +}
> +
> +/* Check whether we understand format of quota files */
> +static int ocfs2_local_check_quota_file(struct super_block *sb, int type)
> +{
> + unsigned int lmagics[MAXQUOTAS] = OCFS2_LOCAL_QMAGICS;
> + unsigned int lversions[MAXQUOTAS] = OCFS2_LOCAL_QVERSIONS;
> + unsigned int gmagics[MAXQUOTAS] = OCFS2_GLOBAL_QMAGICS;
> + unsigned int gversions[MAXQUOTAS] = OCFS2_GLOBAL_QVERSIONS;
> + unsigned int ino[MAXQUOTAS] = { USER_QUOTA_SYSTEM_INODE,
> + GROUP_QUOTA_SYSTEM_INODE };
> + struct buffer_head *bh;
> + struct inode *linode = sb_dqopt(sb)->files[type];
> + struct inode *ginode = NULL;
> + struct ocfs2_disk_dqheader *dqhead;
> + int status, ret = 0;
> +
> + /* First check whether we understand local quota file */
> + bh = ocfs2_read_quota_block(linode, 0, &status);
> + if (!bh) {
> + mlog_errno(status);
> + mlog(ML_ERROR, "failed to read quota file header (type=%d)\n",
> + type);
> + goto out_err;
> + }
> + dqhead = (struct ocfs2_disk_dqheader *)(bh->b_data);
> + if (le32_to_cpu(dqhead->dqh_magic) != lmagics[type]) {
> + mlog(ML_ERROR, "quota file magic does not match (%u != %u),"
> + " type=%d\n", le32_to_cpu(dqhead->dqh_magic),
> + lmagics[type], type);
> + goto out_err;
> + }
> + if (le32_to_cpu(dqhead->dqh_version) != lversions[type]) {
> + mlog(ML_ERROR, "quota file version does not match (%u != %u),"
> + " type=%d\n", le32_to_cpu(dqhead->dqh_version),
> + lversions[type], type);
> + goto out_err;
> + }
> + brelse(bh);
> + bh = NULL;
> +
> + /* Next check whether we understand global quota file */
> + ginode = ocfs2_get_system_file_inode(OCFS2_SB(sb), ino[type],
> + OCFS2_INVALID_SLOT);
> + if (!ginode) {
> + mlog(ML_ERROR, "cannot get global quota file inode "
> + "(type=%d)\n", type);
> + goto out_err;
> + }
> + /* Since the header is read only, we don't care about locking */
> + bh = ocfs2_read_quota_block(ginode, 0, &status);
> + if (!bh) {
> + mlog_errno(status);
> + mlog(ML_ERROR, "failed to read global quota file header "
> + "(type=%d)\n", type);
> + goto out_err;
> + }
> + dqhead = (struct ocfs2_disk_dqheader *)(bh->b_data);
> + if (le32_to_cpu(dqhead->dqh_magic) != gmagics[type]) {
> + mlog(ML_ERROR, "global quota file magic does not match "
> + "(%u != %u), type=%d\n",
> + le32_to_cpu(dqhead->dqh_magic), gmagics[type], type);
> + goto out_err;
> + }
> + if (le32_to_cpu(dqhead->dqh_version) != gversions[type]) {
> + mlog(ML_ERROR, "global quota file version does not match "
> + "(%u != %u), type=%d\n",
> + le32_to_cpu(dqhead->dqh_version), gversions[type],
> + type);
> + goto out_err;
> + }
> +
> + ret = 1;
> +out_err:
> + brelse(bh);
> + iput(ginode);
> + return ret;
> +}
> +
> +/* Release given list of quota file chunks */
> +static void ocfs2_release_local_quota_bitmaps(struct list_head *head)
> +{
> + struct ocfs2_quota_chunk *pos, *next;
> +
> + list_for_each_entry_safe(pos, next, head, qc_chunk) {
> + list_del(&pos->qc_chunk);
> + brelse(pos->qc_headerbh);
> + kmem_cache_free(ocfs2_qf_chunk_cachep, pos);
> + }
> +}
> +
> +/* Load quota bitmaps into memory */
> +static int ocfs2_load_local_quota_bitmaps(struct inode *inode,
> + struct ocfs2_local_disk_dqinfo *ldinfo,
> + struct list_head *head)
> +{
> + struct ocfs2_quota_chunk *newchunk;
> + int i, status;
> +
> + INIT_LIST_HEAD(head);
> + for (i = 0; i < le32_to_cpu(ldinfo->dqi_chunks); i++) {
> + newchunk = kmem_cache_alloc(ocfs2_qf_chunk_cachep, GFP_NOFS);
> + if (!newchunk) {
> + ocfs2_release_local_quota_bitmaps(head);
> + return -ENOMEM;
> + }
> + newchunk->qc_num = i;
> + newchunk->qc_headerbh = ocfs2_read_quota_block(inode,
> + ol_quota_chunk_block(inode->i_sb, i),
> + &status);
> + if (!newchunk->qc_headerbh) {
> + mlog_errno(status);
> + kmem_cache_free(ocfs2_qf_chunk_cachep, newchunk);
> + ocfs2_release_local_quota_bitmaps(head);
> + return status;
> + }
> + list_add_tail(&newchunk->qc_chunk, head);
> + }
> + return 0;
> +}
> +
> +static void olq_update_info(struct buffer_head *bh, void *private)
> +{
> + struct mem_dqinfo *info = private;
> + struct ocfs2_mem_dqinfo *oinfo = info->dqi_priv;
> + struct ocfs2_local_disk_dqinfo *ldinfo;
> +
> + ldinfo = (struct ocfs2_local_disk_dqinfo *)(bh->b_data +
> + OCFS2_LOCAL_INFO_OFF);
> + spin_lock(&dq_data_lock);
> + ldinfo->dqi_flags = cpu_to_le32(info->dqi_flags & DQF_MASK);
> + ldinfo->dqi_chunks = cpu_to_le32(oinfo->dqi_chunks);
> + ldinfo->dqi_blocks = cpu_to_le32(oinfo->dqi_blocks);
> + spin_unlock(&dq_data_lock);
> +}
> +
> +/* Read information header from quota file */
> +static int ocfs2_local_read_info(struct super_block *sb, int type)
> +{
> + struct ocfs2_local_disk_dqinfo *ldinfo;
> + struct mem_dqinfo *info = sb_dqinfo(sb, type);
> + struct ocfs2_mem_dqinfo *oinfo;
> + struct inode *lqinode = sb_dqopt(sb)->files[type];
> + int status;
> + struct buffer_head *bh = NULL;
> + int locked = 0;
> +
> + info->dqi_maxblimit = 0x7fffffffffffffffLL;
> + info->dqi_maxilimit = 0x7fffffffffffffffLL;
> + oinfo = kmalloc(sizeof(struct ocfs2_mem_dqinfo), GFP_NOFS);
> + if (!oinfo) {
> + mlog(ML_ERROR, "failed to allocate memory for ocfs2 quota"
> + " info.");
> + goto out_err;
> + }
> + info->dqi_priv = oinfo;
> + oinfo->dqi_type = type;
> + INIT_LIST_HEAD(&oinfo->dqi_chunk);
> + oinfo->dqi_lqi_bh = NULL;
> + oinfo->dqi_ibh = NULL;
> +
> + status = ocfs2_global_read_info(sb, type);
> + if (status < 0)
> + goto out_err;
> +
> + status = ocfs2_inode_lock(lqinode, &oinfo->dqi_lqi_bh, 1);
> + if (status < 0) {
> + mlog_errno(status);
> + goto out_err;
> + }
> + locked = 1;
> +
> + /* Now read local header */
> + bh = ocfs2_read_quota_block(lqinode, 0, &status);
> + if (!bh) {
> + mlog_errno(status);
> + mlog(ML_ERROR, "failed to read quota file info header "
> + "(type=%d)\n", type);
> + goto out_err;
> + }
> + ldinfo = (struct ocfs2_local_disk_dqinfo *)(bh->b_data +
> + OCFS2_LOCAL_INFO_OFF);
> + info->dqi_flags = le32_to_cpu(ldinfo->dqi_flags);
> + oinfo->dqi_chunks = le32_to_cpu(ldinfo->dqi_chunks);
> + oinfo->dqi_blocks = le32_to_cpu(ldinfo->dqi_blocks);
> + oinfo->dqi_ibh = bh;
> +
> + /* We crashed when using local quota file? */
> + if (!(info->dqi_flags & OLQF_CLEAN))
> + goto out_err; /* So far we just bail out. Later we should resync here */
> +
> + status = ocfs2_load_local_quota_bitmaps(sb_dqopt(sb)->files[type],
> + ldinfo,
> + &oinfo->dqi_chunk);
> + if (status < 0) {
> + mlog_errno(status);
> + goto out_err;
> + }
> +
> + /* Now mark quota file as used */
> + info->dqi_flags &= ~OLQF_CLEAN;
> + status = ocfs2_modify_bh(lqinode, bh, olq_update_info, info);
> + if (status < 0) {
> + mlog_errno(status);
> + goto out_err;
> + }
> +
> + return 0;
> +out_err:
> + if (oinfo) {
> + iput(oinfo->dqi_gqinode);
> + ocfs2_simple_drop_lockres(OCFS2_SB(sb), &oinfo->dqi_gqlock);
> + ocfs2_lock_res_free(&oinfo->dqi_gqlock);
> + brelse(oinfo->dqi_lqi_bh);
> + if (locked)
> + ocfs2_inode_unlock(lqinode, 1);
> + ocfs2_release_local_quota_bitmaps(&oinfo->dqi_chunk);
> + kfree(oinfo);
> + }
> + brelse(bh);
> + return -1;
> +}
> +
> +/* Write local info to quota file */
> +static int ocfs2_local_write_info(struct super_block *sb, int type)
> +{
> + struct mem_dqinfo *info = sb_dqinfo(sb, type);
> + struct buffer_head *bh = ((struct ocfs2_mem_dqinfo *)info->dqi_priv)
> + ->dqi_ibh;
> + int status;
> +
> + status = ocfs2_modify_bh(sb_dqopt(sb)->files[type], bh, olq_update_info,
> + info);
> + if (status < 0) {
> + mlog_errno(status);
> + return -1;
> + }
> +
> + return 0;
> +}
> +
> +/* Release info from memory */
> +static int ocfs2_local_free_info(struct super_block *sb, int type)
> +{
> + struct mem_dqinfo *info = sb_dqinfo(sb, type);
> + struct ocfs2_mem_dqinfo *oinfo = info->dqi_priv;
> + struct ocfs2_quota_chunk *chunk;
> + struct ocfs2_local_disk_chunk *dchunk;
> + int mark_clean = 1, len;
> + int status;
> +
> + iput(oinfo->dqi_gqinode);
> + ocfs2_simple_drop_lockres(OCFS2_SB(sb), &oinfo->dqi_gqlock);
> + ocfs2_lock_res_free(&oinfo->dqi_gqlock);
> + list_for_each_entry(chunk, &oinfo->dqi_chunk, qc_chunk) {
> + dchunk = (struct ocfs2_local_disk_chunk *)
> + (chunk->qc_headerbh->b_data);
> + if (chunk->qc_num < oinfo->dqi_chunks - 1) {
> + len = ol_chunk_entries(sb);
> + } else {
> + len = (oinfo->dqi_blocks -
> + ol_quota_chunk_block(sb, chunk->qc_num) - 1)
> + * ol_quota_entries_per_block(sb);
> + }
> + /* Not all entries free? Bug! */
> + if (le32_to_cpu(dchunk->dqc_free) != len) {
> + mlog(ML_ERROR, "releasing quota file with used "
> + "entries (type=%d)\n", type);
> + mark_clean = 0;
> + }
> + }
> + ocfs2_release_local_quota_bitmaps(&oinfo->dqi_chunk);
> +
> + if (!mark_clean)
> + goto out;
> +
> + /* Mark local file as clean */
> + info->dqi_flags |= OLQF_CLEAN;
> + status = ocfs2_modify_bh(sb_dqopt(sb)->files[type],
> + oinfo->dqi_ibh,
> + olq_update_info,
> + info);
> + if (status < 0) {
> + mlog_errno(status);
> + goto out;
> + }
> +
> +out:
> + ocfs2_inode_unlock(sb_dqopt(sb)->files[type], 1);
> + brelse(oinfo->dqi_ibh);
> + brelse(oinfo->dqi_lqi_bh);
> + kfree(oinfo);
> + return 0;
> +}
> +
> +static void olq_set_dquot(struct buffer_head *bh, void *private)
> +{
> + struct ocfs2_dquot *od = private;
> + struct ocfs2_local_disk_dqblk *dqblk;
> + struct super_block *sb = od->dq_dquot.dq_sb;
> +
> + dqblk = (struct ocfs2_local_disk_dqblk *)(bh->b_data
> + + ol_dqblk_block_offset(sb, od->dq_local_off));
> +
> + dqblk->dqb_id = cpu_to_le64(od->dq_dquot.dq_id);
> + spin_lock(&dq_data_lock);
> + dqblk->dqb_spacemod = cpu_to_le64(od->dq_dquot.dq_dqb.dqb_curspace -
> + od->dq_origspace);
> + dqblk->dqb_inodemod = cpu_to_le64(od->dq_dquot.dq_dqb.dqb_curinodes -
> + od->dq_originodes);
> + spin_unlock(&dq_data_lock);
> + mlog(0, "Writing local dquot %u space %lld inodes %lld\n",
> + od->dq_dquot.dq_id, dqblk->dqb_spacemod, dqblk->dqb_inodemod);
> +}
> +
> +/* Write dquot to local quota file */
> +static int ocfs2_local_write_dquot(struct dquot *dquot)
> +{
> + struct super_block *sb = dquot->dq_sb;
> + struct ocfs2_dquot *od = OCFS2_DQUOT(dquot);
> + struct buffer_head *bh;
> + int status;
> +
> + bh = ocfs2_read_quota_block(sb_dqopt(sb)->files[dquot->dq_type],
> + ol_dqblk_file_block(sb, od->dq_local_off),
> + &status);
> + if (!bh) {
> + mlog_errno(status);
> + goto out;
> + }
> + status = ocfs2_modify_bh(sb_dqopt(sb)->files[dquot->dq_type], bh,
> + olq_set_dquot, od);
> + if (status < 0) {
> + mlog_errno(status);
> + goto out;
> + }
> +out:
> + brelse(bh);
> + return status;
> +}
> +
> +/* Find free entry in local quota file */
> +static struct ocfs2_quota_chunk *ocfs2_find_free_entry(struct super_block *sb,
> + int type,
> + int *offset)
> +{
> + struct mem_dqinfo *info = sb_dqinfo(sb, type);
> + struct ocfs2_mem_dqinfo *oinfo = info->dqi_priv;
> + struct ocfs2_quota_chunk *chunk;
> + struct ocfs2_local_disk_chunk *dchunk;
> + int found = 0, len;
> +
> + list_for_each_entry(chunk, &oinfo->dqi_chunk, qc_chunk) {
> + dchunk = (struct ocfs2_local_disk_chunk *)
> + chunk->qc_headerbh->b_data;
> + if (le32_to_cpu(dchunk->dqc_free) > 0) {
> + found = 1;
> + break;
> + }
> + }
> + if (!found)
> + return NULL;
> +
> + if (chunk->qc_num < oinfo->dqi_chunks - 1) {
> + len = ol_chunk_entries(sb);
> + } else {
> + len = (oinfo->dqi_blocks -
> + ol_quota_chunk_block(sb, chunk->qc_num) - 1)
> + * ol_quota_entries_per_block(sb);
> + }
> +
> + found = ocfs2_find_next_zero_bit(dchunk->dqc_bitmap, len, 0);
> + /* We failed? */
> + if (found == len) {
> + mlog(ML_ERROR, "Did not find empty entry in chunk %d with %u"
> + " entries free (type=%d)\n", chunk->qc_num,
> + le32_to_cpu(dchunk->dqc_free), type);
> + return ERR_PTR(-EIO);
> + }
> + *offset = found;
> + return chunk;
> +}
> +
> +/* Add new chunk to the local quota file */
> +static struct ocfs2_quota_chunk *ocfs2_local_quota_add_chunk(
> + struct super_block *sb,
> + int type,
> + int *offset)
> +{
> + struct mem_dqinfo *info = sb_dqinfo(sb, type);
> + struct ocfs2_mem_dqinfo *oinfo = info->dqi_priv;
> + struct inode *lqinode = sb_dqopt(sb)->files[type];
> + struct ocfs2_quota_chunk *chunk = NULL;
> + struct ocfs2_local_disk_chunk *dchunk;
> + int status;
> + handle_t *handle;
> + struct buffer_head *bh = NULL;
> + u64 p_blkno;
> +
> + /* We are protected by dqio_sem so no locking needed */
> + status = ocfs2_extend_no_holes(lqinode,
> + lqinode->i_size + 2 * sb->s_blocksize,
> + lqinode->i_size);
> + if (status < 0) {
> + mlog_errno(status);
> + goto out;
> + }
> + status = ocfs2_simple_size_update(lqinode, oinfo->dqi_lqi_bh,
> + lqinode->i_size + 2 * sb->s_blocksize);
> + if (status < 0) {
> + mlog_errno(status);
> + goto out;
> + }
> +
> + chunk = kmem_cache_alloc(ocfs2_qf_chunk_cachep, GFP_NOFS);
> + if (!chunk) {
> + status = -ENOMEM;
> + mlog_errno(status);
> + goto out;
> + }
> +
> + down_read(&OCFS2_I(lqinode)->ip_alloc_sem);
> + status = ocfs2_extent_map_get_blocks(lqinode, oinfo->dqi_blocks,
> + &p_blkno, NULL, NULL);
> + up_read(&OCFS2_I(lqinode)->ip_alloc_sem);
> + if (status < 0) {
> + mlog_errno(status);
> + goto out;
> + }
> + bh = sb_getblk(sb, p_blkno);
> + if (!bh) {
> + status = -ENOMEM;
> + mlog_errno(status);
> + goto out;
> + }
> + dchunk = (struct ocfs2_local_disk_chunk *)bh->b_data;
> +
> + handle = ocfs2_start_trans(OCFS2_SB(sb), 2);
> + if (IS_ERR(handle)) {
> + status = PTR_ERR(handle);
> + mlog_errno(status);
> + goto out;
> + }
> +
> + status = ocfs2_journal_access(handle, lqinode, bh,
> + OCFS2_JOURNAL_ACCESS_WRITE);
> + if (status < 0) {
> + mlog_errno(status);
> + goto out_trans;
> + }
> + lock_buffer(bh);
> + dchunk->dqc_free = ol_quota_entries_per_block(sb);
> + memset(dchunk->dqc_bitmap, 0,
> + sb->s_blocksize - sizeof(struct ocfs2_local_disk_chunk) -
> + OCFS2_QBLK_RESERVED_SPACE);
> + set_buffer_uptodate(bh);
> + unlock_buffer(bh);
> + status = ocfs2_journal_dirty(handle, bh);
> + if (status < 0) {
> + mlog_errno(status);
> + goto out_trans;
> + }
> +
> + oinfo->dqi_blocks += 2;
> + oinfo->dqi_chunks++;
> + status = ocfs2_local_write_info(sb, type);
> + if (status < 0) {
> + mlog_errno(status);
> + goto out_trans;
> + }
> + status = ocfs2_commit_trans(OCFS2_SB(sb), handle);
> + if (status < 0) {
> + mlog_errno(status);
> + goto out;
> + }
> +
> + list_add_tail(&chunk->qc_chunk, &oinfo->dqi_chunk);
> + chunk->qc_num = list_entry(chunk->qc_chunk.prev,
> + struct ocfs2_quota_chunk,
> + qc_chunk)->qc_num + 1;
> + chunk->qc_headerbh = bh;
> + *offset = 0;
> + return chunk;
> +out_trans:
> + ocfs2_commit_trans(OCFS2_SB(sb), handle);
> +out:
> + brelse(bh);
> + kmem_cache_free(ocfs2_qf_chunk_cachep, chunk);
> + return ERR_PTR(status);
> +}
> +
> +/* Find free entry in local quota file */
> +static struct ocfs2_quota_chunk *ocfs2_extend_local_quota_file(
> + struct super_block *sb,
> + int type,
> + int *offset)
> +{
> + struct mem_dqinfo *info = sb_dqinfo(sb, type);
> + struct ocfs2_mem_dqinfo *oinfo = info->dqi_priv;
> + struct ocfs2_quota_chunk *chunk;
> + struct inode *lqinode = sb_dqopt(sb)->files[type];
> + struct ocfs2_local_disk_chunk *dchunk;
> + int epb = ol_quota_entries_per_block(sb);
> + unsigned int chunk_blocks;
> + int status;
> + handle_t *handle;
> +
> + if (list_empty(&oinfo->dqi_chunk))
> + return ocfs2_local_quota_add_chunk(sb, type, offset);
> + /* Is the last chunk full? */
> + chunk = list_entry(oinfo->dqi_chunk.prev,
> + struct ocfs2_quota_chunk, qc_chunk);
> + chunk_blocks = oinfo->dqi_blocks -
> + ol_quota_chunk_block(sb, chunk->qc_num) - 1;
> + if (ol_chunk_blocks(sb) == chunk_blocks)
> + return ocfs2_local_quota_add_chunk(sb, type, offset);
> +
> + /* We are protected by dqio_sem so no locking needed */
> + status = ocfs2_extend_no_holes(lqinode,
> + lqinode->i_size + sb->s_blocksize,
> + lqinode->i_size);
> + if (status < 0) {
> + mlog_errno(status);
> + goto out;
> + }
> + status = ocfs2_simple_size_update(lqinode, oinfo->dqi_lqi_bh,
> + lqinode->i_size + sb->s_blocksize);
> + if (status < 0) {
> + mlog_errno(status);
> + goto out;
> + }
> + handle = ocfs2_start_trans(OCFS2_SB(sb), 2);
> + if (IS_ERR(handle)) {
> + status = PTR_ERR(handle);
> + mlog_errno(status);
> + goto out;
> + }
> + status = ocfs2_journal_access(handle, lqinode, chunk->qc_headerbh,
> + OCFS2_JOURNAL_ACCESS_WRITE);
> + if (status < 0) {
> + mlog_errno(status);
> + goto out_trans;
> + }
> +
> + dchunk = (struct ocfs2_local_disk_chunk *)chunk->qc_headerbh->b_data;
> + lock_buffer(chunk->qc_headerbh);
> + le32_add_cpu(&dchunk->dqc_free, ol_quota_entries_per_block(sb));
> + unlock_buffer(chunk->qc_headerbh);
> + status = ocfs2_journal_dirty(handle, chunk->qc_headerbh);
> + if (status < 0) {
> + mlog_errno(status);
> + goto out_trans;
> + }
> + oinfo->dqi_blocks++;
> + status = ocfs2_local_write_info(sb, type);
> + if (status < 0) {
> + mlog_errno(status);
> + goto out_trans;
> + }
> +
> + status = ocfs2_commit_trans(OCFS2_SB(sb), handle);
> + if (status < 0) {
> + mlog_errno(status);
> + goto out;
> + }
> + *offset = chunk_blocks * epb;
> + return chunk;
> +out_trans:
> + ocfs2_commit_trans(OCFS2_SB(sb), handle);
> +out:
> + return ERR_PTR(status);
> +}
> +
> +void olq_alloc_dquot(struct buffer_head *bh, void *private)
> +{
> + int *offset = private;
> + struct ocfs2_local_disk_chunk *dchunk;
> +
> + dchunk = (struct ocfs2_local_disk_chunk *)bh->b_data;
> + ocfs2_set_bit(*offset, dchunk->dqc_bitmap);
> + le32_add_cpu(&dchunk->dqc_free, -1);
> +}
> +
> +/* Create dquot in the local file for given id */
> +static int ocfs2_create_local_dquot(struct dquot *dquot)
> +{
> + struct super_block *sb = dquot->dq_sb;
> + int type = dquot->dq_type;
> + struct inode *lqinode = sb_dqopt(sb)->files[type];
> + struct ocfs2_quota_chunk *chunk;
> + struct ocfs2_dquot *od = OCFS2_DQUOT(dquot);
> + int offset;
> + int status;
> +
> + chunk = ocfs2_find_free_entry(sb, type, &offset);
> + if (!chunk) {
> + chunk = ocfs2_extend_local_quota_file(sb, type, &offset);
> + if (IS_ERR(chunk))
> + return PTR_ERR(chunk);
> + } else if (IS_ERR(chunk)) {
> + return PTR_ERR(chunk);
> + }
> + od->dq_local_off = ol_dqblk_off(sb, chunk->qc_num, offset);
> + od->dq_chunk = chunk;
> +
> + /* Initialize dquot structure on disk */
> + status = ocfs2_local_write_dquot(dquot);
> + if (status < 0) {
> + mlog_errno(status);
> + goto out;
> + }
> +
> + /* Mark structure as allocated */
> + status = ocfs2_modify_bh(lqinode, chunk->qc_headerbh, olq_alloc_dquot,
> + &offset);
> + if (status < 0) {
> + mlog_errno(status);
> + goto out;
> + }
> +out:
> + return status;
> +}
> +
> +/* Create entry in local file for dquot, load data from the global file */
> +static int ocfs2_local_read_dquot(struct dquot *dquot)
> +{
> + int status;
> +
> + mlog_entry("id=%u, type=%d\n", dquot->dq_id, dquot->dq_type);
> +
> + status = ocfs2_global_read_dquot(dquot);
> + if (status < 0) {
> + mlog_errno(status);
> + goto out_err;
> + }
> +
> + /* Now create entry in the local quota file */
> + status = ocfs2_create_local_dquot(dquot);
> + if (status < 0) {
> + mlog_errno(status);
> + goto out_err;
> + }
> + mlog_exit(0);
> + return 0;
> +out_err:
> + mlog_exit(status);
> + return status;
> +}
> +
> +/* Release dquot structure from local quota file. ocfs2_release_dquot() has
> + * already started a transaction and obtained exclusive lock for global
> + * quota file. */
> +static int ocfs2_local_release_dquot(struct dquot *dquot)
> +{
> + int status;
> + int type = dquot->dq_type;
> + struct ocfs2_dquot *od = OCFS2_DQUOT(dquot);
> + struct super_block *sb = dquot->dq_sb;
> + struct ocfs2_local_disk_chunk *dchunk;
> + int offset;
> + handle_t *handle = journal_current_handle();
> +
> + BUG_ON(!handle);
> + /* First write all local changes to global file */
> + status = ocfs2_global_release_dquot(dquot);
> + if (status < 0) {
> + mlog_errno(status);
> + goto out;
> + }
> +
> + status = ocfs2_journal_access(handle, sb_dqopt(sb)->files[type],
> + od->dq_chunk->qc_headerbh, OCFS2_JOURNAL_ACCESS_WRITE);
> + if (status < 0) {
> + mlog_errno(status);
> + goto out;
> + }
> + offset = ol_dqblk_chunk_off(sb, od->dq_chunk->qc_num,
> + od->dq_local_off);
> + dchunk = (struct ocfs2_local_disk_chunk *)
> + (od->dq_chunk->qc_headerbh->b_data);
> + /* Mark structure as freed */
> + lock_buffer(od->dq_chunk->qc_headerbh);
> + ocfs2_clear_bit(offset, dchunk->dqc_bitmap);
> + le32_add_cpu(&dchunk->dqc_free, 1);
> + unlock_buffer(od->dq_chunk->qc_headerbh);
> + status = ocfs2_journal_dirty(handle, od->dq_chunk->qc_headerbh);
> + if (status < 0) {
> + mlog_errno(status);
> + goto out;
> + }
> + status = 0;
> +out:
> + /* Clear the read bit so that next time someone uses this
> + * dquot he reads fresh info from disk and allocates local
> + * dquot structure */
> + clear_bit(DQ_READ_B, &dquot->dq_flags);
> + return status;
> +}
> +
> +static struct quota_format_ops ocfs2_format_ops = {
> + .check_quota_file = ocfs2_local_check_quota_file,
> + .read_file_info = ocfs2_local_read_info,
> + .write_file_info = ocfs2_global_write_info,
> + .free_file_info = ocfs2_local_free_info,
> + .read_dqblk = ocfs2_local_read_dquot,
> + .commit_dqblk = ocfs2_local_write_dquot,
> + .release_dqblk = ocfs2_local_release_dquot,
> +};
> +
> +struct quota_format_type ocfs2_quota_format = {
> + .qf_fmt_id = QFMT_OCFS2,
> + .qf_ops = &ocfs2_format_ops,
> + .qf_owner = THIS_MODULE
> +};
> diff --git a/fs/ocfs2/super.c b/fs/ocfs2/super.c
> index 4712bc7..92539fe 100644
> --- a/fs/ocfs2/super.c
> +++ b/fs/ocfs2/super.c
> @@ -65,10 +65,13 @@
> #include "uptodate.h"
> #include "ver.h"
> #include "xattr.h"
> +#include "quota.h"
>
> #include "buffer_head_io.h"
>
> static struct kmem_cache *ocfs2_inode_cachep = NULL;
> +struct kmem_cache *ocfs2_dquot_cachep;
> +struct kmem_cache *ocfs2_qf_chunk_cachep;
>
> /* OCFS2 needs to schedule several differnt types of work which
> * require cluster locking, disk I/O, recovery waits, etc. Since these
> @@ -137,6 +140,8 @@ static const struct super_operations ocfs2_sops = {
> .put_super = ocfs2_put_super,
> .remount_fs = ocfs2_remount,
> .show_options = ocfs2_show_options,
> + .quota_read = ocfs2_quota_read,
> + .quota_write = ocfs2_quota_write,
> };
>
> enum {
> @@ -1071,6 +1076,7 @@ static int __init ocfs2_init(void)
>
> ocfs2_set_locking_protocol();
>
> + status = register_quota_format(&ocfs2_quota_format);
> leave:
> if (status < 0) {
> ocfs2_free_mem_caches();
> @@ -1094,6 +1100,8 @@ static void __exit ocfs2_exit(void)
> destroy_workqueue(ocfs2_wq);
> }
>
> + unregister_quota_format(&ocfs2_quota_format);
> +
> debugfs_remove(ocfs2_debugfs_root);
>
> ocfs2_free_mem_caches();
> @@ -1209,8 +1217,27 @@ static int ocfs2_initialize_mem_caches(void)
> (SLAB_HWCACHE_ALIGN|SLAB_RECLAIM_ACCOUNT|
> SLAB_MEM_SPREAD),
> ocfs2_inode_init_once);
> - if (!ocfs2_inode_cachep)
> + ocfs2_dquot_cachep = kmem_cache_create("ocfs2_dquot_cache",
> + sizeof(struct ocfs2_dquot),
> + 0,
> + (SLAB_HWCACHE_ALIGN|SLAB_RECLAIM_ACCOUNT|
> + SLAB_MEM_SPREAD),
> + NULL);
> + ocfs2_qf_chunk_cachep = kmem_cache_create("ocfs2_qf_chunk_cache",
> + sizeof(struct ocfs2_quota_chunk),
> + 0,
> + (SLAB_RECLAIM_ACCOUNT|SLAB_MEM_SPREAD),
> + NULL);
> + if (!ocfs2_inode_cachep || !ocfs2_dquot_cachep ||
> + !ocfs2_qf_chunk_cachep) {
> + if (ocfs2_inode_cachep)
> + kmem_cache_destroy(ocfs2_inode_cachep);
> + if (ocfs2_dquot_cachep)
> + kmem_cache_destroy(ocfs2_dquot_cachep);
> + if (ocfs2_qf_chunk_cachep)
> + kmem_cache_destroy(ocfs2_qf_chunk_cachep);
> return -ENOMEM;
> + }
>
> return 0;
> }
> @@ -1219,8 +1246,15 @@ static void ocfs2_free_mem_caches(void)
> {
> if (ocfs2_inode_cachep)
> kmem_cache_destroy(ocfs2_inode_cachep);
> -
> ocfs2_inode_cachep = NULL;
> +
> + if (ocfs2_dquot_cachep)
> + kmem_cache_destroy(ocfs2_dquot_cachep);
> + ocfs2_dquot_cachep = NULL;
> +
> + if (ocfs2_qf_chunk_cachep)
> + kmem_cache_destroy(ocfs2_qf_chunk_cachep);
> + ocfs2_qf_chunk_cachep = NULL;
> }
>
> static int ocfs2_get_sector(struct super_block *sb,
> --
> 1.5.2.4
>
>
> _______________________________________________
> Ocfs2-devel mailing list
> Ocfs2-devel at oss.oracle.com
> http://oss.oracle.com/mailman/listinfo/ocfs2-devel
--
"Born under a bad sign.
I been down since I began to crawl.
If it wasn't for bad luck,
I wouldn't have no luck at all."
Joel Becker
Principal Software Developer
Oracle
E-mail: joel.becker at oracle.com
Phone: (650) 506-8127
More information about the Ocfs2-devel
mailing list