DF-0847 / 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 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 | diff --git a/sys/vfs/ufs/ufs_quota.c b/sys/vfs/ufs/ufs_quota.c index 0000000..1111111 100644 --- a/sys/vfs/ufs/ufs_quota.c +++ b/sys/vfs/ufs/ufs_quota.c @@ -44,6 +44,7 @@ #include <sys/vnode.h> #include <sys/mount.h> #include <vm/vm_zone.h> +#include <sys/thread.h> #include "quota.h" #include "inode.h" @@ -732,6 +733,15 @@ static long ufs_numdquot, ufs_desireddquot = DQUOTINC; /* + * Serializes all mutation of the global dquot hash table and free list. + * The token is held only across list/hash manipulation (lookup, recycle, + * insert, remove) and is NOT held across sleeping operations (kmalloc + * M_WAITOK, vn_lock, VOP_READ/VOP_WRITE). See ufs_dqget/ufs_dqrele/ + * ufs_dqflush. + */ +static struct lwkt_token ufs_dq_token = LWKT_TOKEN_INITIALIZER(ufs_dq_token); + +/* * Initialize the quota system. */ void @@ -741,6 +751,7 @@ ufs_dqhashtbl = hashinit(hsize, M_DQUOT, &ufs_dqhash); TAILQ_INIT(&ufs_dqfreelist); + lwkt_token_init(&ufs_dq_token, "ufsdqlist"); } /* @@ -765,8 +776,14 @@ } /* * Check the cache first. + * + * The dquot token serializes all mutation of the global hash table and + * free list. It is held across the cache lookup and the miss-path + * alloc/recycle + hash insert, but released around sleeping operations + * (kmalloc M_WAITOK below, vn_lock + VOP_READ further down). */ dqh = DQHASH(dqvp, id); + lwkt_gettoken(&ufs_dq_token); LIST_FOREACH(dq, dqh, dq_hash) { if (dq->dq_id != id || dq->dq_ump->um_quotas[dq->dq_type] != dqvp) @@ -779,6 +796,7 @@ TAILQ_REMOVE(&ufs_dqfreelist, dq, dq_freelist); DQREF(dq); *dqp = dq; + lwkt_reltoken(&ufs_dq_token); return (0); } @@ -790,13 +808,20 @@ ufs_desireddquot += DQUOTINC; } if (ufs_numdquot < ufs_desireddquot) { + /* + * kmalloc(M_WAITOK) may sleep -> drop the token around it. + * ufs_numdquot is bumped under the token after the allocation. + */ + lwkt_reltoken(&ufs_dq_token); dq = (struct ufs_dquot *) kmalloc(sizeof *dq, M_DQUOT, M_WAITOK | M_ZERO); + lwkt_gettoken(&ufs_dq_token); ufs_numdquot++; } else { if ((dq = TAILQ_FIRST(&ufs_dqfreelist)) == NULL) { tablefull("dquot"); *dqp = NODQUOT; + lwkt_reltoken(&ufs_dq_token); return (EUSERS); } if (dq->dq_cnt || (dq->dq_flags & DQ_MOD)) @@ -806,16 +831,22 @@ LIST_REMOVE(dq, dq_hash); } /* - * Initialize the contents of the dquot structure. + * Initialize the contents of the dquot structure and publish it on + * the hash chain while still holding the token. */ - if (vp != dqvp) - vn_lock(dqvp, LK_EXCLUSIVE | LK_RETRY); LIST_INSERT_HEAD(dqh, dq, dq_hash); DQREF(dq); dq->dq_flags = DQ_LOCK; dq->dq_id = id; dq->dq_ump = ump; dq->dq_type = type; + lwkt_reltoken(&ufs_dq_token); + + /* + * Read the quota record. Performed outside the token (may sleep). + */ + if (vp != dqvp) + vn_lock(dqvp, LK_EXCLUSIVE | LK_RETRY); auio.uio_iov = &aiov; auio.uio_iovcnt = 1; aiov.iov_base = (caddr_t)&dq->dq_dqb; @@ -838,7 +869,9 @@ * quota structure and reflect problem to caller. */ if (error) { + lwkt_gettoken(&ufs_dq_token); LIST_REMOVE(dq, dq_hash); + lwkt_reltoken(&ufs_dq_token); ufs_dqrele(vp, dq); *dqp = NODQUOT; return (error); @@ -887,7 +920,9 @@ (void)ufs_dqsync(vp, dq); if (--dq->dq_cnt > 0) return; + lwkt_gettoken(&ufs_dq_token); TAILQ_INSERT_TAIL(&ufs_dqfreelist, dq, dq_freelist); + lwkt_reltoken(&ufs_dq_token); } /* @@ -953,6 +988,7 @@ * file off their hash chains (they will eventually * fall off the head of the free list and be re-used). */ + lwkt_gettoken(&ufs_dq_token); for (dqh = &ufs_dqhashtbl[ufs_dqhash]; dqh >= ufs_dqhashtbl; dqh--) { for (dq = dqh->lh_first; dq; dq = nextdq) { nextdq = dq->dq_hash.le_next; @@ -964,4 +1000,5 @@ dq->dq_ump = NULL; } } + lwkt_reltoken(&ufs_dq_token); } |