Module Name:    src
Committed By:   bouyer
Date:           Sat Jan 29 23:22:00 UTC 2011

Modified Files:
        src/sys/ufs/ufs [bouyer-quota2]: quota2.h ufs_quota.c ufs_quota2.c

Log Message:
Describe how the on-disk structures are protected from concurent access,
and try to implement it.


To generate a diff of this commit:
cvs rdiff -u -r1.1.2.3 -r1.1.2.4 src/sys/ufs/ufs/quota2.h \
    src/sys/ufs/ufs/ufs_quota2.c
cvs rdiff -u -r1.68.4.2 -r1.68.4.3 src/sys/ufs/ufs/ufs_quota.c

Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.

Modified files:

Index: src/sys/ufs/ufs/quota2.h
diff -u src/sys/ufs/ufs/quota2.h:1.1.2.3 src/sys/ufs/ufs/quota2.h:1.1.2.4
--- src/sys/ufs/ufs/quota2.h:1.1.2.3	Fri Jan 28 18:36:06 2011
+++ src/sys/ufs/ufs/quota2.h	Sat Jan 29 23:22:00 2011
@@ -1,4 +1,4 @@
-/* $NetBSD: quota2.h,v 1.1.2.3 2011/01/28 18:36:06 bouyer Exp $ */
+/* $NetBSD: quota2.h,v 1.1.2.4 2011/01/29 23:22:00 bouyer Exp $ */
 /*-
   * Copyright (c) 2010 Manuel Bouyer
   * All rights reserved.
@@ -41,6 +41,12 @@
  * superblock
  */
 
+/*
+ * The quota file is comprised of 2 parts, the header and the entries.
+ * The header contains global informations, and head of list of quota entries.
+ * A quota entry can either be in the free list, or one of the hash lists.
+ */
+
 /* description of a block or inode quota */
 struct quota2_val {
 	uint64_t q2v_hardlimit; /* absolute limit */
Index: src/sys/ufs/ufs/ufs_quota2.c
diff -u src/sys/ufs/ufs/ufs_quota2.c:1.1.2.3 src/sys/ufs/ufs/ufs_quota2.c:1.1.2.4
--- src/sys/ufs/ufs/ufs_quota2.c:1.1.2.3	Fri Jan 28 18:36:06 2011
+++ src/sys/ufs/ufs/ufs_quota2.c	Sat Jan 29 23:22:00 2011
@@ -1,4 +1,4 @@
-/* $NetBSD: ufs_quota2.c,v 1.1.2.3 2011/01/28 18:36:06 bouyer Exp $ */
+/* $NetBSD: ufs_quota2.c,v 1.1.2.4 2011/01/29 23:22:00 bouyer Exp $ */
 /*-
   * Copyright (c) 2010 Manuel Bouyer
   * All rights reserved.
@@ -28,12 +28,13 @@
   */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: ufs_quota2.c,v 1.1.2.3 2011/01/28 18:36:06 bouyer Exp $");
+__KERNEL_RCSID(0, "$NetBSD: ufs_quota2.c,v 1.1.2.4 2011/01/29 23:22:00 bouyer Exp $");
 
 #include <sys/buf.h>
 #include <sys/param.h>
 #include <sys/kernel.h>
 #include <sys/systm.h>
+#include <sys/malloc.h>
 #include <sys/namei.h>
 #include <sys/file.h>
 #include <sys/proc.h>
@@ -52,6 +53,16 @@
 #include <ufs/ufs/ufs_wapbl.h>
 #include <ufs/ufs/quota2_prop.h>
 
+/*
+ * LOCKING:
+ * Data in the entries are protected by the associated struct dquot's
+ * dq_interlock (this means we can't read or change a quota entry without
+ * grabing a dquot for it).
+ * The header and lists (including pointers in the data entries, and q2e_uid)
+ * are protected by the global dqlock.
+ * the locking order is dq_interlock -> dqlock
+ */
+
 static int getinoquota2(struct inode *, int, struct buf **,
     struct quota2_entry **);
 static int getq2h(struct ufsmount *, int, struct buf **,
@@ -71,6 +82,7 @@
 	struct buf *bp;
 	struct quota2_header *q2h;
 
+	KASSERT(mutex_owned(&dqlock));
 	error = bread(ump->um_quotas[type], 0, ump->umq2_bsize,
 	    ump->um_cred[type], flags, &bp);
 	if (error)
@@ -214,6 +226,7 @@
 	u_long hash_mask;
 	const int needswap = UFS_MPNEEDSWAP(ump);
 
+	KASSERT(mutex_owned(&dqlock));
 	error = getq2h(ump, type, &hbp, &q2h, B_MODIFY);
 	if (error)
 		return error;
@@ -318,8 +331,10 @@
 				continue;
 			}
 			/* need to alloc a new on-disk quot */
+			mutex_enter(&dqlock);
 			error = quota2_q2ealloc(ump, i, ino_ids[i], dq,
 			    &bpp[i], &q2ep[i]);
+			mutex_exit(&dqlock);
 			if (error)
 				return error;
 		} else {
@@ -394,14 +409,51 @@
 	return quota2_check(ip, Q2V_FILE, change, cred, flags);
 }
 
+static int
+quota2_array_add_q2e(struct ufsmount *ump, int type,
+    int id, prop_array_t replies)
+{
+	struct dquot *dq;
+	int error;
+	struct quota2_entry *q2ep, q2e;
+	struct buf  *bp;
+	const int needswap = UFS_MPNEEDSWAP(ump);
+	prop_dictionary_t dict;
+
+	error = dqget(NULLVP, id, ump, type, &dq);
+	if (error)
+		return error;
+
+	if (dq->dq2_lblkno == 0 && dq->dq2_blkoff == 0) {
+		dqrele(NULLVP, dq);
+		return ENOENT;
+	}
+	error = getq2e(ump, type, dq->dq2_lblkno, dq->dq2_blkoff,
+	    &bp, &q2ep, 0);
+	if (error) {
+		dqrele(NULLVP, dq);
+		return error;
+	}
+	mutex_enter(&dq->dq_interlock);
+	quota2_ufs_rwq2e(q2ep, &q2e, needswap);
+	mutex_exit(&dq->dq_interlock);
+	dqrele(NULLVP, dq);
+	brelse(bp, 0);
+	dict = q2etoprop(&q2e, 0);
+	if (dict == NULL)
+		return ENOMEM;
+	if (!prop_array_add_and_rel(replies, dict))
+		return ENOMEM;
+	return 0;
+}
+
 int
 quota2_handle_cmd_get(struct ufsmount *ump, int type, int id,
     int defaultq, prop_array_t replies)
 {
-	struct dquot *dq;
 	int error;
 	struct quota2_header *q2h;
-	struct quota2_entry *q2ep, q2e;
+	struct quota2_entry q2e;
 	struct buf *bp;
 	prop_dictionary_t dict;
 	const int needswap = UFS_MPNEEDSWAP(ump);
@@ -415,53 +467,46 @@
 			mutex_exit(&dqlock);
 			return error;
 		}
-		q2ep = &q2h->q2h_defentry;
-	} else {
-		error = dqget(NULLVP, id, ump, type, &dq);
-
-		if (error)
-			return error;
-
-		if (dq->dq2_lblkno == 0 && dq->dq2_blkoff == 0) {
-			dqrele(NULLVP, dq);
-			return ENOENT;
-		}
-		error = getq2e(ump, type, dq->dq2_lblkno, dq->dq2_blkoff,
-		    &bp, &q2ep, 0);
-		if (error)
-			return error;
-	}
-	quota2_ufs_rwq2e(q2ep, &q2e, needswap);
-	dict = q2etoprop(&q2e, defaultq);
-	if (defaultq)
+		quota2_ufs_rwq2e(&q2h->q2h_defentry, &q2e, needswap);
 		mutex_exit(&dqlock);
-	else
-		dqrele(NULLVP, dq);
-	brelse(bp, 0);
-	if (dict == NULL)
-		return ENOMEM;
+		brelse(bp, 0);
+		dict = q2etoprop(&q2e, defaultq);
+		if (dict == NULL)
+			return ENOMEM;
+		if (!prop_array_add_and_rel(replies, dict))
+			return ENOMEM;
+	} else
+		error = quota2_array_add_q2e(ump, type, id, replies);
 	
-	if (!prop_array_add_and_rel(replies, dict)) {
-		error = ENOMEM;
-	}
 	return error;
 }
 
+struct getuids {
+	long nuids; /* number of uids in array */
+	long size;  /* size of array */
+	uid_t *uids; /* array of uids, dynamically allocated */
+};
 
 static int
-quota2_getall_callback(struct ufsmount *ump, uint64_t *offp,
+quota2_getuids_callback(struct ufsmount *ump, uint64_t *offp,
     struct quota2_entry *q2ep, uint64_t off, void *v)
 {
-	prop_array_t replies = v;
-	prop_dictionary_t dict;
+	struct getuids *gu = v;
+	uid_t *newuids;
 	const int needswap = UFS_MPNEEDSWAP(ump);
-	struct quota2_entry q2e;
 
-	quota2_ufs_rwq2e(q2ep, &q2e, needswap);
-	dict = q2etoprop(&q2e, 0);	
-	if (!prop_array_add_and_rel(replies, dict)) {
-		return ENOMEM;
+	if (gu->nuids == gu->size) {
+		newuids = realloc(gu->uids, gu->size + PAGE_SIZE, M_TEMP,
+		    M_WAITOK);
+		if (newuids == NULL) {
+			free(gu->uids, M_TEMP);
+			return ENOMEM;
+		}
+		gu->uids = newuids;
+		gu->size += (PAGE_SIZE / sizeof(uid_t));
 	}
+	gu->uids[gu->nuids] = ufs_rw32(q2ep->q2e_uid, needswap);
+	gu->nuids++;
 	return 0;
 }
 
@@ -470,34 +515,59 @@
 {
 	int error;
 	struct quota2_header *q2h;
-	struct quota2_entry q2e;
+	struct quota2_entry  q2e;
 	struct buf *hbp;
 	prop_dictionary_t dict;
 	uint64_t offset;
-	int i;
+	int i, j;
 	int quota2_hash_size;
 	const int needswap = UFS_MPNEEDSWAP(ump);
+	struct getuids gu;
 
 	if (ump->um_quotas[type] == NULLVP)
 		return ENODEV;
+	mutex_enter(&dqlock);
 	error = getq2h(ump, type, &hbp, &q2h, 0);
-	if (error)
+	if (error) {
+		mutex_exit(&dqlock);
 		return error;
+	}
 	quota2_ufs_rwq2e(&q2h->q2h_defentry, &q2e, needswap);
 	dict = q2etoprop(&q2e, 1);
 	if (!prop_array_add_and_rel(replies, dict)) {
-		brelse(hbp, 0);
-		return ENOMEM;
+		error = ENOMEM;
+		goto error_bp;
 	}
+	/*
+	 * we can't directly get entries as we can't walk the list
+	 * with qdlock and grab dq_interlock to read the entries
+	 * at the same time. So just walk the lists to build a list of uid,
+	 * and then read entries for these uids
+	 */
+	memset(&gu, 0, sizeof(gu));
 	quota2_hash_size = ufs_rw16(q2h->q2h_hash_size, needswap);
 	for (i = 0; i < quota2_hash_size ; i++) {
-		offset = q2h->q2h_entries[i], needswap;
-		error = quota2_walk_list(ump, hbp, type, &offset, 0, replies,
-		    quota2_getall_callback);
-		if (error)
+		offset = q2h->q2h_entries[i];
+		error = quota2_walk_list(ump, hbp, type, &offset, 0, &gu,
+		    quota2_getuids_callback);
+		if (error) {
+			if (gu.uids != NULL)
+				free(gu.uids, M_TEMP);
 			break;
+		}
 	}
+error_bp:
+	mutex_exit(&dqlock);
 	brelse(hbp, 0);
+	if (error)
+		return error;
+	for (j = 0; j < gu.nuids; j++) {
+		error = quota2_array_add_q2e(ump, type,
+		    gu.uids[j], replies);
+		if (error && error != ENOENT)
+			break;
+	}
+	free(gu.uids, M_TEMP);
 	return error;
 }
 
@@ -545,6 +615,7 @@
 		.dq = dq
 	};
 
+	KASSERT(mutex_owned(&dq->dq_interlock));
 	mutex_enter(&dqlock);
 	error = getq2h(ump, type, &bp, &q2h, 0);
 	if (error)

Index: src/sys/ufs/ufs/ufs_quota.c
diff -u src/sys/ufs/ufs/ufs_quota.c:1.68.4.2 src/sys/ufs/ufs/ufs_quota.c:1.68.4.3
--- src/sys/ufs/ufs/ufs_quota.c:1.68.4.2	Fri Jan 21 16:58:06 2011
+++ src/sys/ufs/ufs/ufs_quota.c	Sat Jan 29 23:22:00 2011
@@ -1,4 +1,4 @@
-/*	$NetBSD: ufs_quota.c,v 1.68.4.2 2011/01/21 16:58:06 bouyer Exp $	*/
+/*	$NetBSD: ufs_quota.c,v 1.68.4.3 2011/01/29 23:22:00 bouyer Exp $	*/
 
 /*
  * Copyright (c) 1982, 1986, 1990, 1993, 1995
@@ -35,7 +35,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: ufs_quota.c,v 1.68.4.2 2011/01/21 16:58:06 bouyer Exp $");
+__KERNEL_RCSID(0, "$NetBSD: ufs_quota.c,v 1.68.4.3 2011/01/29 23:22:00 bouyer Exp $");
 
 #if defined(_KERNEL_OPT)
 #include "opt_quota.h"
@@ -264,9 +264,7 @@
 
 #ifdef QUOTA2
 	if (ump->um_flags & UFS_QUOTA2) {
-		mutex_enter(&dqlock);
 		error = quota2_handle_cmd_getall(ump, type, replies);
-		mutex_exit(&dqlock);
 	} else
 #endif
 		panic("quota_handle_cmd_getall: no support ?");

Reply via email to