* New SMP locking.
* Jan Kara, <jack@suse.cz>, 10/2002
*
- * Added journalled quota support
+ * Added journalled quota support, fix lock inversion problems
* Jan Kara, <jack@suse.cz>, 2003,2004
*
* (C) Copyright 1994 - 1997 Marco van Wieringen
#include <linux/proc_fs.h>
#include <linux/security.h>
#include <linux/kmod.h>
+#include <linux/namei.h>
+#include <linux/buffer_head.h>
+#include <linux/capability.h>
+#include <linux/quotaops.h>
#include <asm/uaccess.h>
*
* Any operation working on dquots via inode pointers must hold dqptr_sem. If
* operation is just reading pointers from inode (or not using them at all) the
- * read lock is enough. If pointers are altered function must hold write lock.
- * If operation is holding reference to dquot in other way (e.g. quotactl ops)
- * it must be guarded by dqonoff_sem.
+ * read lock is enough. If pointers are altered function must hold write lock
+ * (these locking rules also apply for S_NOQUOTA flag in the inode - note that
+ * for altering the flag i_mutex is also needed). If operation is holding
+ * reference to dquot in other way (e.g. quotactl ops) it must be guarded by
+ * dqonoff_mutex.
* This locking assures that:
* a) update/access to dquot pointers in inode is serialized
* b) everyone is guarded against invalidate_dquots()
*
- * Each dquot has its dq_lock semaphore. Locked dquots might not be referenced
+ * Each dquot has its dq_lock mutex. Locked dquots might not be referenced
* from inodes (dquot_alloc_space() and such don't check the dq_lock).
* Currently dquot is locked only when it is being read to memory (or space for
* it is being allocated) on the first dqget() and when it is being released on
* operations on dquots don't hold dq_lock as they copy data under dq_data_lock
* spinlock to internal buffers before writing.
*
- * Lock ordering (including journal_lock) is following:
- * dqonoff_sem > journal_lock > dqptr_sem > dquot->dq_lock > dqio_sem
+ * Lock ordering (including related VFS locks) is the following:
+ * i_mutex > dqonoff_sem > journal_lock > dqptr_sem > dquot->dq_lock >
+ * dqio_mutex
+ * i_mutex on quota files is special (it's below dqio_mutex)
*/
-spinlock_t dq_list_lock = SPIN_LOCK_UNLOCKED;
-spinlock_t dq_data_lock = SPIN_LOCK_UNLOCKED;
+static DEFINE_SPINLOCK(dq_list_lock);
+DEFINE_SPINLOCK(dq_data_lock);
static char *quotatypes[] = INITQFNAMES;
static struct quota_format_type *quota_formats; /* List of registered formats */
static struct quota_module_name module_names[] = INIT_QUOTA_MODULE_NAMES;
+/* SLAB cache for dquot structures */
+static kmem_cache_t *dquot_cachep;
+
int register_quota_format(struct quota_format_type *fmt)
{
spin_lock(&dq_list_lock);
* on all three lists, depending on its current state.
*
* All dquots are placed to the end of inuse_list when first created, and this
- * list is used for the sync and invalidate operations, which must look
- * at every dquot.
+ * list is used for invalidate operation, which must look at every dquot.
*
* Unused dquots (dq_count == 0) are added to the free_dquots list when freed,
* and this list is searched whenever we need an available dquot. Dquots are
static LIST_HEAD(inuse_list);
static LIST_HEAD(free_dquots);
-unsigned int dq_hash_bits, dq_hash_mask;
+static unsigned int dq_hash_bits, dq_hash_mask;
static struct hlist_head *dquot_hash;
struct dqstats dqstats;
static void dqput(struct dquot *dquot);
-static inline int const hashfn(struct super_block *sb, unsigned int id, int type)
+static inline unsigned int
+hashfn(const struct super_block *sb, unsigned int id, int type)
{
- unsigned long tmp = (((unsigned long)sb>>L1_CACHE_SHIFT) ^ id) * (MAXQUOTAS - type);
+ unsigned long tmp;
+
+ tmp = (((unsigned long)sb>>L1_CACHE_SHIFT) ^ id) * (MAXQUOTAS - type);
return (tmp + (tmp >> dq_hash_bits)) & dq_hash_mask;
}
/* Add a dquot to the tail of the free list */
static inline void put_dquot_last(struct dquot *dquot)
{
- list_add(&dquot->dq_free, free_dquots.prev);
+ list_add_tail(&dquot->dq_free, &free_dquots);
dqstats.free_dquots++;
}
{
/* We add to the back of inuse list so we don't have to restart
* when traversing this list and we block */
- list_add(&dquot->dq_inuse, inuse_list.prev);
+ list_add_tail(&dquot->dq_inuse, &inuse_list);
dqstats.allocated_dquots++;
}
static void wait_on_dquot(struct dquot *dquot)
{
- down(&dquot->dq_lock);
- up(&dquot->dq_lock);
+ mutex_lock(&dquot->dq_lock);
+ mutex_unlock(&dquot->dq_lock);
}
#define mark_dquot_dirty(dquot) ((dquot)->dq_sb->dq_op->mark_dirty(dquot))
int dquot_acquire(struct dquot *dquot)
{
- int ret = 0;
+ int ret = 0, ret2 = 0;
struct quota_info *dqopt = sb_dqopt(dquot->dq_sb);
- down(&dquot->dq_lock);
- down(&dqopt->dqio_sem);
+ mutex_lock(&dquot->dq_lock);
+ mutex_lock(&dqopt->dqio_mutex);
if (!test_bit(DQ_READ_B, &dquot->dq_flags))
ret = dqopt->ops[dquot->dq_type]->read_dqblk(dquot);
if (ret < 0)
/* Instantiate dquot if needed */
if (!test_bit(DQ_ACTIVE_B, &dquot->dq_flags) && !dquot->dq_off) {
ret = dqopt->ops[dquot->dq_type]->commit_dqblk(dquot);
+ /* Write the info if needed */
+ if (info_dirty(&dqopt->info[dquot->dq_type]))
+ ret2 = dqopt->ops[dquot->dq_type]->write_file_info(dquot->dq_sb, dquot->dq_type);
if (ret < 0)
goto out_iolock;
+ if (ret2 < 0) {
+ ret = ret2;
+ goto out_iolock;
+ }
}
set_bit(DQ_ACTIVE_B, &dquot->dq_flags);
out_iolock:
- up(&dqopt->dqio_sem);
- up(&dquot->dq_lock);
+ mutex_unlock(&dqopt->dqio_mutex);
+ mutex_unlock(&dquot->dq_lock);
return ret;
}
*/
int dquot_commit(struct dquot *dquot)
{
- int ret = 0;
+ int ret = 0, ret2 = 0;
struct quota_info *dqopt = sb_dqopt(dquot->dq_sb);
- down(&dqopt->dqio_sem);
+ mutex_lock(&dqopt->dqio_mutex);
spin_lock(&dq_list_lock);
if (!clear_dquot_dirty(dquot)) {
spin_unlock(&dq_list_lock);
spin_unlock(&dq_list_lock);
/* Inactive dquot can be only if there was error during read/init
* => we have better not writing it */
- if (test_bit(DQ_ACTIVE_B, &dquot->dq_flags))
+ if (test_bit(DQ_ACTIVE_B, &dquot->dq_flags)) {
ret = dqopt->ops[dquot->dq_type]->commit_dqblk(dquot);
+ if (info_dirty(&dqopt->info[dquot->dq_type]))
+ ret2 = dqopt->ops[dquot->dq_type]->write_file_info(dquot->dq_sb, dquot->dq_type);
+ if (ret >= 0)
+ ret = ret2;
+ }
out_sem:
- up(&dqopt->dqio_sem);
- if (info_dirty(&dqopt->info[dquot->dq_type]))
- dquot->dq_sb->dq_op->write_info(dquot->dq_sb, dquot->dq_type);
+ mutex_unlock(&dqopt->dqio_mutex);
return ret;
}
*/
int dquot_release(struct dquot *dquot)
{
- int ret = 0;
+ int ret = 0, ret2 = 0;
struct quota_info *dqopt = sb_dqopt(dquot->dq_sb);
- down(&dquot->dq_lock);
+ mutex_lock(&dquot->dq_lock);
/* Check whether we are not racing with some other dqget() */
if (atomic_read(&dquot->dq_count) > 1)
goto out_dqlock;
- down(&dqopt->dqio_sem);
- ret = dqopt->ops[dquot->dq_type]->release_dqblk(dquot);
+ mutex_lock(&dqopt->dqio_mutex);
+ if (dqopt->ops[dquot->dq_type]->release_dqblk) {
+ ret = dqopt->ops[dquot->dq_type]->release_dqblk(dquot);
+ /* Write the info */
+ if (info_dirty(&dqopt->info[dquot->dq_type]))
+ ret2 = dqopt->ops[dquot->dq_type]->write_file_info(dquot->dq_sb, dquot->dq_type);
+ if (ret >= 0)
+ ret = ret2;
+ }
clear_bit(DQ_ACTIVE_B, &dquot->dq_flags);
- up(&dqopt->dqio_sem);
+ mutex_unlock(&dqopt->dqio_mutex);
out_dqlock:
- up(&dquot->dq_lock);
+ mutex_unlock(&dquot->dq_lock);
return ret;
}
/* Invalidate all dquots on the list. Note that this function is called after
* quota is disabled and pointers from inodes removed so there cannot be new
- * quota users. Also because we hold dqonoff_sem there can be no quota users
- * for this sb+type at all. */
+ * quota users. There can still be some users of quotas due to inodes being
+ * just deleted or pruned by prune_icache() (those are not attached to any
+ * list). We have to wait for such users.
+ */
static void invalidate_dquots(struct super_block *sb, int type)
{
- struct dquot *dquot;
- struct list_head *head;
+ struct dquot *dquot, *tmp;
+restart:
spin_lock(&dq_list_lock);
- for (head = inuse_list.next; head != &inuse_list;) {
- dquot = list_entry(head, struct dquot, dq_inuse);
- head = head->next;
+ list_for_each_entry_safe(dquot, tmp, &inuse_list, dq_inuse) {
if (dquot->dq_sb != sb)
continue;
if (dquot->dq_type != type)
continue;
-#ifdef __DQUOT_PARANOIA
- if (atomic_read(&dquot->dq_count))
- BUG();
-#endif
- /* Quota now has no users and it has been written on last dqput() */
+ /* Wait for dquot users */
+ if (atomic_read(&dquot->dq_count)) {
+ DEFINE_WAIT(wait);
+
+ atomic_inc(&dquot->dq_count);
+ prepare_to_wait(&dquot->dq_wait_unused, &wait,
+ TASK_UNINTERRUPTIBLE);
+ spin_unlock(&dq_list_lock);
+ /* Once dqput() wakes us up, we know it's time to free
+ * the dquot.
+ * IMPORTANT: we rely on the fact that there is always
+ * at most one process waiting for dquot to free.
+ * Otherwise dq_count would be > 1 and we would never
+ * wake up.
+ */
+ if (atomic_read(&dquot->dq_count) > 1)
+ schedule();
+ finish_wait(&dquot->dq_wait_unused, &wait);
+ dqput(dquot);
+ /* At this moment dquot() need not exist (it could be
+ * reclaimed by prune_dqcache(). Hence we must
+ * restart. */
+ goto restart;
+ }
+ /*
+ * Quota now has no users and it has been written on last
+ * dqput()
+ */
remove_dquot_hash(dquot);
remove_free_dquot(dquot);
remove_inuse(dquot);
struct quota_info *dqopt = sb_dqopt(sb);
int cnt;
- down(&dqopt->dqonoff_sem);
+ mutex_lock(&dqopt->dqonoff_mutex);
for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
if (type != -1 && cnt != type)
continue;
spin_lock(&dq_list_lock);
dqstats.syncs++;
spin_unlock(&dq_list_lock);
- up(&dqopt->dqonoff_sem);
+ mutex_unlock(&dqopt->dqonoff_mutex);
return 0;
}
* more memory
*/
-static int shrink_dqcache_memory(int nr, unsigned int gfp_mask)
+static int shrink_dqcache_memory(int nr, gfp_t gfp_mask)
{
- int ret;
-
- spin_lock(&dq_list_lock);
- if (nr)
+ if (nr) {
+ spin_lock(&dq_list_lock);
prune_dqcache(nr);
- ret = dqstats.allocated_dquots;
- spin_unlock(&dq_list_lock);
- return ret;
+ spin_unlock(&dq_list_lock);
+ }
+ return (dqstats.free_dquots / 100) * sysctl_vfs_cache_pressure;
}
/*
* Put reference to dquot
* NOTE: If you change this function please check whether dqput_blocks() works right...
- * MUST be called with either dqptr_sem or dqonoff_sem held
+ * MUST be called with either dqptr_sem or dqonoff_mutex held
*/
static void dqput(struct dquot *dquot)
{
if (atomic_read(&dquot->dq_count) > 1) {
/* We have more than one user... nothing to do */
atomic_dec(&dquot->dq_count);
+ /* Releasing dquot during quotaoff phase? */
+ if (!sb_has_quota_enabled(dquot->dq_sb, dquot->dq_type) &&
+ atomic_read(&dquot->dq_count) == 1)
+ wake_up(&dquot->dq_wait_unused);
spin_unlock(&dq_list_lock);
return;
}
clear_dquot_dirty(dquot);
if (test_bit(DQ_ACTIVE_B, &dquot->dq_flags)) {
spin_unlock(&dq_list_lock);
- dquot_release(dquot);
+ dquot->dq_sb->dq_op->release_dquot(dquot);
goto we_slept;
}
atomic_dec(&dquot->dq_count);
#ifdef __DQUOT_PARANOIA
/* sanity check */
- if (!list_empty(&dquot->dq_free))
- BUG();
+ BUG_ON(!list_empty(&dquot->dq_free));
#endif
put_dquot_last(dquot);
spin_unlock(&dq_list_lock);
{
struct dquot *dquot;
- dquot = kmem_cache_alloc(dquot_cachep, SLAB_KERNEL);
+ dquot = kmem_cache_alloc(dquot_cachep, SLAB_NOFS);
if(!dquot)
return NODQUOT;
memset((caddr_t)dquot, 0, sizeof(struct dquot));
- sema_init(&dquot->dq_lock, 1);
+ mutex_init(&dquot->dq_lock);
INIT_LIST_HEAD(&dquot->dq_free);
INIT_LIST_HEAD(&dquot->dq_inuse);
INIT_HLIST_NODE(&dquot->dq_hash);
INIT_LIST_HEAD(&dquot->dq_dirty);
+ init_waitqueue_head(&dquot->dq_wait_unused);
dquot->dq_sb = sb;
dquot->dq_type = type;
atomic_set(&dquot->dq_count, 1);
/*
* Get reference to dquot
- * MUST be called with either dqptr_sem or dqonoff_sem held
+ * MUST be called with either dqptr_sem or dqonoff_mutex held
*/
static struct dquot *dqget(struct super_block *sb, unsigned int id, int type)
{
* finished or it will be canceled due to dq_count > 1 test */
wait_on_dquot(dquot);
/* Read the dquot and instantiate it (everything done only if needed) */
- if (!test_bit(DQ_ACTIVE_B, &dquot->dq_flags) && dquot_acquire(dquot) < 0) {
+ if (!test_bit(DQ_ACTIVE_B, &dquot->dq_flags) && sb->dq_op->acquire_dquot(dquot) < 0) {
dqput(dquot);
return NODQUOT;
}
#ifdef __DQUOT_PARANOIA
- if (!dquot->dq_sb) /* Has somebody invalidated entry under us? */
- BUG();
+ BUG_ON(!dquot->dq_sb); /* Has somebody invalidated entry under us? */
#endif
return dquot;
return 0;
}
-/* This routine is guarded by dqonoff_sem semaphore */
+/* This routine is guarded by dqonoff_mutex mutex */
static void add_dquot_ref(struct super_block *sb, int type)
{
struct list_head *p;
restart:
file_list_lock();
list_for_each(p, &sb->s_files) {
- struct file *filp = list_entry(p, struct file, f_list);
+ struct file *filp = list_entry(p, struct file, f_u.fu_list);
struct inode *inode = filp->f_dentry->d_inode;
if (filp->f_mode & FMODE_WRITE && dqinit_needed(inode, type)) {
struct dentry *dentry = dget(filp->f_dentry);
int remove_inode_dquot_ref(struct inode *inode, int type, struct list_head *tofree_head)
{
struct dquot *dquot = inode->i_dquot[type];
- int cnt;
inode->i_dquot[type] = NODQUOT;
- /* any other quota in use? */
- for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
- if (inode->i_dquot[cnt] != NODQUOT)
- goto put_it;
- }
- inode->i_flags &= ~S_QUOTA;
-put_it:
if (dquot != NODQUOT) {
if (dqput_blocks(dquot)) {
#ifdef __DQUOT_PARANOIA
}
}
-/* Function in inode.c - remove pointers to dquots in icache */
-extern void remove_dquot_ref(struct super_block *, int, struct list_head *);
-
/* Gather all references from inodes and drop them */
static void drop_dquot_ref(struct super_block *sb, int type)
{
dquot->dq_dqb.dqb_curinodes -= number;
else
dquot->dq_dqb.dqb_curinodes = 0;
- if (dquot->dq_dqb.dqb_curinodes < dquot->dq_dqb.dqb_isoftlimit)
+ if (dquot->dq_dqb.dqb_curinodes <= dquot->dq_dqb.dqb_isoftlimit)
dquot->dq_dqb.dqb_itime = (time_t) 0;
clear_bit(DQ_INODES_B, &dquot->dq_flags);
}
dquot->dq_dqb.dqb_curspace -= number;
else
dquot->dq_dqb.dqb_curspace = 0;
- if (toqb(dquot->dq_dqb.dqb_curspace) < dquot->dq_dqb.dqb_bsoftlimit)
+ if (toqb(dquot->dq_dqb.dqb_curspace) <= dquot->dq_dqb.dqb_bsoftlimit)
dquot->dq_dqb.dqb_btime = (time_t) 0;
clear_bit(DQ_BLKS_B, &dquot->dq_flags);
}
+static int flag_print_warnings = 1;
+
static inline int need_print_warning(struct dquot *dquot)
{
+ if (!flag_print_warnings)
+ return 0;
+
switch (dquot->dq_type) {
case USRQUOTA:
return current->fsuid == dquot->dq_id;
if (!need_print_warning(dquot) || (flag && test_and_set_bit(flag, &dquot->dq_flags)))
return;
+
tty_write_message(current->signal->tty, dquot->dq_sb->s_id);
if (warntype == ISOFTWARN || warntype == BSOFTWARN)
tty_write_message(current->signal->tty, ": warning, ");
tty_write_message(current->signal->tty, quotatypes[dquot->dq_type]);
switch (warntype) {
case IHARDWARN:
- msg = " file limit reached.\n";
+ msg = " file limit reached.\r\n";
break;
case ISOFTLONGWARN:
- msg = " file quota exceeded too long.\n";
+ msg = " file quota exceeded too long.\r\n";
break;
case ISOFTWARN:
- msg = " file quota exceeded.\n";
+ msg = " file quota exceeded.\r\n";
break;
case BHARDWARN:
- msg = " block limit reached.\n";
+ msg = " block limit reached.\r\n";
break;
case BSOFTLONGWARN:
- msg = " block quota exceeded too long.\n";
+ msg = " block quota exceeded too long.\r\n";
break;
case BSOFTWARN:
- msg = " block quota exceeded.\n";
+ msg = " block quota exceeded.\r\n";
break;
}
tty_write_message(current->signal->tty, msg);
unsigned int id = 0;
int cnt, ret = 0;
- /* First test before acquiring semaphore - solves deadlocks when we
- * re-enter the quota code and are already holding the semaphore */
+ /* First test before acquiring mutex - solves deadlocks when we
+ * re-enter the quota code and are already holding the mutex */
if (IS_NOQUOTA(inode))
return 0;
down_write(&sb_dqopt(inode->i_sb)->dqptr_sem);
break;
}
inode->i_dquot[cnt] = dqget(inode->i_sb, id, cnt);
- if (inode->i_dquot[cnt])
- inode->i_flags |= S_QUOTA;
}
}
out_err:
int cnt;
down_write(&sb_dqopt(inode->i_sb)->dqptr_sem);
- inode->i_flags &= ~S_QUOTA;
for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
if (inode->i_dquot[cnt] != NODQUOT) {
dqput(inode->i_dquot[cnt]);
int cnt, ret = NO_QUOTA;
char warntype[MAXQUOTAS];
- /* First test before acquiring semaphore - solves deadlocks when we
- * re-enter the quota code and are already holding the semaphore */
+ /* First test before acquiring mutex - solves deadlocks when we
+ * re-enter the quota code and are already holding the mutex */
if (IS_NOQUOTA(inode)) {
out_add:
inode_add_bytes(inode, number);
int cnt, ret = NO_QUOTA;
char warntype[MAXQUOTAS];
- /* First test before acquiring semaphore - solves deadlocks when we
- * re-enter the quota code and are already holding the semaphore */
+ /* First test before acquiring mutex - solves deadlocks when we
+ * re-enter the quota code and are already holding the mutex */
if (IS_NOQUOTA(inode))
return QUOTA_OK;
for (cnt = 0; cnt < MAXQUOTAS; cnt++)
}
/*
- * This is a non-blocking operation.
+ * This operation can block, but only after everything is updated
*/
int dquot_free_space(struct inode *inode, qsize_t number)
{
unsigned int cnt;
- /* First test before acquiring semaphore - solves deadlocks when we
- * re-enter the quota code and are already holding the semaphore */
+ /* First test before acquiring mutex - solves deadlocks when we
+ * re-enter the quota code and are already holding the mutex */
if (IS_NOQUOTA(inode)) {
out_sub:
inode_sub_bytes(inode, number);
}
/*
- * This is a non-blocking operation.
+ * This operation can block, but only after everything is updated
*/
int dquot_free_inode(const struct inode *inode, unsigned long number)
{
unsigned int cnt;
- /* First test before acquiring semaphore - solves deadlocks when we
- * re-enter the quota code and are already holding the semaphore */
+ /* First test before acquiring mutex - solves deadlocks when we
+ * re-enter the quota code and are already holding the mutex */
if (IS_NOQUOTA(inode))
return QUOTA_OK;
down_read(&sb_dqopt(inode->i_sb)->dqptr_sem);
* Transfer the number of inode and blocks from one diskquota to an other.
*
* This operation can block, but only after everything is updated
+ * A transaction must be started when entering this function.
*/
int dquot_transfer(struct inode *inode, struct iattr *iattr)
{
chgid = (iattr->ia_valid & ATTR_GID) && inode->i_gid != iattr->ia_gid;
char warntype[MAXQUOTAS];
- /* First test before acquiring semaphore - solves deadlocks when we
- * re-enter the quota code and are already holding the semaphore */
+ /* First test before acquiring mutex - solves deadlocks when we
+ * re-enter the quota code and are already holding the mutex */
if (IS_NOQUOTA(inode))
return QUOTA_OK;
/* Clear the arrays */
if (transfer_to[cnt] == NODQUOT)
continue;
- dquot_decr_inodes(transfer_from[cnt], 1);
- dquot_decr_space(transfer_from[cnt], space);
+ /* Due to IO error we might not have transfer_from[] structure */
+ if (transfer_from[cnt]) {
+ dquot_decr_inodes(transfer_from[cnt], 1);
+ dquot_decr_space(transfer_from[cnt], space);
+ }
dquot_incr_inodes(transfer_to[cnt], 1);
dquot_incr_space(transfer_to[cnt], space);
int ret;
struct quota_info *dqopt = sb_dqopt(sb);
- down(&dqopt->dqio_sem);
+ mutex_lock(&dqopt->dqio_mutex);
ret = dqopt->ops[type]->write_file_info(sb, type);
- up(&dqopt->dqio_sem);
+ mutex_unlock(&dqopt->dqio_mutex);
return ret;
}
.free_inode = dquot_free_inode,
.transfer = dquot_transfer,
.write_dquot = dquot_commit,
+ .acquire_dquot = dquot_acquire,
+ .release_dquot = dquot_release,
.mark_dirty = dquot_mark_dquot_dirty,
.write_info = dquot_commit_info
};
{
int cnt;
struct quota_info *dqopt = sb_dqopt(sb);
+ struct inode *toputinode[MAXQUOTAS];
/* We need to serialize quota_off() for device */
- down(&dqopt->dqonoff_sem);
+ mutex_lock(&dqopt->dqonoff_mutex);
for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
+ toputinode[cnt] = NULL;
if (type != -1 && cnt != type)
continue;
if (!sb_has_quota_enabled(sb, cnt))
dqopt->ops[cnt]->free_file_info(sb, cnt);
put_quota_format(dqopt->info[cnt].dqi_format);
- fput(dqopt->files[cnt]);
+ toputinode[cnt] = dqopt->files[cnt];
dqopt->files[cnt] = NULL;
dqopt->info[cnt].dqi_flags = 0;
dqopt->info[cnt].dqi_igrace = 0;
dqopt->info[cnt].dqi_bgrace = 0;
dqopt->ops[cnt] = NULL;
}
- up(&dqopt->dqonoff_sem);
+ mutex_unlock(&dqopt->dqonoff_mutex);
+ /* Sync the superblock so that buffers with quota data are written to
+ * disk (and so userspace sees correct data afterwards). */
+ if (sb->s_op->sync_fs)
+ sb->s_op->sync_fs(sb, 1);
+ sync_blockdev(sb->s_bdev);
+ /* Now the quota files are just ordinary files and we can set the
+ * inode flags back. Moreover we discard the pagecache so that
+ * userspace sees the writes we did bypassing the pagecache. We
+ * must also discard the blockdev buffers so that we see the
+ * changes done by userspace on the next quotaon() */
+ for (cnt = 0; cnt < MAXQUOTAS; cnt++)
+ if (toputinode[cnt]) {
+ mutex_lock(&dqopt->dqonoff_mutex);
+ /* If quota was reenabled in the meantime, we have
+ * nothing to do */
+ if (!sb_has_quota_enabled(sb, cnt)) {
+ mutex_lock(&toputinode[cnt]->i_mutex);
+ toputinode[cnt]->i_flags &= ~(S_IMMUTABLE |
+ S_NOATIME | S_NOQUOTA);
+ truncate_inode_pages(&toputinode[cnt]->i_data, 0);
+ mutex_unlock(&toputinode[cnt]->i_mutex);
+ mark_inode_dirty(toputinode[cnt]);
+ iput(toputinode[cnt]);
+ }
+ mutex_unlock(&dqopt->dqonoff_mutex);
+ }
+ if (sb->s_bdev)
+ invalidate_bdev(sb->s_bdev, 0);
return 0;
}
* Turn quotas on on a device
*/
-/* Helper function when we already have file open */
-static int vfs_quota_on_file(struct file *f, int type, int format_id)
+/* Helper function when we already have the inode */
+static int vfs_quota_on_inode(struct inode *inode, int type, int format_id)
{
struct quota_format_type *fmt = find_quota_format(format_id);
- struct inode *inode;
- struct super_block *sb = f->f_dentry->d_sb;
+ struct super_block *sb = inode->i_sb;
struct quota_info *dqopt = sb_dqopt(sb);
- struct dquot *to_drop[MAXQUOTAS];
- int error, cnt;
- unsigned int oldflags;
+ int error;
+ int oldflags = -1;
if (!fmt)
return -ESRCH;
- error = -EIO;
- if (!f->f_op || !f->f_op->read || !f->f_op->write)
+ if (!S_ISREG(inode->i_mode)) {
+ error = -EACCES;
goto out_fmt;
- inode = f->f_dentry->d_inode;
- error = -EACCES;
- if (!S_ISREG(inode->i_mode))
+ }
+ if (IS_RDONLY(inode)) {
+ error = -EROFS;
+ goto out_fmt;
+ }
+ if (!sb->s_op->quota_write || !sb->s_op->quota_read) {
+ error = -EINVAL;
goto out_fmt;
+ }
- down(&dqopt->dqonoff_sem);
+ /* As we bypass the pagecache we must now flush the inode so that
+ * we see all the changes from userspace... */
+ write_inode_now(inode, 1);
+ /* And now flush the block cache so that kernel sees the changes */
+ invalidate_bdev(sb->s_bdev, 0);
+ mutex_lock(&inode->i_mutex);
+ mutex_lock(&dqopt->dqonoff_mutex);
if (sb_has_quota_enabled(sb, type)) {
error = -EBUSY;
goto out_lock;
}
- oldflags = inode->i_flags;
- dqopt->files[type] = f;
+ /* We don't want quota and atime on quota files (deadlocks possible)
+ * Also nobody should write to the file - we use special IO operations
+ * which ignore the immutable bit. */
+ down_write(&dqopt->dqptr_sem);
+ oldflags = inode->i_flags & (S_NOATIME | S_IMMUTABLE | S_NOQUOTA);
+ inode->i_flags |= S_NOQUOTA | S_NOATIME | S_IMMUTABLE;
+ up_write(&dqopt->dqptr_sem);
+ sb->dq_op->drop(inode);
+
+ error = -EIO;
+ dqopt->files[type] = igrab(inode);
+ if (!dqopt->files[type])
+ goto out_lock;
error = -EINVAL;
if (!fmt->qf_ops->check_quota_file(sb, type))
goto out_file_init;
- /* We don't want quota and atime on quota files (deadlocks possible) */
- down_write(&dqopt->dqptr_sem);
- inode->i_flags |= S_NOQUOTA | S_NOATIME;
- for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
- to_drop[cnt] = inode->i_dquot[cnt];
- inode->i_dquot[cnt] = NODQUOT;
- }
- inode->i_flags &= ~S_QUOTA;
- up_write(&dqopt->dqptr_sem);
- /* We must put dquots outside of dqptr_sem because we may need to
- * start transaction for dquot_release() */
- for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
- if (to_drop[cnt])
- dqput(to_drop[cnt]);
- }
dqopt->ops[type] = fmt->qf_ops;
dqopt->info[type].dqi_format = fmt;
INIT_LIST_HEAD(&dqopt->info[type].dqi_dirty_list);
- down(&dqopt->dqio_sem);
+ mutex_lock(&dqopt->dqio_mutex);
if ((error = dqopt->ops[type]->read_file_info(sb, type)) < 0) {
- up(&dqopt->dqio_sem);
+ mutex_unlock(&dqopt->dqio_mutex);
goto out_file_init;
}
- up(&dqopt->dqio_sem);
+ mutex_unlock(&dqopt->dqio_mutex);
+ mutex_unlock(&inode->i_mutex);
set_enable_flags(dqopt, type);
add_dquot_ref(sb, type);
- up(&dqopt->dqonoff_sem);
+ mutex_unlock(&dqopt->dqonoff_mutex);
return 0;
out_file_init:
- inode->i_flags = oldflags;
dqopt->files[type] = NULL;
+ iput(inode);
out_lock:
- up_write(&dqopt->dqptr_sem);
- up(&dqopt->dqonoff_sem);
+ mutex_unlock(&dqopt->dqonoff_mutex);
+ if (oldflags != -1) {
+ down_write(&dqopt->dqptr_sem);
+ /* Set the flags back (in the case of accidental quotaon()
+ * on a wrong file we don't want to mess up the flags) */
+ inode->i_flags &= ~(S_NOATIME | S_NOQUOTA | S_IMMUTABLE);
+ inode->i_flags |= oldflags;
+ up_write(&dqopt->dqptr_sem);
+ }
+ mutex_unlock(&inode->i_mutex);
out_fmt:
put_quota_format(fmt);
/* Actual function called from quotactl() */
int vfs_quota_on(struct super_block *sb, int type, int format_id, char *path)
{
- struct file *f;
+ struct nameidata nd;
int error;
- f = filp_open(path, O_RDWR, 0600);
- if (IS_ERR(f))
- return PTR_ERR(f);
- error = security_quota_on(f);
+ error = path_lookup(path, LOOKUP_FOLLOW, &nd);
+ if (error < 0)
+ return error;
+ error = security_quota_on(nd.dentry);
if (error)
- goto out_f;
- error = vfs_quota_on_file(f, type, format_id);
- if (!error)
- return 0;
-out_f:
- filp_close(f, NULL);
+ goto out_path;
+ /* Quota file not on the same filesystem? */
+ if (nd.mnt->mnt_sb != sb)
+ error = -EXDEV;
+ else
+ error = vfs_quota_on_inode(nd.dentry->d_inode, type, format_id);
+out_path:
+ path_release(&nd);
return error;
}
/*
- * Function used by filesystems when filp_open() would fail (filesystem is
- * being mounted now). We will use a private file structure. Caller is
- * responsible that it's IO functions won't need vfsmnt structure or
- * some dentry tricks...
+ * This function is used when filesystem needs to initialize quotas
+ * during mount time.
*/
-int vfs_quota_on_mount(int type, int format_id, struct dentry *dentry)
+int vfs_quota_on_mount(struct super_block *sb, char *qf_name,
+ int format_id, int type)
{
- struct file *f;
+ struct dentry *dentry;
int error;
- dget(dentry); /* Get a reference for struct file */
- f = dentry_open(dentry, NULL, O_RDWR);
- if (IS_ERR(f)) {
- error = PTR_ERR(f);
- goto out_dentry;
+ dentry = lookup_one_len(qf_name, sb->s_root, strlen(qf_name));
+ if (IS_ERR(dentry))
+ return PTR_ERR(dentry);
+
+ if (!dentry->d_inode) {
+ error = -ENOENT;
+ goto out;
}
- error = vfs_quota_on_file(f, type, format_id);
+
+ error = security_quota_on(dentry);
if (!error)
- return 0;
- fput(f);
-out_dentry:
+ error = vfs_quota_on_inode(dentry->d_inode, type, format_id);
+
+out:
dput(dentry);
return error;
}
{
struct dquot *dquot;
- down(&sb_dqopt(sb)->dqonoff_sem);
+ mutex_lock(&sb_dqopt(sb)->dqonoff_mutex);
if (!(dquot = dqget(sb, id, type))) {
- up(&sb_dqopt(sb)->dqonoff_sem);
+ mutex_unlock(&sb_dqopt(sb)->dqonoff_mutex);
return -ESRCH;
}
do_get_dqblk(dquot, di);
dqput(dquot);
- up(&sb_dqopt(sb)->dqonoff_sem);
+ mutex_unlock(&sb_dqopt(sb)->dqonoff_mutex);
return 0;
}
{
struct dquot *dquot;
- down(&sb_dqopt(sb)->dqonoff_sem);
+ mutex_lock(&sb_dqopt(sb)->dqonoff_mutex);
if (!(dquot = dqget(sb, id, type))) {
- up(&sb_dqopt(sb)->dqonoff_sem);
+ mutex_unlock(&sb_dqopt(sb)->dqonoff_mutex);
return -ESRCH;
}
do_set_dqblk(dquot, di);
dqput(dquot);
- up(&sb_dqopt(sb)->dqonoff_sem);
+ mutex_unlock(&sb_dqopt(sb)->dqonoff_mutex);
return 0;
}
{
struct mem_dqinfo *mi;
- down(&sb_dqopt(sb)->dqonoff_sem);
+ mutex_lock(&sb_dqopt(sb)->dqonoff_mutex);
if (!sb_has_quota_enabled(sb, type)) {
- up(&sb_dqopt(sb)->dqonoff_sem);
+ mutex_unlock(&sb_dqopt(sb)->dqonoff_mutex);
return -ESRCH;
}
mi = sb_dqopt(sb)->info + type;
ii->dqi_flags = mi->dqi_flags & DQF_MASK;
ii->dqi_valid = IIF_ALL;
spin_unlock(&dq_data_lock);
- up(&sb_dqopt(sb)->dqonoff_sem);
+ mutex_unlock(&sb_dqopt(sb)->dqonoff_mutex);
return 0;
}
{
struct mem_dqinfo *mi;
- down(&sb_dqopt(sb)->dqonoff_sem);
+ mutex_lock(&sb_dqopt(sb)->dqonoff_mutex);
if (!sb_has_quota_enabled(sb, type)) {
- up(&sb_dqopt(sb)->dqonoff_sem);
+ mutex_unlock(&sb_dqopt(sb)->dqonoff_mutex);
return -ESRCH;
}
mi = sb_dqopt(sb)->info + type;
mark_info_dirty(sb, type);
/* Force write to disk */
sb->dq_op->write_info(sb, type);
- up(&sb_dqopt(sb)->dqonoff_sem);
+ mutex_unlock(&sb_dqopt(sb)->dqonoff_mutex);
return 0;
}
.mode = 0444,
.proc_handler = &proc_dointvec,
},
+ {
+ .ctl_name = FS_DQ_WARNINGS,
+ .procname = "warnings",
+ .data = &flag_print_warnings,
+ .maxlen = sizeof(int),
+ .mode = 0644,
+ .proc_handler = &proc_dointvec,
+ },
{ .ctl_name = 0 },
};
{ .ctl_name = 0 },
};
-/* SLAB cache for dquot structures */
-kmem_cache_t *dquot_cachep;
-
static int __init dquot_init(void)
{
int i;
dquot_cachep = kmem_cache_create("dquot",
sizeof(struct dquot), sizeof(unsigned long) * 4,
- SLAB_HWCACHE_ALIGN|SLAB_RECLAIM_ACCOUNT, NULL, NULL);
- if (!dquot_cachep)
- panic("Cannot create dquot SLAB cache");
+ (SLAB_HWCACHE_ALIGN|SLAB_RECLAIM_ACCOUNT|
+ SLAB_MEM_SPREAD|SLAB_PANIC),
+ NULL, NULL);
order = 0;
dquot_hash = (struct hlist_head *)__get_free_pages(GFP_ATOMIC, order);
EXPORT_SYMBOL(register_quota_format);
EXPORT_SYMBOL(unregister_quota_format);
EXPORT_SYMBOL(dqstats);
-EXPORT_SYMBOL(dq_list_lock);
EXPORT_SYMBOL(dq_data_lock);
EXPORT_SYMBOL(vfs_quota_on);
EXPORT_SYMBOL(vfs_quota_on_mount);
EXPORT_SYMBOL(vfs_set_dqblk);
EXPORT_SYMBOL(dquot_commit);
EXPORT_SYMBOL(dquot_commit_info);
+EXPORT_SYMBOL(dquot_acquire);
+EXPORT_SYMBOL(dquot_release);
EXPORT_SYMBOL(dquot_mark_dquot_dirty);
EXPORT_SYMBOL(dquot_initialize);
EXPORT_SYMBOL(dquot_drop);