diff -u -r -x *.orig -x *.o linux-2.4.0-test11/fs/attr.c linux-2.4.0-test11-quotafix/fs/attr.c --- linux-2.4.0-test11/fs/attr.c Fri Nov 3 22:07:21 2000 +++ linux-2.4.0-test11-quotafix/fs/attr.c Mon Nov 27 09:43:42 2000 @@ -11,6 +11,7 @@ #include #include #include +#include /* Taken over from the old code... */ @@ -124,8 +125,13 @@ error = inode->i_op->setattr(dentry, attr); else { error = inode_change_ok(inode, attr); - if (!error) - inode_setattr(inode, attr); + if (!error) { + if ((ia_valid & ATTR_UID && attr->ia_uid != inode->i_uid) || + (ia_valid & ATTR_GID && attr->ia_gid != inode->i_gid)) + error = DQUOT_TRANSFER(inode, attr) ? -EDQUOT : 0; + if (!error) + inode_setattr(inode, attr); + } } unlock_kernel(); if (!error) { diff -u -r -x *.orig -x *.o linux-2.4.0-test11/fs/dquot.c linux-2.4.0-test11-quotafix/fs/dquot.c --- linux-2.4.0-test11/fs/dquot.c Sun Nov 26 22:52:32 2000 +++ linux-2.4.0-test11-quotafix/fs/dquot.c Mon Nov 27 09:43:43 2000 @@ -34,22 +34,27 @@ * Added check for bogus uid and fixed check for group in quotactl. * Jan Kara, , sponsored by SuSE CR, 10-11/99 * + * Used struct list_head instead of own list struct + * Invalidation of dquots with dq_count > 0 no longer possible + * Improved free_dquots list management + * Quota and i_blocks are now updated in one place to avoid races + * Warnings are now delayed so we won't block in critical section + * Write updated not to require dquot lock + * Jan Kara, , 9/2000 + * * (C) Copyright 1994 - 1997 Marco van Wieringen */ #include #include +#include #include - #include #include #include #include #include #include -#include -#include -#include #include #include #include @@ -59,9 +64,7 @@ #define __DQUOT_VERSION__ "dquot_6.4.0" int nr_dquots, nr_free_dquots; -int max_dquots = NR_DQUOTS; -static char quotamessage[MAX_QUOTA_MESSAGE]; static char *quotatypes[] = INITQFNAMES; static inline struct quota_mount_options *sb_dqopt(struct super_block *sb) @@ -75,28 +78,31 @@ * free_dquots, and dquot_hash[] array. A single dquot structure may be * on all three lists, depending on its current state. * - * All dquots are placed on the inuse_list when first created, and this + * 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. * * 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 removed from the list as soon as they are used again, and - * nr_free_dquots gives the number of dquots on the list. + * nr_free_dquots gives the number of dquots on the list. When dquot is + * invalidated it's completely released from memory. * * Dquots with a specific identity (device, type and id) are placed on * one of the dquot_hash[] hash chains. The provides an efficient search - * mechanism to lcoate a specific dquot. + * mechanism to locate a specific dquot. + */ + +/* + * Note that any operation which operates on dquot data (ie. dq_dqb) mustn't + * block while it's updating/reading it. Otherwise races would occur. */ -static struct dquot *inuse_list; +static LIST_HEAD(inuse_list); static LIST_HEAD(free_dquots); -static struct dquot *dquot_hash[NR_DQHASH]; -static int dquot_updating[NR_DQHASH]; +static struct list_head dquot_hash[NR_DQHASH]; static struct dqstats dqstats; -static DECLARE_WAIT_QUEUE_HEAD(dquot_wait); -static DECLARE_WAIT_QUEUE_HEAD(update_wait); static void dqput(struct dquot *); static struct dquot *dqduplicate(struct dquot *); @@ -124,38 +130,27 @@ static inline void insert_dquot_hash(struct dquot *dquot) { - struct dquot **htable; - - htable = &dquot_hash[hashfn(dquot->dq_dev, dquot->dq_id, dquot->dq_type)]; - if ((dquot->dq_hash_next = *htable) != NULL) - (*htable)->dq_hash_pprev = &dquot->dq_hash_next; - *htable = dquot; - dquot->dq_hash_pprev = htable; + struct list_head *head = dquot_hash + hashfn(dquot->dq_dev, dquot->dq_id, dquot->dq_type); + list_add(&dquot->dq_hash, head); } -static inline void hash_dquot(struct dquot *dquot) +static inline void remove_dquot_hash(struct dquot *dquot) { - insert_dquot_hash(dquot); -} - -static inline void unhash_dquot(struct dquot *dquot) -{ - if (dquot->dq_hash_pprev) { - if (dquot->dq_hash_next) - dquot->dq_hash_next->dq_hash_pprev = dquot->dq_hash_pprev; - *(dquot->dq_hash_pprev) = dquot->dq_hash_next; - dquot->dq_hash_pprev = NULL; - } + list_del(&dquot->dq_hash); + INIT_LIST_HEAD(&dquot->dq_hash); } static inline struct dquot *find_dquot(unsigned int hashent, kdev_t dev, unsigned int id, short type) { + struct list_head *head; struct dquot *dquot; - for (dquot = dquot_hash[hashent]; dquot; dquot = dquot->dq_hash_next) + for (head = dquot_hash[hashent].next; head != dquot_hash+hashent; head = head->next) { + dquot = list_entry(head, struct dquot, dq_hash); if (dquot->dq_dev == dev && dquot->dq_id == id && dquot->dq_type == type) - break; - return dquot; + return dquot; + } + return NODQUOT; } /* Add a dquot to the head of the free list */ @@ -172,6 +167,13 @@ nr_free_dquots++; } +/* Move dquot to the head of free list (it must be already on it) */ +static inline void move_dquot_head(struct dquot *dquot) +{ + list_del(&dquot->dq_free); + list_add(&dquot->dq_free, &free_dquots); +} + static inline void remove_free_dquot(struct dquot *dquot) { /* sanity check */ @@ -186,36 +188,30 @@ static inline void put_inuse(struct dquot *dquot) { - if ((dquot->dq_next = inuse_list) != NULL) - inuse_list->dq_pprev = &dquot->dq_next; - inuse_list = dquot; - dquot->dq_pprev = &inuse_list; + /* 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); + nr_dquots++; } -#if 0 /* currently not needed */ static inline void remove_inuse(struct dquot *dquot) { - if (dquot->dq_pprev) { - if (dquot->dq_next) - dquot->dq_next->dq_pprev = dquot->dq_pprev; - *dquot->dq_pprev = dquot->dq_next; - dquot->dq_pprev = NULL; - } + nr_dquots--; + list_del(&dquot->dq_inuse); } -#endif static void __wait_on_dquot(struct dquot *dquot) { DECLARE_WAITQUEUE(wait, current); - add_wait_queue(&dquot->dq_wait, &wait); + add_wait_queue(&dquot->dq_wait_lock, &wait); repeat: set_current_state(TASK_UNINTERRUPTIBLE); if (dquot->dq_flags & DQ_LOCKED) { schedule(); goto repeat; } - remove_wait_queue(&dquot->dq_wait, &wait); + remove_wait_queue(&dquot->dq_wait_lock, &wait); current->state = TASK_RUNNING; } @@ -234,7 +230,22 @@ static inline void unlock_dquot(struct dquot *dquot) { dquot->dq_flags &= ~DQ_LOCKED; - wake_up(&dquot->dq_wait); + wake_up(&dquot->dq_wait_lock); +} + +static void __wait_dquot_unused(struct dquot *dquot) +{ + DECLARE_WAITQUEUE(wait, current); + + add_wait_queue(&dquot->dq_wait_free, &wait); +repeat: + set_current_state(TASK_UNINTERRUPTIBLE); + if (dquot->dq_count) { + schedule(); + goto repeat; + } + remove_wait_queue(&dquot->dq_wait_free, &wait); + current->state = TASK_RUNNING; } /* @@ -248,12 +259,8 @@ loff_t offset; ssize_t ret; struct semaphore *sem = &dquot->dq_sb->s_dquot.dqio_sem; + struct dqblk dqbuf; - lock_dquot(dquot); - if (!dquot->dq_sb) { /* Invalidated quota? */ - unlock_dquot(dquot); - return; - } down(sem); filp = dquot->dq_sb->s_dquot.files[type]; offset = dqoff(dquot->dq_id); @@ -264,10 +271,11 @@ * Note: clear the DQ_MOD flag unconditionally, * so we don't loop forever on failure. */ + memcpy(&dqbuf, &dquot->dq_dqb, sizeof(struct dqblk)); dquot->dq_flags &= ~DQ_MOD; ret = 0; if (filp) - ret = filp->f_op->write(filp, (char *)&dquot->dq_dqb, + ret = filp->f_op->write(filp, (char *)&dqbuf, sizeof(struct dqblk), &offset); if (ret != sizeof(struct dqblk)) printk(KERN_WARNING "VFS: dquota write failed on dev %s\n", @@ -275,8 +283,6 @@ set_fs(fs); up(sem); - unlock_dquot(dquot); - dqstats.writes++; } @@ -319,100 +325,100 @@ void clear_dquot(struct dquot *dquot) { /* unhash it first */ - unhash_dquot(dquot); - dquot->dq_sb = NULL; - dquot->dq_flags = 0; - dquot->dq_referenced = 0; - memset(&dquot->dq_dqb, 0, sizeof(struct dqblk)); + remove_dquot_hash(dquot); + dquot->dq_sb = NULL; + dquot->dq_id = 0; + dquot->dq_dev = NODEV; + dquot->dq_type = -1; + dquot->dq_flags = 0; + dquot->dq_referenced = 0; + memset(&dquot->dq_dqb, 0, sizeof(struct dqblk)); } +/* Invalidate all dquots on the list, wait for all users. Note that this function is called + * after quota is disabled so no new quota might be created. As we only insert to the end of + * inuse list, we don't have to restart searching... */ void invalidate_dquots(kdev_t dev, short type) { - struct dquot *dquot, *next; - int need_restart; + struct dquot *dquot; + struct list_head *head; restart: - next = inuse_list; /* Here it is better. Otherwise the restart doesn't have any sense ;-) */ - need_restart = 0; - while ((dquot = next) != NULL) { - next = dquot->dq_next; + for (head = inuse_list.next; head != &inuse_list; head = head->next) { + dquot = list_entry(head, struct dquot, dq_inuse); if (dquot->dq_dev != dev) continue; if (dquot->dq_type != type) continue; if (!dquot->dq_sb) /* Already invalidated entry? */ continue; - if (dquot->dq_flags & DQ_LOCKED) { - __wait_on_dquot(dquot); - - /* Set the flag for another pass. */ - need_restart = 1; + if (dquot->dq_count) /* - * Make sure it's still the same dquot. + * Wait for any users of quota. As we have already cleared the flags in + * superblock and cleared all pointers from inodes we are assured + * that there will be no new users of this quota. */ - if (dquot->dq_dev != dev) - continue; - if (dquot->dq_type != type) - continue; - if (!dquot->dq_sb) - continue; - } - /* - * Because inodes needn't to be the only holders of dquot - * the quota needn't to be written to disk. So we write it - * ourselves before discarding the data just for sure... - */ - if (dquot->dq_flags & DQ_MOD && dquot->dq_sb) - { - write_dquot(dquot); - need_restart = 1; /* We slept on IO */ - } - clear_dquot(dquot); - } - /* - * If anything blocked, restart the operation - * to ensure we don't miss any dquots. - */ - if (need_restart) + __wait_dquot_unused(dquot); + /* Quota now have no users and it has been written on last dqput() */ + remove_dquot_hash(dquot); + remove_free_dquot(dquot); + remove_inuse(dquot); + kmem_cache_free(dquot_cachep, dquot); goto restart; + } } int sync_dquots(kdev_t dev, short type) { - struct dquot *dquot, *next, *ddquot; - int need_restart; + struct list_head *head; + struct dquot *dquot; restart: - next = inuse_list; - need_restart = 0; - while ((dquot = next) != NULL) { - next = dquot->dq_next; + for (head = inuse_list.next; head != &inuse_list; head = head->next) { + dquot = list_entry(head, struct dquot, dq_inuse); if (dev && dquot->dq_dev != dev) continue; if (type != -1 && dquot->dq_type != type) continue; if (!dquot->dq_sb) /* Invalidated? */ continue; - if (!(dquot->dq_flags & (DQ_LOCKED | DQ_MOD))) + if (!(dquot->dq_flags & (DQ_MOD | DQ_LOCKED))) continue; + /* Raise use count so quota won't be invalidated. We can't use dqduplicate() as it does too many tests */ + dquot->dq_count++; + if (dquot->dq_flags & DQ_LOCKED) + wait_on_dquot(dquot); + if (dquot->dq_flags & DQ_MOD) + write_dquot(dquot); + dqput(dquot); + goto restart; + } + dqstats.syncs++; + return 0; +} - if ((ddquot = dqduplicate(dquot)) == NODQUOT) - continue; - if (ddquot->dq_flags & DQ_MOD) - write_dquot(ddquot); - dqput(ddquot); - /* Set the flag for another pass. */ - need_restart = 1; +/* Free unused dquots from cache */ +void prune_dqcache(int count) +{ + struct list_head *head; + struct dquot *dquot; + + head = free_dquots.prev; + while (head != &free_dquots && count) { + dquot = list_entry(head, struct dquot, dq_free); + remove_dquot_hash(dquot); + remove_free_dquot(dquot); + remove_inuse(dquot); + kmem_cache_free(dquot_cachep, dquot); + count--; + head = free_dquots.prev; } - /* - * If anything blocked, restart the operation - * to ensure we don't miss any dquots. - */ - if (need_restart) - goto restart; +} - dqstats.syncs++; - return(0); +void shrink_dqcache_memory(int priority, unsigned int gfp_mask) +{ + prune_dqcache(nr_free_dquots / (priority + 1)); + kmem_cache_shrink(dquot_cachep); } /* NOTE: If you change this function please check whether dqput_blocks() works right... */ @@ -428,24 +434,14 @@ return; } - /* - * If the dq_sb pointer isn't initialized this entry needs no - * checking and doesn't need to be written. It's just an empty - * dquot that is put back on to the freelist. - */ - if (dquot->dq_sb) - dqstats.drops++; + dqstats.drops++; we_slept: if (dquot->dq_count > 1) { /* We have more than one user... We can simply decrement use count */ dquot->dq_count--; return; } - if (dquot->dq_flags & DQ_LOCKED) { - printk(KERN_ERR "VFS: Locked quota to be put on the free list.\n"); - dquot->dq_flags &= ~DQ_LOCKED; - } - if (dquot->dq_sb && dquot->dq_flags & DQ_MOD) { + if (dquot->dq_flags & DQ_MOD) { write_dquot(dquot); goto we_slept; } @@ -457,130 +453,49 @@ return; } dquot->dq_count--; - dquot->dq_flags &= ~DQ_MOD; /* Modified flag has no sense on free list */ /* Place at end of LRU free queue */ put_dquot_last(dquot); - wake_up(&dquot_wait); -} - -static int grow_dquots(void) -{ - struct dquot *dquot; - int cnt = 0; - - while (cnt < 32) { - dquot = kmem_cache_alloc(dquot_cachep, SLAB_KERNEL); - if(!dquot) - return cnt; - - nr_dquots++; - memset((caddr_t)dquot, 0, sizeof(struct dquot)); - init_waitqueue_head(&dquot->dq_wait); - /* all dquots go on the inuse_list */ - put_inuse(dquot); - put_dquot_head(dquot); - cnt++; - } - return cnt; -} - -static struct dquot *find_best_candidate_weighted(void) -{ - struct list_head *tmp = &free_dquots; - struct dquot *dquot, *best = NULL; - unsigned long myscore, bestscore = ~0U; - int limit = (nr_free_dquots > 128) ? nr_free_dquots >> 2 : 32; - - while ((tmp = tmp->next) != &free_dquots && --limit) { - dquot = list_entry(tmp, struct dquot, dq_free); - /* This should never happen... */ - if (dquot->dq_flags & (DQ_LOCKED | DQ_MOD)) - continue; - myscore = dquot->dq_referenced; - if (myscore < bestscore) { - bestscore = myscore; - best = dquot; - } - } - return best; -} - -static inline struct dquot *find_best_free(void) -{ - struct list_head *tmp = &free_dquots; - struct dquot *dquot; - int limit = (nr_free_dquots > 1024) ? nr_free_dquots >> 5 : 32; - - while ((tmp = tmp->next) != &free_dquots && --limit) { - dquot = list_entry(tmp, struct dquot, dq_free); - if (dquot->dq_referenced == 0) - return dquot; - } - return NULL; + wake_up(&dquot->dq_wait_free); } struct dquot *get_empty_dquot(void) { struct dquot *dquot; - int shrink = 1; /* Number of times we should try to shrink dcache and icache */ -repeat: - dquot = find_best_free(); - if (!dquot) - goto pressure; -got_it: - /* Sanity checks */ - if (dquot->dq_flags & DQ_LOCKED) - printk(KERN_ERR "VFS: Locked dquot on the free list\n"); - if (dquot->dq_count != 0) - printk(KERN_ERR "VFS: free dquot count=%d\n", dquot->dq_count); + dquot = kmem_cache_alloc(dquot_cachep, SLAB_KERNEL); + if(!dquot) + return NODQUOT; - remove_free_dquot(dquot); + memset((caddr_t)dquot, 0, sizeof(struct dquot)); + init_waitqueue_head(&dquot->dq_wait_free); + init_waitqueue_head(&dquot->dq_wait_lock); + INIT_LIST_HEAD(&dquot->dq_free); + INIT_LIST_HEAD(&dquot->dq_inuse); + INIT_LIST_HEAD(&dquot->dq_hash); dquot->dq_count = 1; - /* unhash and selectively clear the structure */ - clear_dquot(dquot); - return dquot; - -pressure: - if (nr_dquots < max_dquots) - if (grow_dquots()) - goto repeat; - - dquot = find_best_candidate_weighted(); - if (dquot) - goto got_it; - /* - * Try pruning the dcache to free up some dquots ... - */ - if (shrink) { - printk(KERN_DEBUG "get_empty_dquot: pruning dcache and icache\n"); - prune_dcache(128); - prune_icache(128); - shrink--; - goto repeat; - } + /* all dquots go on the inuse_list */ + put_inuse(dquot); - printk("VFS: No free dquots, contact mvw@planets.elm.net\n"); - sleep_on(&dquot_wait); - goto repeat; + return dquot; } static struct dquot *dqget(struct super_block *sb, unsigned int id, short type) { unsigned int hashent = hashfn(sb->s_dev, id, type); - struct dquot *dquot, *empty = NULL; + struct dquot *dquot, *empty = NODQUOT; struct quota_mount_options *dqopt = sb_dqopt(sb); - if (!is_enabled(dqopt, type)) - return(NODQUOT); - we_slept: - if ((dquot = find_dquot(hashent, sb->s_dev, id, type)) == NULL) { - if (empty == NULL) { - dquot_updating[hashent]++; - empty = get_empty_dquot(); - if (!--dquot_updating[hashent]) - wake_up(&update_wait); + if (!is_enabled(dqopt, type)) { + if (empty) + dqput(empty); + return NODQUOT; + } + + if ((dquot = find_dquot(hashent, sb->s_dev, id, type)) == NODQUOT) { + if (empty == NODQUOT) { + if ((empty = get_empty_dquot()) == NODQUOT) + schedule(); /* Try to wait for a moment... */ goto we_slept; } dquot = empty; @@ -589,25 +504,19 @@ dquot->dq_dev = sb->s_dev; dquot->dq_sb = sb; /* hash it first so it can be found */ - hash_dquot(dquot); + insert_dquot_hash(dquot); read_dquot(dquot); } else { - if (!dquot->dq_count++) { + if (!dquot->dq_count++) remove_free_dquot(dquot); - } else - dqstats.cache_hits++; + dqstats.cache_hits++; wait_on_dquot(dquot); if (empty) dqput(empty); } - while (dquot_updating[hashent]) - sleep_on(&update_wait); - if (!dquot->dq_sb) { /* Has somebody invalidated entry under us? */ - /* - * Do it as if the quota was invalidated before we started - */ + printk(KERN_ERR "VFS: dqget(): Quota invalidated in dqget()!\n"); dqput(dquot); return NODQUOT; } @@ -619,14 +528,16 @@ static struct dquot *dqduplicate(struct dquot *dquot) { - if (dquot == NODQUOT || !dquot->dq_sb) + if (dquot == NODQUOT) return NODQUOT; dquot->dq_count++; - wait_on_dquot(dquot); if (!dquot->dq_sb) { + printk(KERN_ERR "VFS: dqduplicate(): Invalidated quota to be duplicated!\n"); dquot->dq_count--; return NODQUOT; } + if (dquot->dq_flags & DQ_LOCKED) + printk(KERN_ERR "VFS: dqduplicate(): Locked quota to be duplicated!\n"); dquot->dq_referenced++; dqstats.lookups++; return dquot; @@ -718,9 +629,9 @@ printk(KERN_WARNING "VFS: Adding dquot with dq_count %d to dispose list.\n", dquot->dq_count); list_add(&dquot->dq_free, tofree_head); /* As dquot must have currently users it can't be on the free list... */ return 1; - } else { - dqput(dquot); /* We have guaranteed we won't block */ } + else + dqput(dquot); /* We have guaranteed we won't block */ } return 0; } @@ -788,14 +699,61 @@ return 0; } -static void print_warning(struct dquot *dquot, int flag, const char *fmtstr) -{ +/* Values of warnings */ +#define NOWARN 0 +#define IHARDWARN 1 +#define ISOFTLONGWARN 2 +#define ISOFTWARN 3 +#define BHARDWARN 4 +#define BSOFTLONGWARN 5 +#define BSOFTWARN 6 + +/* Print warning to user which exceeded quota */ +static void print_warning(struct dquot *dquot, const char warntype) +{ + char *msg = NULL; + int flag = (warntype == BHARDWARN || warntype == BSOFTLONGWARN) ? DQ_BLKS : + ((warntype == IHARDWARN || warntype == ISOFTLONGWARN) ? DQ_INODES : 0); + if (!need_print_warning(dquot, flag)) return; - sprintf(quotamessage, fmtstr, - bdevname(dquot->dq_sb->s_dev), quotatypes[dquot->dq_type]); - tty_write_message(current->tty, quotamessage); dquot->dq_flags |= flag; + tty_write_message(current->tty, (char *)bdevname(dquot->dq_sb->s_dev)); + if (warntype == ISOFTWARN || warntype == BSOFTWARN) + tty_write_message(current->tty, ": warning, "); + else + tty_write_message(current->tty, ": write failed, "); + tty_write_message(current->tty, quotatypes[dquot->dq_type]); + switch (warntype) { + case IHARDWARN: + msg = " file limit reached.\n"; + break; + case ISOFTLONGWARN: + msg = " file quota exceeded too long.\n"; + break; + case ISOFTWARN: + msg = " file quota exceeded.\n"; + break; + case BHARDWARN: + msg = " block limit reached.\n"; + break; + case BSOFTLONGWARN: + msg = " block quota exceeded too long.\n"; + break; + case BSOFTWARN: + msg = " block quota exceeded.\n"; + break; + } + tty_write_message(current->tty, msg); +} + +static inline void flush_warnings(struct dquot **dquots, char *warntype) +{ + int i; + + for (i = 0; i < MAXQUOTAS; i++) + if (dquots[i] != NODQUOT && warntype[i] != NOWARN) + print_warning(dquots[i], warntype[i]); } static inline char ignore_hardlimit(struct dquot *dquot) @@ -803,15 +761,16 @@ return capable(CAP_SYS_RESOURCE) && !dquot->dq_sb->s_dquot.rsquash[dquot->dq_type]; } -static int check_idq(struct dquot *dquot, u_long inodes) +static int check_idq(struct dquot *dquot, ulong inodes, char *warntype) { + *warntype = NOWARN; if (inodes <= 0 || dquot->dq_flags & DQ_FAKE) return QUOTA_OK; if (dquot->dq_ihardlimit && (dquot->dq_curinodes + inodes) > dquot->dq_ihardlimit && !ignore_hardlimit(dquot)) { - print_warning(dquot, DQ_INODES, "%s: write failed, %s file limit reached\n"); + *warntype = IHARDWARN; return NO_QUOTA; } @@ -819,22 +778,23 @@ (dquot->dq_curinodes + inodes) > dquot->dq_isoftlimit && dquot->dq_itime && CURRENT_TIME >= dquot->dq_itime && !ignore_hardlimit(dquot)) { - print_warning(dquot, DQ_INODES, "%s: warning, %s file quota exceeded too long.\n"); + *warntype = ISOFTLONGWARN; return NO_QUOTA; } if (dquot->dq_isoftlimit && (dquot->dq_curinodes + inodes) > dquot->dq_isoftlimit && dquot->dq_itime == 0) { - print_warning(dquot, 0, "%s: warning, %s file quota exceeded\n"); + *warntype = ISOFTWARN; dquot->dq_itime = CURRENT_TIME + dquot->dq_sb->s_dquot.inode_expire[dquot->dq_type]; } return QUOTA_OK; } -static int check_bdq(struct dquot *dquot, u_long blocks, char prealloc) +static int check_bdq(struct dquot *dquot, ulong blocks, char prealloc, char *warntype) { + *warntype = 0; if (blocks <= 0 || dquot->dq_flags & DQ_FAKE) return QUOTA_OK; @@ -842,7 +802,7 @@ (dquot->dq_curblocks + blocks) > dquot->dq_bhardlimit && !ignore_hardlimit(dquot)) { if (!prealloc) - print_warning(dquot, DQ_BLKS, "%s: write failed, %s disk limit reached.\n"); + *warntype = BHARDWARN; return NO_QUOTA; } @@ -851,7 +811,7 @@ dquot->dq_btime && CURRENT_TIME >= dquot->dq_btime && !ignore_hardlimit(dquot)) { if (!prealloc) - print_warning(dquot, DQ_BLKS, "%s: write failed, %s disk quota exceeded too long.\n"); + *warntype = BSOFTLONGWARN; return NO_QUOTA; } @@ -859,7 +819,7 @@ (dquot->dq_curblocks + blocks) > dquot->dq_bsoftlimit && dquot->dq_btime == 0) { if (!prealloc) { - print_warning(dquot, 0, "%s: warning, %s disk quota exceeded\n"); + *warntype = BSOFTWARN; dquot->dq_btime = CURRENT_TIME + dquot->dq_sb->s_dquot.block_expire[dquot->dq_type]; } else @@ -883,18 +843,11 @@ int error = -EFAULT; struct dqblk dq_dqblk; - if (dqblk == (struct dqblk *)NULL) + if (copy_from_user(&dq_dqblk, dqblk, sizeof(struct dqblk))) return error; - if (flags & QUOTA_SYSCALL) { - if (copy_from_user(&dq_dqblk, dqblk, sizeof(struct dqblk))) - return(error); - } else - memcpy((caddr_t)&dq_dqblk, (caddr_t)dqblk, sizeof(struct dqblk)); - if (sb && (dquot = dqget(sb, id, type)) != NODQUOT) { - lock_dquot(dquot); - + /* We can't block while changing quota structure... */ if (id > 0 && ((flags & SET_QUOTA) || (flags & SET_QLIMIT))) { dquot->dq_bhardlimit = dq_dqblk.dqb_bhardlimit; dquot->dq_bsoftlimit = dq_dqblk.dqb_bsoftlimit; @@ -931,15 +884,15 @@ dquot->dq_flags &= ~DQ_FAKE; dquot->dq_flags |= DQ_MOD; - unlock_dquot(dquot); dqput(dquot); } - return(0); + return 0; } static int get_quota(struct super_block *sb, int id, short type, struct dqblk *dqblk) { struct dquot *dquot; + struct dqblk data; int error = -ESRCH; if (!sb || !sb_has_quota_enabled(sb, type)) @@ -948,12 +901,11 @@ if (dquot == NODQUOT) goto out; - lock_dquot(dquot); /* We must protect against invalidating the quota */ + memcpy(&data, &dquot->dq_dqb, sizeof(struct dqblk)); /* We copy data to preserve them from changing */ + dqput(dquot); error = -EFAULT; - if (dqblk && !copy_to_user(dqblk, &dquot->dq_dqb, sizeof(struct dqblk))) + if (dqblk && !copy_to_user(dqblk, &data, sizeof(struct dqblk))) error = 0; - unlock_dquot(dquot); - dqput(dquot); out: return error; } @@ -988,6 +940,7 @@ return error; } +#if 0 /* We are not going to support filesystems without i_blocks... */ /* * This is a simple algorithm that calculates the size of a file in blocks. * This is only used on filesystems that do not have an i_blocks count. @@ -1011,6 +964,7 @@ } return blocks; } +#endif /* * Externally referenced functions through dquot_operations in inode. @@ -1019,26 +973,23 @@ */ void dquot_initialize(struct inode *inode, short type) { - struct dquot *dquot; + struct dquot *dquot[MAXQUOTAS]; unsigned int id = 0; short cnt; if (!S_ISREG(inode->i_mode) && !S_ISDIR(inode->i_mode) && !S_ISLNK(inode->i_mode)) return; - lock_kernel(); /* We don't want to have quotas on quota files - nasty deadlocks possible */ - if (is_quotafile(inode)) { - unlock_kernel(); + if (is_quotafile(inode)) return; - } + /* Build list of quotas to initialize... We can block here */ for (cnt = 0; cnt < MAXQUOTAS; cnt++) { + dquot[cnt] = NODQUOT; if (type != -1 && cnt != type) continue; - if (!sb_has_quota_enabled(inode->i_sb, cnt)) continue; - if (inode->i_dquot[cnt] == NODQUOT) { switch (cnt) { case USRQUOTA: @@ -1048,18 +999,22 @@ id = inode->i_gid; break; } - dquot = dqget(inode->i_sb, id, cnt); - if (dquot == NODQUOT) - continue; - if (inode->i_dquot[cnt] != NODQUOT) { - dqput(dquot); - continue; - } - inode->i_dquot[cnt] = dquot; - inode->i_flags |= S_QUOTA; + dquot[cnt] = dqget(inode->i_sb, id, cnt); } } - unlock_kernel(); + /* NOBLOCK START: Here we shouldn't block */ + for (cnt = 0; cnt < MAXQUOTAS; cnt++) { + if (dquot[cnt] == NODQUOT || !sb_has_quota_enabled(inode->i_sb, cnt) || inode->i_dquot[cnt] != NODQUOT) + continue; + inode->i_dquot[cnt] = dquot[cnt]; + dquot[cnt] = NODQUOT; + inode->i_flags |= S_QUOTA; + } + /* NOBLOCK END */ + /* Put quotas which we didn't use */ + for (cnt = 0; cnt < MAXQUOTAS; cnt++) + if (dquot[cnt] != NODQUOT) + dqput(dquot[cnt]); } /* @@ -1085,96 +1040,97 @@ } /* - * Note: this is a blocking operation. + * This operation can block, but only after everything is updated */ -int dquot_alloc_block(const struct inode *inode, unsigned long number, char warn) +int dquot_alloc_block(struct inode *inode, unsigned long number, char warn) { - int cnt; + int cnt, ret = NO_QUOTA; struct dquot *dquot[MAXQUOTAS]; + char warntype[MAXQUOTAS]; for (cnt = 0; cnt < MAXQUOTAS; cnt++) { + dquot[cnt] = NODQUOT; + warntype[cnt] = NOWARN; + } + for (cnt = 0; cnt < MAXQUOTAS; cnt++) { dquot[cnt] = dqduplicate(inode->i_dquot[cnt]); if (dquot[cnt] == NODQUOT) continue; - lock_dquot(dquot[cnt]); - if (check_bdq(dquot[cnt], number, warn)) - goto put_all; + if (check_bdq(dquot[cnt], number, warn, warntype+cnt) == NO_QUOTA) + goto warn_put_all; } - for (cnt = 0; cnt < MAXQUOTAS; cnt++) { if (dquot[cnt] == NODQUOT) continue; dquot_incr_blocks(dquot[cnt], number); - unlock_dquot(dquot[cnt]); - dqput(dquot[cnt]); } - - return QUOTA_OK; -put_all: - for (; cnt >= 0; cnt--) { - if (dquot[cnt] == NODQUOT) - continue; - unlock_dquot(dquot[cnt]); - dqput(dquot[cnt]); - } - return NO_QUOTA; + inode->i_blocks += number << (BLOCK_SIZE_BITS - 9); + mark_inode_dirty(inode); + ret = QUOTA_OK; +warn_put_all: + flush_warnings(dquot, warntype); + for (cnt = 0; cnt < MAXQUOTAS; cnt++) + if (dquot[cnt] != NODQUOT) + dqput(dquot[cnt]); + return ret; } /* - * Note: this is a blocking operation. + * This operation can block, but only after everything is updated */ int dquot_alloc_inode(const struct inode *inode, unsigned long number) { - int cnt; + int cnt, ret = NO_QUOTA; struct dquot *dquot[MAXQUOTAS]; + char warntype[MAXQUOTAS]; for (cnt = 0; cnt < MAXQUOTAS; cnt++) { + dquot[cnt] = NODQUOT; + warntype[cnt] = NOWARN; + } + for (cnt = 0; cnt < MAXQUOTAS; cnt++) { dquot[cnt] = dqduplicate(inode -> i_dquot[cnt]); if (dquot[cnt] == NODQUOT) continue; - lock_dquot(dquot[cnt]); - if (check_idq(dquot[cnt], number)) - goto put_all; + if (check_idq(dquot[cnt], number, warntype+cnt) == NO_QUOTA) + goto warn_put_all; } for (cnt = 0; cnt < MAXQUOTAS; cnt++) { if (dquot[cnt] == NODQUOT) continue; dquot_incr_inodes(dquot[cnt], number); - unlock_dquot(dquot[cnt]); - dqput(dquot[cnt]); } - - return QUOTA_OK; -put_all: - for (; cnt >= 0; cnt--) { - if (dquot[cnt] == NODQUOT) - continue; - unlock_dquot(dquot[cnt]); - dqput(dquot[cnt]); - } - return NO_QUOTA; + ret = QUOTA_OK; +warn_put_all: + flush_warnings(dquot, warntype); + for (cnt = 0; cnt < MAXQUOTAS; cnt++) + if (dquot[cnt] != NODQUOT) + dqput(dquot[cnt]); + return ret; } /* - * Note: this is a blocking operation. + * This is a non-blocking operation. */ -void dquot_free_block(const struct inode *inode, unsigned long number) +void dquot_free_block(struct inode *inode, unsigned long number) { unsigned short cnt; struct dquot *dquot; for (cnt = 0; cnt < MAXQUOTAS; cnt++) { - dquot = inode->i_dquot[cnt]; + dquot = dqduplicate(inode->i_dquot[cnt]); if (dquot == NODQUOT) continue; - wait_on_dquot(dquot); dquot_decr_blocks(dquot, number); + dqput(dquot); } + inode->i_blocks -= number << (BLOCK_SIZE_BITS - 9); + mark_inode_dirty(inode); } /* - * Note: this is a blocking operation. + * This is a non-blocking operation. */ void dquot_free_inode(const struct inode *inode, unsigned long number) { @@ -1182,128 +1138,73 @@ struct dquot *dquot; for (cnt = 0; cnt < MAXQUOTAS; cnt++) { - dquot = inode->i_dquot[cnt]; + dquot = dqduplicate(inode->i_dquot[cnt]); if (dquot == NODQUOT) continue; - wait_on_dquot(dquot); dquot_decr_inodes(dquot, number); + dqput(dquot); } } /* * Transfer the number of inode and blocks from one diskquota to an other. * - * Note: this is a blocking operation. + * This operation can block, but only after everything is updated */ -int dquot_transfer(struct dentry *dentry, struct iattr *iattr) +int dquot_transfer(struct inode *inode, struct iattr *iattr) { - struct inode *inode = dentry -> d_inode; unsigned long blocks; struct dquot *transfer_from[MAXQUOTAS]; struct dquot *transfer_to[MAXQUOTAS]; - short cnt, disc; - int error = -EDQUOT; + int cnt, ret = NO_QUOTA, chuid = (iattr->ia_valid & ATTR_UID) && inode->i_uid != iattr->ia_uid, + chgid = (iattr->ia_valid & ATTR_GID) && inode->i_gid != iattr->ia_gid; + char warntype[MAXQUOTAS]; - if (!inode) - return -ENOENT; - /* Arguably we could consider that as error, but... no fs - no quota */ - if (!inode->i_sb) - return 0; - - lock_kernel(); - /* - * Build the transfer_from and transfer_to lists and check quotas to see - * if operation is permitted. - */ + /* Clear the arrays */ + for (cnt = 0; cnt < MAXQUOTAS; cnt++) { + transfer_to[cnt] = transfer_from[cnt] = NODQUOT; + warntype[cnt] = NOWARN; + } + /* First build the transfer_to list - here we can block on reading of dquots... */ for (cnt = 0; cnt < MAXQUOTAS; cnt++) { - transfer_from[cnt] = NODQUOT; - transfer_to[cnt] = NODQUOT; - if (!sb_has_quota_enabled(inode->i_sb, cnt)) continue; - switch (cnt) { case USRQUOTA: - if (inode->i_uid == iattr->ia_uid) + if (!chuid) continue; - /* We can get transfer_from from inode, can't we? */ - transfer_from[cnt] = dqget(inode->i_sb, inode->i_uid, cnt); transfer_to[cnt] = dqget(inode->i_sb, iattr->ia_uid, cnt); break; case GRPQUOTA: - if (inode->i_gid == iattr->ia_gid) + if (!chgid) continue; - transfer_from[cnt] = dqget(inode->i_sb, inode->i_gid, cnt); transfer_to[cnt] = dqget(inode->i_sb, iattr->ia_gid, cnt); break; } - - /* Something bad (eg. quotaoff) happened while we were sleeping? */ - if (transfer_from[cnt] == NODQUOT || transfer_to[cnt] == NODQUOT) - { - if (transfer_from[cnt] != NODQUOT) { - dqput(transfer_from[cnt]); - transfer_from[cnt] = NODQUOT; - } - if (transfer_to[cnt] != NODQUOT) { - dqput(transfer_to[cnt]); - transfer_to[cnt] = NODQUOT; - } - continue; - } - /* - * We have to lock the quotas to prevent races... - */ - if (transfer_from[cnt] < transfer_to[cnt]) - { - lock_dquot(transfer_from[cnt]); - lock_dquot(transfer_to[cnt]); - } - else - { - lock_dquot(transfer_to[cnt]); - lock_dquot(transfer_from[cnt]); - } - - /* - * The entries might got invalidated while locking. The second - * dqget() could block and so the first structure might got - * invalidated or locked... - */ - if (!transfer_to[cnt]->dq_sb || !transfer_from[cnt]->dq_sb) { - cnt++; - goto put_all; - } } - - /* - * Find out if this filesystem uses i_blocks. - */ - if (!inode->i_sb->s_blocksize) - blocks = isize_to_blocks(inode->i_size, BLOCK_SIZE_BITS); - else - blocks = (inode->i_blocks >> 1); + /* NOBLOCK START: From now on we shouldn't block */ + blocks = (inode->i_blocks >> 1); + /* Build the transfer_from list and check the limits */ for (cnt = 0; cnt < MAXQUOTAS; cnt++) { - if (!transfer_to[cnt]) + /* The second test can fail when quotaoff is in progress... */ + if (transfer_to[cnt] == NODQUOT || !sb_has_quota_enabled(inode->i_sb, cnt)) continue; - if (check_idq(transfer_to[cnt], 1) == NO_QUOTA || - check_bdq(transfer_to[cnt], blocks, 0) == NO_QUOTA) { - cnt = MAXQUOTAS; - goto put_all; - } + transfer_from[cnt] = dqduplicate(inode->i_dquot[cnt]); + if (transfer_from[cnt] == NODQUOT) /* Can happen on quotafiles (quota isn't initialized on them)... */ + continue; + if (check_idq(transfer_to[cnt], 1, warntype+cnt) == NO_QUOTA || + check_bdq(transfer_to[cnt], blocks, 0, warntype+cnt) == NO_QUOTA) + goto warn_put_all; } - if ((error = notify_change(dentry, iattr))) - goto put_all; /* - * Finally perform the needed transfer from transfer_from to transfer_to, - * and release any pointers to dquots not needed anymore. + * Finally perform the needed transfer from transfer_from to transfer_to */ for (cnt = 0; cnt < MAXQUOTAS; cnt++) { /* * Skip changes for same uid or gid or for non-existing quota-type. */ - if (transfer_from[cnt] == NODQUOT && transfer_to[cnt] == NODQUOT) + if (transfer_from[cnt] == NODQUOT || transfer_to[cnt] == NODQUOT) continue; dquot_decr_inodes(transfer_from[cnt], 1); @@ -1312,43 +1213,35 @@ dquot_incr_inodes(transfer_to[cnt], 1); dquot_incr_blocks(transfer_to[cnt], blocks); - unlock_dquot(transfer_from[cnt]); - dqput(transfer_from[cnt]); - if (inode->i_dquot[cnt] != NODQUOT) { - struct dquot *temp = inode->i_dquot[cnt]; - inode->i_dquot[cnt] = transfer_to[cnt]; - unlock_dquot(transfer_to[cnt]); - dqput(temp); - } else { - unlock_dquot(transfer_to[cnt]); - dqput(transfer_to[cnt]); - } + if (inode->i_dquot[cnt] == NODQUOT) + BUG(); + inode->i_dquot[cnt] = transfer_to[cnt]; + /* + * We've got to release transfer_from[] twice - once for dquot_transfer() and + * once for inode. We don't want to release transfer_to[] as it's now placed in inode + */ + transfer_to[cnt] = transfer_from[cnt]; } - - unlock_kernel(); - return 0; -put_all: - for (disc = 0; disc < cnt; disc++) { - /* There should be none or both pointers set but... */ - if (transfer_to[disc] != NODQUOT) { - unlock_dquot(transfer_to[disc]); - dqput(transfer_to[disc]); - } - if (transfer_from[disc] != NODQUOT) { - unlock_dquot(transfer_from[disc]); - dqput(transfer_from[disc]); - } + /* NOBLOCK END. From now on we can block as we wish */ + ret = QUOTA_OK; +warn_put_all: + flush_warnings(transfer_to, warntype); + for (cnt = 0; cnt < MAXQUOTAS; cnt++) { + if (transfer_to[cnt] != NODQUOT) + dqput(transfer_to[cnt]); + if (transfer_from[cnt] != NODQUOT) + dqput(transfer_from[cnt]); } - unlock_kernel(); - return error; + return ret; } - void __init dquot_init_hash(void) { - printk(KERN_NOTICE "VFS: Diskquotas version %s initialized\n", __DQUOT_VERSION__); + int i; - memset(dquot_hash, 0, sizeof(dquot_hash)); + printk(KERN_NOTICE "VFS: Diskquotas version %s initialized\n", __DQUOT_VERSION__); + for (i = 0; i < NR_DQHASH; i++) + INIT_LIST_HEAD(dquot_hash + i); memset((caddr_t)&dqstats, 0, sizeof(dqstats)); } @@ -1399,7 +1292,6 @@ { struct file *filp; short cnt; - int enabled = 0; struct quota_mount_options *dqopt = sb_dqopt(sb); if (!sb) @@ -1418,27 +1310,15 @@ remove_dquot_ref(sb->s_dev, cnt); invalidate_dquots(sb->s_dev, cnt); - /* Wait for any pending IO - remove me as soon as invalidate is more polite */ - down(&dqopt->dqio_sem); filp = dqopt->files[cnt]; dqopt->files[cnt] = (struct file *)NULL; dqopt->inode_expire[cnt] = 0; dqopt->block_expire[cnt] = 0; - up(&dqopt->dqio_sem); fput(filp); } - - /* - * Check whether any quota is still enabled, - * and if not clear the dq_op pointer. - */ - for (cnt = 0; cnt < MAXQUOTAS; cnt++) - enabled |= is_enabled(dqopt, cnt); - if (!enabled) - sb->dq_op = NULL; up(&dqopt->dqoff_sem); out: - return(0); + return 0; } static inline int check_quotafile_size(loff_t size) @@ -1483,10 +1363,10 @@ error = -EINVAL; if (inode->i_size == 0 || !check_quotafile_size(inode->i_size)) goto out_f; - dquot_drop(inode); /* We don't want quota on quota files */ + dquot_drop(inode); /* We don't want quota on quota files - open might initialize the other quota type... */ - set_enable_flags(dqopt, type); dqopt->files[type] = f; + set_enable_flags(dqopt, type); dquot = dqget(sb, 0, type); dqopt->inode_expire[type] = (dquot != NODQUOT) ? dquot->dq_itime : MAX_IQ_TIME; @@ -1537,11 +1417,11 @@ case Q_GETQUOTA: if (((type == USRQUOTA && current->euid != id) || (type == GRPQUOTA && in_egroup_p(id))) && - !capable(CAP_SYS_RESOURCE)) + !capable(CAP_SYS_ADMIN)) goto out; break; default: - if (!capable(CAP_SYS_RESOURCE)) + if (!capable(CAP_SYS_ADMIN)) goto out; } @@ -1562,13 +1442,16 @@ ret = -ENOTBLK; if (!S_ISBLK(mode)) goto out; + ret = -ENODEV; sb = get_super(dev); + if (!sb) + goto out; } ret = -EINVAL; switch (cmds) { case Q_QUOTAON: - ret = sb ? quota_on(sb, type, (char *) addr) : -ENODEV; + ret = quota_on(sb, type, (char *) addr); goto out; case Q_QUOTAOFF: ret = quota_off(sb, type); @@ -1598,9 +1481,7 @@ goto out; } - flags |= QUOTA_SYSCALL; - - ret = -ESRCH; + ret = -NODEV; if (sb && sb_has_quota_enabled(sb, type)) ret = set_dqblk(sb, id, type, flags, (struct dqblk *) addr); out: diff -u -r -x *.orig -x *.o linux-2.4.0-test11/fs/ext2/balloc.c linux-2.4.0-test11-quotafix/fs/ext2/balloc.c --- linux-2.4.0-test11/fs/ext2/balloc.c Fri Oct 6 00:18:14 2000 +++ linux-2.4.0-test11-quotafix/fs/ext2/balloc.c Mon Nov 27 09:43:43 2000 @@ -247,7 +247,8 @@ return slot; } -void ext2_free_blocks (const struct inode * inode, unsigned long block, +/* Free given blocks, update quota and i_blocks field */ +void ext2_free_blocks (struct inode * inode, unsigned long block, unsigned long count) { struct buffer_head * bh; @@ -318,7 +319,7 @@ "bit already cleared for block %lu", block); else { - DQUOT_FREE_BLOCK(sb, inode, 1); + DQUOT_FREE_BLOCK(inode, 1); gdp->bg_free_blocks_count = cpu_to_le16(le16_to_cpu(gdp->bg_free_blocks_count)+1); es->s_free_blocks_count = @@ -351,8 +352,9 @@ * is allocated. Otherwise a forward search is made for a free block; within * each block group the search first looks for an entire free byte in the block * bitmap, and then for any free bit if that fails. + * This function also updates quota and i_blocks field. */ -int ext2_new_block (const struct inode * inode, unsigned long goal, +int ext2_new_block (struct inode * inode, unsigned long goal, u32 * prealloc_count, u32 * prealloc_block, int * err) { struct buffer_head * bh; @@ -513,7 +515,7 @@ /* * Check quota for allocation of this block. */ - if(DQUOT_ALLOC_BLOCK(sb, inode, 1)) { + if(DQUOT_ALLOC_BLOCK(inode, 1)) { *err = -EDQUOT; goto out; } @@ -531,7 +533,7 @@ if (ext2_set_bit (j, bh->b_data)) { ext2_warning (sb, "ext2_new_block", "bit already set for block %d", j); - DQUOT_FREE_BLOCK(sb, inode, 1); + DQUOT_FREE_BLOCK(inode, 1); goto repeat; } @@ -554,13 +556,13 @@ for (k = 1; k < prealloc_goal && (j + k) < EXT2_BLOCKS_PER_GROUP(sb); k++, next_block++) { - if (DQUOT_PREALLOC_BLOCK(sb, inode, 1)) + if (DQUOT_PREALLOC_BLOCK(inode, 1)) break; /* Writer: ->i_prealloc* */ if (*prealloc_block + *prealloc_count != next_block || ext2_set_bit (j + k, bh->b_data)) { /* Writer: end */ - DQUOT_FREE_BLOCK(sb, inode, 1); + DQUOT_FREE_BLOCK(inode, 1); break; } (*prealloc_count)++; diff -u -r -x *.orig -x *.o linux-2.4.0-test11/fs/ext2/ialloc.c linux-2.4.0-test11-quotafix/fs/ext2/ialloc.c --- linux-2.4.0-test11/fs/ext2/ialloc.c Sun Nov 26 22:52:32 2000 +++ linux-2.4.0-test11-quotafix/fs/ext2/ialloc.c Mon Nov 27 09:43:43 2000 @@ -194,7 +194,7 @@ * Note: we must free any quota before locking the superblock, * as writing the quota to disk may need the lock as well. */ - DQUOT_FREE_INODE(sb, inode); + DQUOT_FREE_INODE(inode); DQUOT_DROP(inode); lock_super (sb); @@ -460,8 +460,8 @@ mark_inode_dirty(inode); unlock_super (sb); - if(DQUOT_ALLOC_INODE(sb, inode)) { - sb->dq_op->drop(inode); + if(DQUOT_ALLOC_INODE(inode)) { + DQUOT_DROP(inode); inode->i_nlink = 0; iput(inode); *err = -EDQUOT; diff -u -r -x *.orig -x *.o linux-2.4.0-test11/fs/ext2/inode.c linux-2.4.0-test11-quotafix/fs/ext2/inode.c --- linux-2.4.0-test11/fs/ext2/inode.c Fri Oct 6 00:18:14 2000 +++ linux-2.4.0-test11-quotafix/fs/ext2/inode.c Mon Nov 27 09:43:43 2000 @@ -28,6 +28,7 @@ #include #include #include +#include static int ext2_update_inode(struct inode * inode, int do_sync); @@ -449,7 +450,7 @@ /* Verify that place we are splicing to is still there and vacant */ - /* Writer: pointers, ->i_next_alloc*, ->i_blocks */ + /* Writer: pointers, ->i_next_alloc* */ if (!verify_chain(chain, where-1) || *where->p) /* Writer: end */ goto changed; @@ -459,7 +460,6 @@ *where->p = where->key; inode->u.ext2_i.i_next_alloc_block = block; inode->u.ext2_i.i_next_alloc_goal = le32_to_cpu(where[num-1].key); - inode->i_blocks += num * inode->i_sb->s_blocksize/512; /* Writer: end */ @@ -782,7 +782,6 @@ */ static inline void ext2_free_data(struct inode *inode, u32 *p, u32 *q) { - int blocks = inode->i_sb->s_blocksize / 512; unsigned long block_to_free = 0, count = 0; unsigned long nr; @@ -796,9 +795,6 @@ else if (block_to_free == nr - count) count++; else { - /* Writer: ->i_blocks */ - inode->i_blocks -= blocks * count; - /* Writer: end */ ext2_free_blocks (inode, block_to_free, count); mark_inode_dirty(inode); free_this: @@ -808,9 +804,6 @@ } } if (count > 0) { - /* Writer: ->i_blocks */ - inode->i_blocks -= blocks * count; - /* Writer: end */ ext2_free_blocks (inode, block_to_free, count); mark_inode_dirty(inode); } @@ -855,9 +848,6 @@ (u32*)bh->b_data + addr_per_block, depth); bforget(bh); - /* Writer: ->i_blocks */ - inode->i_blocks -= inode->i_sb->s_blocksize / 512; - /* Writer: end */ ext2_free_blocks(inode, nr, 1); mark_inode_dirty(inode); } @@ -1253,7 +1243,9 @@ goto out; retval = inode_change_ok(inode, iattr); - if (retval != 0) + if (retval != 0 || (((iattr->ia_valid & ATTR_UID && iattr->ia_uid != inode->i_uid) || + (iattr->ia_valid & ATTR_GID && iattr->ia_gid != inode->i_gid)) && + DQUOT_TRANSFER(inode, iattr))) goto out; inode_setattr(inode, iattr); diff -u -r -x *.orig -x *.o linux-2.4.0-test11/fs/nfsd/vfs.c linux-2.4.0-test11-quotafix/fs/nfsd/vfs.c --- linux-2.4.0-test11/fs/nfsd/vfs.c Sun Nov 26 22:52:32 2000 +++ linux-2.4.0-test11-quotafix/fs/nfsd/vfs.c Mon Nov 27 09:43:43 2000 @@ -293,27 +293,11 @@ iap->ia_valid |= ATTR_CTIME; -#ifdef CONFIG_QUOTA - /* DQUOT_TRANSFER needs both ia_uid and ia_gid defined */ - if (iap->ia_valid & (ATTR_UID|ATTR_GID)) { - if (! (iap->ia_valid & ATTR_UID)) - iap->ia_uid = inode->i_uid; - if (! (iap->ia_valid & ATTR_GID)) - iap->ia_gid = inode->i_gid; - iap->ia_valid |= ATTR_UID|ATTR_GID; - } -#endif /* CONFIG_QUOTA */ - if (iap->ia_valid & ATTR_SIZE) { fh_lock(fhp); size_change = 1; } -#ifdef CONFIG_QUOTA - if (iap->ia_valid & (ATTR_UID|ATTR_GID)) - err = DQUOT_TRANSFER(dentry, iap); - else -#endif - err = notify_change(dentry, iap); + err = notify_change(dentry, iap); if (size_change) { fh_unlock(fhp); put_write_access(inode); diff -u -r -x *.orig -x *.o linux-2.4.0-test11/fs/open.c linux-2.4.0-test11-quotafix/fs/open.c --- linux-2.4.0-test11/fs/open.c Fri Nov 3 22:07:22 2000 +++ linux-2.4.0-test11-quotafix/fs/open.c Mon Nov 27 09:43:43 2000 @@ -552,7 +552,7 @@ newattrs.ia_mode &= ~S_ISGID; newattrs.ia_valid |= ATTR_MODE; } - error = DQUOT_TRANSFER(dentry, &newattrs); + error = notify_change(dentry, &newattrs); out: return error; } diff -u -r -x *.orig -x *.o linux-2.4.0-test11/fs/udf/balloc.c linux-2.4.0-test11-quotafix/fs/udf/balloc.c --- linux-2.4.0-test11/fs/udf/balloc.c Fri Oct 6 00:11:00 2000 +++ linux-2.4.0-test11-quotafix/fs/udf/balloc.c Mon Nov 27 09:43:43 2000 @@ -260,7 +260,7 @@ } else { - DQUOT_FREE_BLOCK(sb, inode, 1); + DQUOT_FREE_BLOCK(inode, 1); if (UDF_SB_LVIDBH(sb)) { UDF_SB_LVID(sb)->freeSpaceTable[UDF_SB_PARTITION(sb)] = @@ -321,12 +321,12 @@ { if (!udf_test_bit(bit, bh->b_data)) goto out; - else if (DQUOT_PREALLOC_BLOCK(sb, inode, 1)) + else if (DQUOT_PREALLOC_BLOCK(inode, 1)) goto out; else if (!udf_clear_bit(bit, bh->b_data)) { udf_debug("bit already cleared for block %d\n", bit); - DQUOT_FREE_BLOCK(sb, inode, 1); + DQUOT_FREE_BLOCK(inode, 1); goto out; } block_count --; @@ -461,7 +461,7 @@ /* * Check quota for allocation of this block. */ - if (DQUOT_ALLOC_BLOCK(sb, inode, 1)) + if (DQUOT_ALLOC_BLOCK(inode, 1)) { unlock_super(sb); *err = -EDQUOT; diff -u -r -x *.orig -x *.o linux-2.4.0-test11/fs/udf/ialloc.c linux-2.4.0-test11-quotafix/fs/udf/ialloc.c --- linux-2.4.0-test11/fs/udf/ialloc.c Sun Nov 26 22:52:33 2000 +++ linux-2.4.0-test11-quotafix/fs/udf/ialloc.c Mon Nov 27 09:43:43 2000 @@ -44,7 +44,7 @@ * Note: we must free any quota before locking the superblock, * as writing the quota to disk may need the lock as well. */ - DQUOT_FREE_INODE(sb, inode); + DQUOT_FREE_INODE(inode); DQUOT_DROP(inode); lock_super(sb); @@ -149,9 +149,9 @@ mark_inode_dirty(inode); unlock_super(sb); - if (DQUOT_ALLOC_INODE(sb, inode)) + if (DQUOT_ALLOC_INODE(inode)) { - sb->dq_op->drop(inode); + DQUOT_DROP(inode); inode->i_nlink = 0; iput(inode); *err = -EDQUOT; diff -u -r -x *.orig -x *.o linux-2.4.0-test11/fs/ufs/balloc.c linux-2.4.0-test11-quotafix/fs/ufs/balloc.c --- linux-2.4.0-test11/fs/ufs/balloc.c Fri Oct 6 00:10:58 2000 +++ linux-2.4.0-test11-quotafix/fs/ufs/balloc.c Mon Nov 27 09:43:43 2000 @@ -85,7 +85,7 @@ "bit already cleared for fragment %u", i); } - DQUOT_FREE_BLOCK (sb, inode, count); + DQUOT_FREE_BLOCK (inode, count); ADD_SWAB32(ucg->cg_cs.cs_nffree, count); ADD_SWAB32(usb1->fs_cstotal.cs_nffree, count); ADD_SWAB32(sb->fs_cs(cgno).cs_nffree, count); @@ -187,7 +187,7 @@ ubh_setblock(UCPI_UBH, ucpi->c_freeoff, blkno); if ((sb->u.ufs_sb.s_flags & UFS_CG_MASK) == UFS_CG_44BSD) ufs_clusteracct (sb, ucpi, blkno, 1); - DQUOT_FREE_BLOCK(sb, inode, uspi->s_fpb); + DQUOT_FREE_BLOCK(inode, uspi->s_fpb); INC_SWAB32(ucg->cg_cs.cs_nbfree); INC_SWAB32(usb1->fs_cstotal.cs_nbfree); INC_SWAB32(sb->fs_cs(cgno).cs_nbfree); @@ -450,7 +450,7 @@ INC_SWAB32(ucg->cg_frsum[fragsize - count]); for (i = oldcount; i < newcount; i++) ubh_clrbit (UCPI_UBH, ucpi->c_freeoff, fragno + i); - if(DQUOT_ALLOC_BLOCK(sb, inode, count)) { + if(DQUOT_ALLOC_BLOCK(inode, count)) { *err = -EDQUOT; return 0; } @@ -557,7 +557,7 @@ for (i = count; i < uspi->s_fpb; i++) ubh_setbit (UCPI_UBH, ucpi->c_freeoff, goal + i); i = uspi->s_fpb - count; - DQUOT_FREE_BLOCK(sb, inode, i); + DQUOT_FREE_BLOCK(inode, i); ADD_SWAB32(ucg->cg_cs.cs_nffree, i); ADD_SWAB32(usb1->fs_cstotal.cs_nffree, i); ADD_SWAB32(sb->fs_cs(cgno).cs_nffree, i); @@ -568,7 +568,7 @@ result = ufs_bitmap_search (sb, ucpi, goal, allocsize); if (result == (unsigned)-1) return 0; - if(DQUOT_ALLOC_BLOCK(sb, inode, count)) { + if(DQUOT_ALLOC_BLOCK(inode, count)) { *err = -EDQUOT; return 0; } @@ -638,7 +638,7 @@ ubh_clrblock (UCPI_UBH, ucpi->c_freeoff, blkno); if ((sb->u.ufs_sb.s_flags & UFS_CG_MASK) == UFS_CG_44BSD) ufs_clusteracct (sb, ucpi, blkno, -1); - if(DQUOT_ALLOC_BLOCK(sb, inode, uspi->s_fpb)) { + if(DQUOT_ALLOC_BLOCK(inode, uspi->s_fpb)) { *err = -EDQUOT; return (unsigned)-1; } diff -u -r -x *.orig -x *.o linux-2.4.0-test11/fs/ufs/ialloc.c linux-2.4.0-test11-quotafix/fs/ufs/ialloc.c --- linux-2.4.0-test11/fs/ufs/ialloc.c Sun Nov 26 22:52:33 2000 +++ linux-2.4.0-test11-quotafix/fs/ufs/ialloc.c Mon Nov 27 09:43:43 2000 @@ -100,7 +100,7 @@ is_directory = S_ISDIR(inode->i_mode); - DQUOT_FREE_INODE(sb, inode); + DQUOT_FREE_INODE(inode); DQUOT_DROP(inode); clear_inode (inode); @@ -278,8 +278,8 @@ unlock_super (sb); - if(DQUOT_ALLOC_INODE(sb, inode)) { - sb->dq_op->drop(inode); + if(DQUOT_ALLOC_INODE(inode)) { + DQUOT_DROP(inode); inode->i_nlink = 0; iput(inode); *err = -EDQUOT; diff -u -r -x *.orig -x *.o linux-2.4.0-test11/include/linux/dcache.h linux-2.4.0-test11-quotafix/include/linux/dcache.h --- linux-2.4.0-test11/include/linux/dcache.h Sat Nov 4 21:32:16 2000 +++ linux-2.4.0-test11-quotafix/include/linux/dcache.h Mon Nov 27 10:13:33 2000 @@ -170,6 +170,10 @@ extern void shrink_icache_memory(int, int); extern void prune_icache(int); +/* quota cache memory management (defined in linux/fs/dquot.c) */ +extern void shrink_dqcache_memory(int, unsigned int); +extern void prune_dqcache(int); + /* only used at mount-time */ extern struct dentry * d_alloc_root(struct inode *); diff -u -r -x *.orig -x *.o linux-2.4.0-test11/include/linux/ext2_fs.h linux-2.4.0-test11-quotafix/include/linux/ext2_fs.h --- linux-2.4.0-test11/include/linux/ext2_fs.h Sun Oct 15 15:54:34 2000 +++ linux-2.4.0-test11-quotafix/include/linux/ext2_fs.h Mon Nov 27 10:13:50 2000 @@ -525,9 +525,9 @@ /* balloc.c */ extern int ext2_group_sparse(int group); -extern int ext2_new_block (const struct inode *, unsigned long, +extern int ext2_new_block (struct inode *, unsigned long, __u32 *, __u32 *, int *); -extern void ext2_free_blocks (const struct inode *, unsigned long, +extern void ext2_free_blocks (struct inode *, unsigned long, unsigned long); extern unsigned long ext2_count_free_blocks (struct super_block *); extern void ext2_check_blocks_bitmap (struct super_block *); diff -u -r -x *.orig -x *.o linux-2.4.0-test11/include/linux/fs.h linux-2.4.0-test11-quotafix/include/linux/fs.h --- linux-2.4.0-test11/include/linux/fs.h Sun Nov 26 22:52:35 2000 +++ linux-2.4.0-test11-quotafix/include/linux/fs.h Mon Nov 27 09:43:43 2000 @@ -803,11 +803,11 @@ struct dquot_operations { void (*initialize) (struct inode *, short); void (*drop) (struct inode *); - int (*alloc_block) (const struct inode *, unsigned long, char); + int (*alloc_block) (struct inode *, unsigned long, char); int (*alloc_inode) (const struct inode *, unsigned long); - void (*free_block) (const struct inode *, unsigned long); + void (*free_block) (struct inode *, unsigned long); void (*free_inode) (const struct inode *, unsigned long); - int (*transfer) (struct dentry *, struct iattr *); + int (*transfer) (struct inode *, struct iattr *); }; struct file_system_type { diff -u -r -x *.orig -x *.o linux-2.4.0-test11/include/linux/quota.h linux-2.4.0-test11-quotafix/include/linux/quota.h --- linux-2.4.0-test11/include/linux/quota.h Sat Nov 4 21:32:04 2000 +++ linux-2.4.0-test11-quotafix/include/linux/quota.h Mon Nov 27 09:43:43 2000 @@ -81,13 +81,6 @@ #define QUOTAFILENAME "quota" #define QUOTAGROUP "staff" -extern int nr_dquots, nr_free_dquots; -extern int max_dquots; -extern int dquot_root_squash; - -#define NR_DQHASH 43 /* Just an arbitrary number */ -#define NR_DQUOTS 1024 /* Maximum number of quotas active at one time (Configurable from /proc/sys/fs) */ - /* * Command definitions for the 'quotactl' system call. * The commands are broken into a main command defined below @@ -151,26 +144,23 @@ #ifdef __KERNEL__ -/* - * Maximum length of a message generated in the quota system, - * that needs to be kicked onto the tty. - */ -#define MAX_QUOTA_MESSAGE 75 +extern int nr_dquots, nr_free_dquots; +extern int dquot_root_squash; + +#define NR_DQHASH 43 /* Just an arbitrary number */ -#define DQ_LOCKED 0x01 /* locked for update */ -#define DQ_WANT 0x02 /* wanted for update */ -#define DQ_MOD 0x04 /* dquot modified since read */ +#define DQ_LOCKED 0x01 /* dquot under IO */ +#define DQ_MOD 0x02 /* dquot modified since read */ #define DQ_BLKS 0x10 /* uid/gid has been warned about blk limit */ #define DQ_INODES 0x20 /* uid/gid has been warned about inode limit */ #define DQ_FAKE 0x40 /* no limits only usage */ struct dquot { - struct dquot *dq_next; /* Pointer to next dquot */ - struct dquot **dq_pprev; - struct list_head dq_free; /* free list element */ - struct dquot *dq_hash_next; /* Pointer to next in dquot_hash */ - struct dquot **dq_hash_pprev; /* Pointer to previous in dquot_hash */ - wait_queue_head_t dq_wait; /* Pointer to waitqueue */ + struct list_head dq_hash; /* Hash list in memory */ + struct list_head dq_inuse; /* List of all quotas */ + struct list_head dq_free; /* Free list element */ + wait_queue_head_t dq_wait_lock; /* Pointer to waitqueue on dquot lock */ + wait_queue_head_t dq_wait_free; /* Pointer to waitqueue for quota to be unused */ int dq_count; /* Reference count */ /* fields after this point are cleared when invalidating */ @@ -189,7 +179,6 @@ /* * Flags used for set_dqblk. */ -#define QUOTA_SYSCALL 0x01 #define SET_QUOTA 0x02 #define SET_USE 0x04 #define SET_QLIMIT 0x08 diff -u -r -x *.orig -x *.o linux-2.4.0-test11/include/linux/quotaops.h linux-2.4.0-test11-quotafix/include/linux/quotaops.h --- linux-2.4.0-test11/include/linux/quotaops.h Sat Nov 4 21:34:56 2000 +++ linux-2.4.0-test11-quotafix/include/linux/quotaops.h Mon Nov 27 09:43:43 2000 @@ -14,6 +14,7 @@ #if defined(CONFIG_QUOTA) +#include #include /* @@ -25,83 +26,121 @@ extern int quota_off(struct super_block *sb, short type); extern int sync_dquots(kdev_t dev, short type); -extern int dquot_alloc_block(const struct inode *inode, unsigned long number, char prealloc); +extern int dquot_alloc_block(struct inode *inode, unsigned long number, char prealloc); extern int dquot_alloc_inode(const struct inode *inode, unsigned long number); -extern void dquot_free_block(const struct inode *inode, unsigned long number); +extern void dquot_free_block(struct inode *inode, unsigned long number); extern void dquot_free_inode(const struct inode *inode, unsigned long number); -extern int dquot_transfer(struct dentry *dentry, struct iattr *iattr); +extern int dquot_transfer(struct inode *inode, struct iattr *iattr); /* * Operations supported for diskquotas. */ +#define sb_any_quota_enabled(sb) ((sb)->s_dquot.flags & (DQUOT_USR_ENABLED | DQUOT_GRP_ENABLED)) + extern __inline__ void DQUOT_INIT(struct inode *inode) { - if (inode->i_sb && inode->i_sb->dq_op) + if (!inode->i_sb) + BUG(); + lock_kernel(); + if (sb_any_quota_enabled(inode->i_sb)) inode->i_sb->dq_op->initialize(inode, -1); + unlock_kernel(); } extern __inline__ void DQUOT_DROP(struct inode *inode) { - if (IS_QUOTAINIT(inode)) { - if (inode->i_sb && inode->i_sb->dq_op) - inode->i_sb->dq_op->drop(inode); - } + if (!inode->i_sb) + BUG(); + lock_kernel(); + if (IS_QUOTAINIT(inode)) + inode->i_sb->dq_op->drop(inode); /* Ops must be set when there's any quota... */ + unlock_kernel(); } -extern __inline__ int DQUOT_PREALLOC_BLOCK(struct super_block *sb, const struct inode *inode, int nr) -{ - if (sb->dq_op) { - if (sb->dq_op->alloc_block(inode, fs_to_dq_blocks(nr, sb->s_blocksize), 1) == NO_QUOTA) +extern __inline__ int DQUOT_PREALLOC_BLOCK(struct inode *inode, int nr) +{ + lock_kernel(); + if (sb_any_quota_enabled(inode->i_sb)) { + /* Number of used blocks is updated in alloc_block() */ + if (inode->i_sb->dq_op->alloc_block(inode, fs_to_dq_blocks(nr, inode->i_sb->s_blocksize), 1) == NO_QUOTA) { + unlock_kernel(); return 1; + } } + else { + inode->i_blocks += nr << (inode->i_sb->s_blocksize_bits - 9); + mark_inode_dirty(inode); + } + unlock_kernel(); return 0; } -extern __inline__ int DQUOT_ALLOC_BLOCK(struct super_block *sb, const struct inode *inode, int nr) +extern __inline__ int DQUOT_ALLOC_BLOCK(struct inode *inode, int nr) { - if (sb->dq_op) { - if (sb->dq_op->alloc_block(inode, fs_to_dq_blocks(nr, sb->s_blocksize), 0) == NO_QUOTA) + lock_kernel(); + if (sb_any_quota_enabled(inode->i_sb)) { + /* Number of used blocks is updated in alloc_block() */ + if (inode->i_sb->dq_op->alloc_block(inode, fs_to_dq_blocks(nr, inode->i_sb->s_blocksize), 0) == NO_QUOTA) { + unlock_kernel(); return 1; + } + } + else { + inode->i_blocks += nr << (inode->i_sb->s_blocksize_bits - 9); + mark_inode_dirty(inode); } + unlock_kernel(); return 0; } -extern __inline__ int DQUOT_ALLOC_INODE(struct super_block *sb, struct inode *inode) +extern __inline__ int DQUOT_ALLOC_INODE(struct inode *inode) { - if (sb->dq_op) { - sb->dq_op->initialize (inode, -1); - if (sb->dq_op->alloc_inode (inode, 1)) + lock_kernel(); + if (sb_any_quota_enabled(inode->i_sb)) { + inode->i_sb->dq_op->initialize(inode, -1); + if (inode->i_sb->dq_op->alloc_inode(inode, 1) == NO_QUOTA) { + unlock_kernel(); return 1; + } } - inode->i_flags |= S_QUOTA; + unlock_kernel(); return 0; } -extern __inline__ void DQUOT_FREE_BLOCK(struct super_block *sb, const struct inode *inode, int nr) +extern __inline__ void DQUOT_FREE_BLOCK(struct inode *inode, int nr) { - if (sb->dq_op) - sb->dq_op->free_block(inode, fs_to_dq_blocks(nr, sb->s_blocksize)); + lock_kernel(); + if (sb_any_quota_enabled(inode->i_sb)) + inode->i_sb->dq_op->free_block(inode, fs_to_dq_blocks(nr, inode->i_sb->s_blocksize)); + else { + inode->i_blocks -= nr << (inode->i_sb->s_blocksize_bits - 9); + mark_inode_dirty(inode); + } + unlock_kernel(); } -extern __inline__ void DQUOT_FREE_INODE(struct super_block *sb, struct inode *inode) +extern __inline__ void DQUOT_FREE_INODE(struct inode *inode) { - if (sb->dq_op) - sb->dq_op->free_inode(inode, 1); + lock_kernel(); + if (sb_any_quota_enabled(inode->i_sb)) + inode->i_sb->dq_op->free_inode(inode, 1); + unlock_kernel(); } -extern __inline__ int DQUOT_TRANSFER(struct dentry *dentry, struct iattr *iattr) +extern __inline__ int DQUOT_TRANSFER(struct inode *inode, struct iattr *iattr) { - int error = -EDQUOT; - - if (dentry->d_inode->i_sb->dq_op) { - dentry->d_inode->i_sb->dq_op->initialize(dentry->d_inode, -1); - error = dentry->d_inode->i_sb->dq_op->transfer(dentry, iattr); - } else { - error = notify_change(dentry, iattr); + lock_kernel(); + if (sb_any_quota_enabled(inode->i_sb)) { + inode->i_sb->dq_op->initialize(inode, -1); + if (inode->i_sb->dq_op->transfer(inode, iattr) == NO_QUOTA) { + unlock_kernel(); + return 1; + } } - return error; + unlock_kernel(); + return 0; } #define DQUOT_SYNC(dev) sync_dquots(dev, -1) @@ -114,18 +153,36 @@ */ #define DQUOT_INIT(inode) do { } while(0) #define DQUOT_DROP(inode) do { } while(0) -#define DQUOT_PREALLOC_BLOCK(sb, inode, nr) (0) -#define DQUOT_ALLOC_BLOCK(sb, inode, nr) (0) -#define DQUOT_ALLOC_INODE(sb, inode) (0) -#define DQUOT_FREE_BLOCK(sb, inode, nr) do { } while(0) -#define DQUOT_FREE_INODE(sb, inode) do { } while(0) +#define DQUOT_ALLOC_INODE(inode) (0) +#define DQUOT_FREE_INODE(inode) do { } while(0) #define DQUOT_SYNC(dev) do { } while(0) #define DQUOT_OFF(sb) do { } while(0) +#define DQUOT_TRANSFER(inode, iattr) (0) +extern __inline__ int DQUOT_PREALLOC_BLOCK(struct inode *inode, int nr) +{ + lock_kernel(); + inode->i_blocks += nr << (inode->i_sb->s_blocksize_bits - 9); + unlock_kernel(); + mark_inode_dirty(inode); + return 0; +} -/* - * Special case expands to a simple notify_change. - */ -#define DQUOT_TRANSFER(dentry, iattr) notify_change(dentry, iattr) +extern __inline__ int DQUOT_ALLOC_BLOCK(struct inode *inode, int nr) +{ + lock_kernel(); + inode->i_blocks += nr << (inode->i_sb->s_blocksize_bits - 9); + unlock_kernel(); + mark_inode_dirty(inode); + return 0; +} + +extern __inline__ void DQUOT_FREE_BLOCK(struct inode *inode, int nr) +{ + lock_kernel(); + inode->i_blocks -= nr << (inode->i_sb->s_blocksize_bits - 9); + unlock_kernel(); + mark_inode_dirty(inode); +} #endif /* CONFIG_QUOTA */ #endif /* _LINUX_QUOTAOPS_ */ diff -u -r -x *.orig -x *.o linux-2.4.0-test11/kernel/sysctl.c linux-2.4.0-test11-quotafix/kernel/sysctl.c --- linux-2.4.0-test11/kernel/sysctl.c Sun Nov 26 22:52:36 2000 +++ linux-2.4.0-test11-quotafix/kernel/sysctl.c Mon Nov 27 09:43:43 2000 @@ -273,8 +273,6 @@ 0644, NULL, &proc_dointvec}, {FS_NRDQUOT, "dquot-nr", &nr_dquots, 2*sizeof(int), 0444, NULL, &proc_dointvec}, - {FS_MAXDQUOT, "dquot-max", &max_dquots, sizeof(int), - 0644, NULL, &proc_dointvec}, {FS_DENTRY, "dentry-state", &dentry_stat, 6*sizeof(int), 0444, NULL, &proc_dointvec}, {FS_OVERFLOWUID, "overflowuid", &fs_overflowuid, sizeof(int), 0644, NULL, diff -u -r -x *.orig -x *.o linux-2.4.0-test11/mm/vmscan.c linux-2.4.0-test11-quotafix/mm/vmscan.c --- linux-2.4.0-test11/mm/vmscan.c Sun Nov 26 22:52:36 2000 +++ linux-2.4.0-test11-quotafix/mm/vmscan.c Mon Nov 27 09:43:43 2000 @@ -924,6 +924,7 @@ */ shrink_dcache_memory(priority, gfp_mask); shrink_icache_memory(priority, gfp_mask); + shrink_dqcache_memory(priority, gfp_mask); /* Try to get rid of some shared memory pages.. */ while (shm_swap(priority, gfp_mask)) { @@ -992,6 +993,7 @@ if (free_shortage() || inactive_shortage()) { shrink_dcache_memory(6, gfp_mask); shrink_icache_memory(6, gfp_mask); + shrink_dqcache_memory(6, gfp_mask); ret += refill_inactive(gfp_mask, user); } else { /* .