// SPDX-License-Identifier: GPL-2.0 /* * Implementation of operations over global quota file */ #include <linux/spinlock.h> #include <linux/fs.h> #include <linux/slab.h> #include <linux/quota.h> #include <linux/quotaops.h> #include <linux/dqblk_qtree.h> #include <linux/jiffies.h> #include <linux/writeback.h> #include <linux/workqueue.h> #include <linux/llist.h> #include <linux/iversion.h> #include <cluster/masklog.h> #include "ocfs2_fs.h" #include "ocfs2.h" #include "alloc.h" #include "blockcheck.h" #include "inode.h" #include "journal.h" #include "file.h" #include "sysfile.h" #include "dlmglue.h" #include "uptodate.h" #include "super.h" #include "buffer_head_io.h" #include "quota.h" #include "ocfs2_trace.h" /* * Locking of quotas with OCFS2 is rather complex. Here are rules that * should be obeyed by all the functions: * - any write of quota structure (either to local or global file) is protected * by dqio_sem or dquot->dq_lock. * - any modification of global quota file holds inode cluster lock, i_rwsem, * and ip_alloc_sem of the global quota file (achieved by * ocfs2_lock_global_qf). It also has to hold qinfo_lock. * - an allocation of new blocks for local quota file is protected by * its ip_alloc_sem * * A rough sketch of locking dependencies (lf = local file, gf = global file): * Normal filesystem operation: * start_trans -> dqio_sem -> write to lf * Syncing of local and global file: * ocfs2_lock_global_qf -> start_trans -> dqio_sem -> qinfo_lock -> * write to gf * -> write to lf * Acquire dquot for the first time: * dq_lock -> ocfs2_lock_global_qf -> qinfo_lock -> read from gf * -> alloc space for gf * -> start_trans -> qinfo_lock -> write to gf * -> ip_alloc_sem of lf -> alloc space for lf * -> write to lf * Release last reference to dquot: * dq_lock -> ocfs2_lock_global_qf -> start_trans -> qinfo_lock -> write to gf * -> write to lf * Note that all the above operations also hold the inode cluster lock of lf. * Recovery: * inode cluster lock of recovered lf * -> read bitmaps -> ip_alloc_sem of lf * -> ocfs2_lock_global_qf -> start_trans -> dqio_sem -> qinfo_lock -> * write to gf */ static void qsync_work_fn(struct work_struct *work); static void ocfs2_global_disk2memdqb(struct dquot *dquot, void *dp) { … } static void ocfs2_global_mem2diskdqb(void *dp, struct dquot *dquot) { … } static int ocfs2_global_is_id(void *dp, struct dquot *dquot) { … } const struct qtree_fmt_operations ocfs2_global_ops = …; int ocfs2_validate_quota_block(struct super_block *sb, struct buffer_head *bh) { … } int ocfs2_read_quota_phys_block(struct inode *inode, u64 p_block, struct buffer_head **bhp) { … } /* 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) { … } /* 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) { … } int ocfs2_lock_global_qf(struct ocfs2_mem_dqinfo *oinfo, int ex) { … } void ocfs2_unlock_global_qf(struct ocfs2_mem_dqinfo *oinfo, int ex) { … } /* Read information header from global quota file */ int ocfs2_global_read_info(struct super_block *sb, int type) { … } /* Write information to global quota file. Expects exclusive lock on quota * file inode and quota info */ static int __ocfs2_global_write_info(struct super_block *sb, int type) { … } int ocfs2_global_write_info(struct super_block *sb, int type) { … } static int ocfs2_global_qinit_alloc(struct super_block *sb, int type) { … } static int ocfs2_calc_global_qinit_credits(struct super_block *sb, int type) { … } /* 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) { … } /* * Functions for periodic syncing of dquots with global file */ static int ocfs2_sync_dquot_helper(struct dquot *dquot, unsigned long type) { … } static void qsync_work_fn(struct work_struct *work) { … } /* * Wrappers for generic quota functions */ static int ocfs2_write_dquot(struct dquot *dquot) { … } static int ocfs2_calc_qdel_credits(struct super_block *sb, int type) { … } void ocfs2_drop_dquot_refs(struct work_struct *work) { … } /* * Called when the last reference to dquot is dropped. If we are called from * downconvert thread, we cannot do all the handling here because grabbing * quota lock could deadlock (the node holding the quota lock could need some * other cluster lock to proceed but with blocked downconvert thread we cannot * release any lock). */ static int ocfs2_release_dquot(struct dquot *dquot) { … } /* * Read global dquot structure from disk or create it if it does * not exist. Also update use count of the global structure and * create structure in node-local quota file. */ static int ocfs2_acquire_dquot(struct dquot *dquot) { … } static int ocfs2_get_next_id(struct super_block *sb, struct kqid *qid) { … } static int ocfs2_mark_dquot_dirty(struct dquot *dquot) { … } /* This should happen only after set_dqinfo(). */ static int ocfs2_write_info(struct super_block *sb, int type) { … } static struct dquot *ocfs2_alloc_dquot(struct super_block *sb, int type) { … } static void ocfs2_destroy_dquot(struct dquot *dquot) { … } const struct dquot_operations ocfs2_quota_operations = …;