DF-0142 / fix.diff
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 | diff --git a/sys/kern/vfs_quota.c b/sys/kern/vfs_quota.c --- a/sys/kern/vfs_quota.c +++ b/sys/kern/vfs_quota.c @@ -86,11 +86,24 @@ { struct ac_unode *unp, *res; + /* + * The caller must NOT hold ac_spin: this M_WAITOK allocation is + * allowed to sleep (the slab allocator may block in kmem_slab_alloc + * via vm_map_lock / vm_wait). Holding a spinlock across it panics + * in lwkt_switch() (KASSERT gd->gd_spinlocks == 0). + */ unp = kmalloc(sizeof(struct ac_unode), M_MOUNT, M_ZERO | M_WAITOK); - unp->left_bits = (uid >> ACCT_CHUNK_BITS); + + spin_lock(&mp->mnt_acct.ac_spin); res = RB_INSERT(ac_utree, &mp->mnt_acct.ac_uroot, unp); - KASSERT(res == NULL, ("unode_insert(): RB_INSERT didn't return NULL")); + if (res != NULL) { + /* raced with a concurrent inserter; keep the winner, drop ours */ + spin_unlock(&mp->mnt_acct.ac_spin); + kfree(unp, M_MOUNT); + return res; + } + spin_unlock(&mp->mnt_acct.ac_spin); return unp; } @@ -100,11 +113,18 @@ { struct ac_gnode *gnp, *res; + /* See unode_insert(): caller must NOT hold ac_spin here. */ gnp = kmalloc(sizeof(struct ac_gnode), M_MOUNT, M_ZERO | M_WAITOK); - gnp->left_bits = (gid >> ACCT_CHUNK_BITS); + + spin_lock(&mp->mnt_acct.ac_spin); res = RB_INSERT(ac_gtree, &mp->mnt_acct.ac_groot, gnp); - KASSERT(res == NULL, ("gnode_insert(): RB_INSERT didn't return NULL")); + if (res != NULL) { + spin_unlock(&mp->mnt_acct.ac_spin); + kfree(gnp, M_MOUNT); + return res; + } + spin_unlock(&mp->mnt_acct.ac_spin); return gnp; } @@ -159,10 +179,16 @@ mp->mnt_acct.ac_bytes += delta; - if ((unp = RB_FIND(ac_utree, &mp->mnt_acct.ac_uroot, &ufind)) == NULL) + if ((unp = RB_FIND(ac_utree, &mp->mnt_acct.ac_uroot, &ufind)) == NULL) { + spin_unlock(&mp->mnt_acct.ac_spin); unp = unode_insert(mp, uid); - if ((gnp = RB_FIND(ac_gtree, &mp->mnt_acct.ac_groot, &gfind)) == NULL) + spin_lock(&mp->mnt_acct.ac_spin); + } + if ((gnp = RB_FIND(ac_gtree, &mp->mnt_acct.ac_groot, &gfind)) == NULL) { + spin_unlock(&mp->mnt_acct.ac_spin); gnp = gnode_insert(mp, gid); + spin_lock(&mp->mnt_acct.ac_spin); + } /* update existing chunk */ unp->uid_chunk[(uid & ACCT_CHUNK_MASK)].space += delta; @@ -250,14 +276,20 @@ if (prop_dictionary_get_uint32(item, "uid", &id)) { ufind.left_bits = (id >> ACCT_CHUNK_BITS); unp = RB_FIND(ac_utree, &mp->mnt_acct.ac_uroot, &ufind); - if (unp == NULL) + if (unp == NULL) { + spin_unlock(&mp->mnt_acct.ac_spin); unp = unode_insert(mp, id); + spin_lock(&mp->mnt_acct.ac_spin); + } unp->uid_chunk[(id & ACCT_CHUNK_MASK)].space = space; } else if (prop_dictionary_get_uint32(item, "gid", &id)) { gfind.left_bits = (id >> ACCT_CHUNK_BITS); gnp = RB_FIND(ac_gtree, &mp->mnt_acct.ac_groot, &gfind); - if (gnp == NULL) + if (gnp == NULL) { + spin_unlock(&mp->mnt_acct.ac_spin); gnp = gnode_insert(mp, id); + spin_lock(&mp->mnt_acct.ac_spin); + } gnp->gid_chunk[(id & ACCT_CHUNK_MASK)].space = space; } else { mp->mnt_acct.ac_bytes = space; @@ -296,8 +328,11 @@ ufind.left_bits = (uid >> ACCT_CHUNK_BITS); spin_lock(&mp->mnt_acct.ac_spin); - if ((unp = RB_FIND(ac_utree, &mp->mnt_acct.ac_uroot, &ufind)) == NULL) + if ((unp = RB_FIND(ac_utree, &mp->mnt_acct.ac_uroot, &ufind)) == NULL) { + spin_unlock(&mp->mnt_acct.ac_spin); unp = unode_insert(mp, uid); + spin_lock(&mp->mnt_acct.ac_spin); + } unp->uid_chunk[(uid & ACCT_CHUNK_MASK)].limit = limit; spin_unlock(&mp->mnt_acct.ac_spin); @@ -317,8 +352,11 @@ gfind.left_bits = (gid >> ACCT_CHUNK_BITS); spin_lock(&mp->mnt_acct.ac_spin); - if ((gnp = RB_FIND(ac_gtree, &mp->mnt_acct.ac_groot, &gfind)) == NULL) + if ((gnp = RB_FIND(ac_gtree, &mp->mnt_acct.ac_groot, &gfind)) == NULL) { + spin_unlock(&mp->mnt_acct.ac_spin); gnp = gnode_insert(mp, gid); + spin_lock(&mp->mnt_acct.ac_spin); + } gnp->gid_chunk[(gid & ACCT_CHUNK_MASK)].limit = limit; spin_unlock(&mp->mnt_acct.ac_spin); |