Module Name:    src
Committed By:   jdolecek
Date:           Thu Nov 10 20:56:32 UTC 2016

Modified Files:
        src/sys/kern: vfs_wapbl.c
        src/sys/sys: wapbl.h
        src/sys/ufs/ffs: ffs_inode.c ffs_wapbl.c
        src/sys/ufs/ufs: ufs_wapbl.h

Log Message:
during truncate with wapbl, register deallocation for upper indirect block
before recursing into lower blocks, to make sure that it will be removed after
all its referenced blocks are removed

fixes 'ffs_blkfree_common: freeing free block' panic triggered by
ufs_truncate_retry() when just the upper indirect block registration failed,
code tried to free the lower blocks again after wapbl flush

problem found by hannken@, thank you


To generate a diff of this commit:
cvs rdiff -u -r1.85 -r1.86 src/sys/kern/vfs_wapbl.c
cvs rdiff -u -r1.19 -r1.20 src/sys/sys/wapbl.h
cvs rdiff -u -r1.121 -r1.122 src/sys/ufs/ffs/ffs_inode.c
cvs rdiff -u -r1.35 -r1.36 src/sys/ufs/ffs/ffs_wapbl.c
cvs rdiff -u -r1.12 -r1.13 src/sys/ufs/ufs/ufs_wapbl.h

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

Modified files:

Index: src/sys/kern/vfs_wapbl.c
diff -u src/sys/kern/vfs_wapbl.c:1.85 src/sys/kern/vfs_wapbl.c:1.86
--- src/sys/kern/vfs_wapbl.c:1.85	Fri Oct 28 20:38:12 2016
+++ src/sys/kern/vfs_wapbl.c	Thu Nov 10 20:56:32 2016
@@ -1,4 +1,4 @@
-/*	$NetBSD: vfs_wapbl.c,v 1.85 2016/10/28 20:38:12 jdolecek Exp $	*/
+/*	$NetBSD: vfs_wapbl.c,v 1.86 2016/11/10 20:56:32 jdolecek Exp $	*/
 
 /*-
  * Copyright (c) 2003, 2008, 2009 The NetBSD Foundation, Inc.
@@ -36,7 +36,7 @@
 #define WAPBL_INTERNAL
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: vfs_wapbl.c,v 1.85 2016/10/28 20:38:12 jdolecek Exp $");
+__KERNEL_RCSID(0, "$NetBSD: vfs_wapbl.c,v 1.86 2016/11/10 20:56:32 jdolecek Exp $");
 
 #include <sys/param.h>
 #include <sys/bitops.h>
@@ -206,7 +206,7 @@ struct wapbl {
 	int wl_brperjblock;	/* r Block records per journal block */
 #endif
 
-	SIMPLEQ_HEAD(, wapbl_dealloc) wl_dealloclist;	/* lm:	list head */
+	TAILQ_HEAD(, wapbl_dealloc) wl_dealloclist;	/* lm:	list head */
 	int wl_dealloccnt;				/* lm:	total count */
 	int wl_dealloclim;				/* r:	max count */
 
@@ -267,6 +267,9 @@ static struct wapbl_ino *wapbl_inodetrk_
 static size_t wapbl_transaction_len(struct wapbl *wl);
 static inline size_t wapbl_transaction_inodes_len(struct wapbl *wl);
 
+static void wapbl_deallocation_free(struct wapbl *, struct wapbl_dealloc *,
+	bool);
+
 #if 0
 int wapbl_replay_verify(struct wapbl_replay *, struct vnode *);
 #endif
@@ -512,7 +515,7 @@ wapbl_start(struct wapbl ** wlp, struct 
 
 	/* XXX tie this into resource estimation */
 	wl->wl_dealloclim = wl->wl_bufbytes_max / mp->mnt_stat.f_bsize / 2;
-	SIMPLEQ_INIT(&wl->wl_dealloclist);
+	TAILQ_INIT(&wl->wl_dealloclist);
 	
 	wl->wl_buffer = wapbl_alloc(MAXPHYS);
 	wl->wl_buffer_used = 0;
@@ -586,7 +589,7 @@ wapbl_discard(struct wapbl *wl)
 	 * if we want to call flush from inside a transaction
 	 */
 	rw_enter(&wl->wl_rwlock, RW_WRITER);
-	wl->wl_flush(wl->wl_mount, SIMPLEQ_FIRST(&wl->wl_dealloclist));
+	wl->wl_flush(wl->wl_mount, TAILQ_FIRST(&wl->wl_dealloclist));
 
 #ifdef WAPBL_DEBUG_PRINT
 	{
@@ -688,11 +691,8 @@ wapbl_discard(struct wapbl *wl)
 	}
 
 	/* Discard list of deallocs */
-	while ((wd = SIMPLEQ_FIRST(&wl->wl_dealloclist)) != NULL) {
-		SIMPLEQ_REMOVE_HEAD(&wl->wl_dealloclist, wd_entries);
-		pool_put(&wapbl_dealloc_pool, wd);
-		wl->wl_dealloccnt--;
-	}
+	while ((wd = TAILQ_FIRST(&wl->wl_dealloclist)) != NULL)
+		wapbl_deallocation_free(wl, wd, true);
 
 	/* XXX should we clear wl_reserved_bytes? */
 
@@ -702,7 +702,7 @@ wapbl_discard(struct wapbl *wl)
 	KASSERT(LIST_EMPTY(&wl->wl_bufs));
 	KASSERT(SIMPLEQ_EMPTY(&wl->wl_entries));
 	KASSERT(wl->wl_inohashcnt == 0);
-	KASSERT(SIMPLEQ_EMPTY(&wl->wl_dealloclist));
+	KASSERT(TAILQ_EMPTY(&wl->wl_dealloclist));
 	KASSERT(wl->wl_dealloccnt == 0);
 
 	rw_exit(&wl->wl_rwlock);
@@ -738,7 +738,7 @@ wapbl_stop(struct wapbl *wl, int force)
 	KASSERT(wl->wl_dealloccnt == 0);
 	KASSERT(SIMPLEQ_EMPTY(&wl->wl_entries));
 	KASSERT(wl->wl_inohashcnt == 0);
-	KASSERT(SIMPLEQ_EMPTY(&wl->wl_dealloclist));
+	KASSERT(TAILQ_EMPTY(&wl->wl_dealloclist));
 	KASSERT(wl->wl_dealloccnt == 0);
 
 	wapbl_free(wl->wl_wc_scratch, wl->wl_wc_header->wc_len);
@@ -1561,7 +1561,7 @@ wapbl_flush(struct wapbl *wl, int waitfo
 	 * if we want to call flush from inside a transaction
 	 */
 	rw_enter(&wl->wl_rwlock, RW_WRITER);
-	wl->wl_flush(wl->wl_mount, SIMPLEQ_FIRST(&wl->wl_dealloclist));
+	wl->wl_flush(wl->wl_mount, TAILQ_FIRST(&wl->wl_dealloclist));
 
 	/*
 	 * Now that we are exclusively locked and the file system has
@@ -1739,7 +1739,7 @@ wapbl_flush(struct wapbl *wl, int waitfo
  out:
 	if (error) {
 		wl->wl_flush_abort(wl->wl_mount,
-		    SIMPLEQ_FIRST(&wl->wl_dealloclist));
+		    TAILQ_FIRST(&wl->wl_dealloclist));
 	}
 
 #ifdef WAPBL_DEBUG_PRINT
@@ -1878,7 +1878,7 @@ wapbl_print(struct wapbl *wl,
 		{
 			struct wapbl_dealloc *wd;
 			cnt = 0;
-			SIMPLEQ_FOREACH(wd, &wl->wl_dealloclist, wd_entries) {
+			TAILQ_FOREACH(wd, &wl->wl_dealloclist, wd_entries) {
 				(*pr)(" %"PRId64":%d,",
 				      wd->wd_blkno,
 				      wd->wd_len);
@@ -1930,7 +1930,8 @@ wapbl_dump(struct wapbl *wl)
 /****************************************************************/
 
 int
-wapbl_register_deallocation(struct wapbl *wl, daddr_t blk, int len, bool force)
+wapbl_register_deallocation(struct wapbl *wl, daddr_t blk, int len, bool force,
+    void **cookiep)
 {
 	struct wapbl_dealloc *wd;
 	int error = 0;
@@ -1967,7 +1968,10 @@ wapbl_register_deallocation(struct wapbl
 	wd->wd_len = len;
 
 	mutex_enter(&wl->wl_mtx);
-	SIMPLEQ_INSERT_TAIL(&wl->wl_dealloclist, wd, wd_entries);
+	TAILQ_INSERT_TAIL(&wl->wl_dealloclist, wd, wd_entries);
+
+	if (cookiep)
+		*cookiep = wd;
 
  out:
 	mutex_exit(&wl->wl_mtx);
@@ -1979,6 +1983,32 @@ wapbl_register_deallocation(struct wapbl
 	return error;
 }
 
+static void
+wapbl_deallocation_free(struct wapbl *wl, struct wapbl_dealloc *wd,
+	bool locked)
+{
+	KASSERT(!locked
+	    || rw_lock_held(&wl->wl_rwlock) || mutex_owned(&wl->wl_mtx));
+
+	if (!locked)
+		mutex_enter(&wl->wl_mtx);
+
+	TAILQ_REMOVE(&wl->wl_dealloclist, wd, wd_entries);
+	wl->wl_dealloccnt--;
+
+	if (!locked)
+		mutex_exit(&wl->wl_mtx);
+
+	pool_put(&wapbl_dealloc_pool, wd);
+}
+
+void
+wapbl_unregister_deallocation(struct wapbl *wl, void *cookie)
+{
+	KASSERT(cookie != NULL);
+	wapbl_deallocation_free(wl, cookie, false);
+}
+
 /****************************************************************/
 
 static void
@@ -2356,7 +2386,7 @@ wapbl_write_revocations(struct wapbl *wl
 	if (wl->wl_dealloccnt == 0)
 		return 0;
 
-	while ((wd = SIMPLEQ_FIRST(&wl->wl_dealloclist)) != NULL) {
+	while ((wd = TAILQ_FIRST(&wl->wl_dealloclist)) != NULL) {
 		wc->wc_type = WAPBL_WC_REVOCATIONS;
 		wc->wc_len = blocklen;
 		wc->wc_blkcount = 0;
@@ -2367,7 +2397,7 @@ wapbl_write_revocations(struct wapbl *wl
 			    wd->wd_len;
 			wc->wc_blkcount++;
 
-			wd = SIMPLEQ_NEXT(wd, wd_entries);
+			wd = TAILQ_NEXT(wd, wd_entries);
 		}
 		WAPBL_PRINTF(WAPBL_PRINT_WRITE,
 		    ("wapbl_write_revocations: len = %u off = %"PRIdMAX"\n",
@@ -2378,12 +2408,10 @@ wapbl_write_revocations(struct wapbl *wl
 
 		/* free all successfully written deallocs */
 		lwd = wd;
-		while ((wd = SIMPLEQ_FIRST(&wl->wl_dealloclist)) != NULL) {
+		while ((wd = TAILQ_FIRST(&wl->wl_dealloclist)) != NULL) {
 			if (wd == lwd)
 				break;
-			SIMPLEQ_REMOVE_HEAD(&wl->wl_dealloclist, wd_entries);
-			pool_put(&wapbl_dealloc_pool, wd);
-			wl->wl_dealloccnt--;
+			wapbl_deallocation_free(wl, wd, true);
 		}
 	}
 	*offp = off;

Index: src/sys/sys/wapbl.h
diff -u src/sys/sys/wapbl.h:1.19 src/sys/sys/wapbl.h:1.20
--- src/sys/sys/wapbl.h:1.19	Fri Oct 28 20:38:12 2016
+++ src/sys/sys/wapbl.h	Thu Nov 10 20:56:32 2016
@@ -1,4 +1,4 @@
-/*	$NetBSD: wapbl.h,v 1.19 2016/10/28 20:38:12 jdolecek Exp $	*/
+/*	$NetBSD: wapbl.h,v 1.20 2016/11/10 20:56:32 jdolecek Exp $	*/
 
 /*-
  * Copyright (c) 2003,2008 The NetBSD Foundation, Inc.
@@ -95,7 +95,7 @@ struct wapbl_replay;
 struct wapbl;
 
 struct wapbl_dealloc {
-	SIMPLEQ_ENTRY(wapbl_dealloc) wd_entries;
+	TAILQ_ENTRY(wapbl_dealloc) wd_entries;
 	daddr_t wd_blkno;	/* address of block */
 	int wd_len;		/* size of block */
 };
@@ -173,7 +173,9 @@ void	wapbl_unregister_inode(struct wapbl
  * the corresponding blocks from being reused as data
  * blocks until the log is on disk.
  */
-int	wapbl_register_deallocation(struct wapbl *, daddr_t, int, bool);
+int	wapbl_register_deallocation(struct wapbl *, daddr_t, int, bool,
+		void **);
+void	wapbl_unregister_deallocation(struct wapbl *, void *);
 
 void	wapbl_jlock_assert(struct wapbl *wl);
 void	wapbl_junlock_assert(struct wapbl *wl);

Index: src/sys/ufs/ffs/ffs_inode.c
diff -u src/sys/ufs/ffs/ffs_inode.c:1.121 src/sys/ufs/ffs/ffs_inode.c:1.122
--- src/sys/ufs/ffs/ffs_inode.c:1.121	Thu Nov 10 19:10:05 2016
+++ src/sys/ufs/ffs/ffs_inode.c	Thu Nov 10 20:56:32 2016
@@ -1,4 +1,4 @@
-/*	$NetBSD: ffs_inode.c,v 1.121 2016/11/10 19:10:05 jdolecek Exp $	*/
+/*	$NetBSD: ffs_inode.c,v 1.122 2016/11/10 20:56:32 jdolecek Exp $	*/
 
 /*-
  * Copyright (c) 2008 The NetBSD Foundation, Inc.
@@ -61,7 +61,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: ffs_inode.c,v 1.121 2016/11/10 19:10:05 jdolecek Exp $");
+__KERNEL_RCSID(0, "$NetBSD: ffs_inode.c,v 1.122 2016/11/10 20:56:32 jdolecek Exp $");
 
 #if defined(_KERNEL_OPT)
 #include "opt_ffs.h"
@@ -218,6 +218,7 @@ ffs_truncate(struct vnode *ovp, off_t le
 	off_t osize;
 	int sync;
 	struct ufsmount *ump = oip->i_ump;
+	void *dcookie;
 
 	UFS_WAPBL_JLOCK_ASSERT(ip->i_ump->um_mountp);
 
@@ -419,20 +420,31 @@ ffs_truncate(struct vnode *ovp, off_t le
 		else
 			bn = ufs_rw64(oip->i_ffs2_ib[level],UFS_FSNEEDSWAP(fs));
 		if (bn != 0) {
+			if (lastiblock[level] < 0 &&
+			    oip->i_ump->um_mountp->mnt_wapbl) {
+				error = UFS_WAPBL_REGISTER_DEALLOCATION(
+				    oip->i_ump->um_mountp,
+				    FFS_FSBTODB(fs, bn), fs->fs_bsize,
+				    &dcookie);
+				if (error)
+					goto out;
+			} else {
+				dcookie = NULL;
+			}
+			    
 			error = ffs_indirtrunc(oip, indir_lbn[level],
 			    FFS_FSBTODB(fs, bn), lastiblock[level], level,
 			    &blocksreleased);
-			if (error)
+			if (error) {
+				if (dcookie) {
+					UFS_WAPBL_UNREGISTER_DEALLOCATION(
+					    oip->i_ump->um_mountp, dcookie);
+				}
 				goto out;
+			}
 
 			if (lastiblock[level] < 0) {
-				if (oip->i_ump->um_mountp->mnt_wapbl) {
-					error = UFS_WAPBL_REGISTER_DEALLOCATION(
-					    oip->i_ump->um_mountp,
-					    FFS_FSBTODB(fs, bn), fs->fs_bsize);
-					if (error)
-						goto out;
-				} else
+				if (!dcookie)
 					ffs_blkfree(fs, oip->i_devvp, bn,
 					    fs->fs_bsize, oip->i_number);
 				DIP_ASSIGN(oip, ib[level], 0);
@@ -461,7 +473,7 @@ ffs_truncate(struct vnode *ovp, off_t le
 		    (ovp->v_type != VREG)) {
 			error = UFS_WAPBL_REGISTER_DEALLOCATION(
 			    oip->i_ump->um_mountp,
-			    FFS_FSBTODB(fs, bn), bsize);
+			    FFS_FSBTODB(fs, bn), bsize, NULL);
 			if (error)
 				goto out;
 		} else
@@ -504,7 +516,7 @@ ffs_truncate(struct vnode *ovp, off_t le
 			    (ovp->v_type != VREG)) {
 				error = UFS_WAPBL_REGISTER_DEALLOCATION(
 				    oip->i_ump->um_mountp, FFS_FSBTODB(fs, bn),
-				    oldspace - newspace);
+				    oldspace - newspace, NULL);
 				if (error)
 					goto out;
 			} else
@@ -581,6 +593,7 @@ ffs_indirtrunc(struct inode *ip, daddr_t
 	int error = 0, allerror = 0;
 	const int needswap = UFS_FSNEEDSWAP(fs);
 	const int wapbl = (ip->i_ump->um_mountp->mnt_wapbl != NULL);
+	void *dcookie;
 
 #define RBAP(ip, i) (((ip)->i_ump->um_fstype == UFS1) ? \
 	    ufs_rw32(bap1[i], needswap) : ufs_rw64(bap2[i], needswap))
@@ -675,21 +688,32 @@ ffs_indirtrunc(struct inode *ip, daddr_t
 		if (nb == 0)
 			continue;
 
-		if (level > SINGLE) {
-			error = ffs_indirtrunc(ip, nlbn, FFS_FSBTODB(fs, nb),
-					       (daddr_t)-1, level - 1, countp);
-			if (error)
-				goto out;
-		}
-
 		if ((ip->i_ump->um_mountp->mnt_wapbl) &&
 		    ((level > SINGLE) || (ITOV(ip)->v_type != VREG))) {
 			error = UFS_WAPBL_REGISTER_DEALLOCATION(
 			    ip->i_ump->um_mountp,
-			    FFS_FSBTODB(fs, nb), fs->fs_bsize);
+			    FFS_FSBTODB(fs, nb), fs->fs_bsize,
+			    &dcookie);
 			if (error)
 				goto out;
-		} else
+		} else {
+			dcookie = NULL;
+		}
+
+		if (level > SINGLE) {
+			error = ffs_indirtrunc(ip, nlbn, FFS_FSBTODB(fs, nb),
+					       (daddr_t)-1, level - 1, countp);
+			if (error) {
+				if (dcookie) {
+					UFS_WAPBL_UNREGISTER_DEALLOCATION(
+					    ip->i_ump->um_mountp, dcookie);
+				}
+
+				goto out;
+			}
+		}
+
+		if (!dcookie)
 			ffs_blkfree(fs, ip->i_devvp, nb, fs->fs_bsize,
 			    ip->i_number);
 

Index: src/sys/ufs/ffs/ffs_wapbl.c
diff -u src/sys/ufs/ffs/ffs_wapbl.c:1.35 src/sys/ufs/ffs/ffs_wapbl.c:1.36
--- src/sys/ufs/ffs/ffs_wapbl.c:1.35	Sun Oct  2 19:02:57 2016
+++ src/sys/ufs/ffs/ffs_wapbl.c	Thu Nov 10 20:56:32 2016
@@ -1,4 +1,4 @@
-/*	$NetBSD: ffs_wapbl.c,v 1.35 2016/10/02 19:02:57 christos Exp $	*/
+/*	$NetBSD: ffs_wapbl.c,v 1.36 2016/11/10 20:56:32 jdolecek Exp $	*/
 
 /*-
  * Copyright (c) 2003,2006,2008 The NetBSD Foundation, Inc.
@@ -30,7 +30,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: ffs_wapbl.c,v 1.35 2016/10/02 19:02:57 christos Exp $");
+__KERNEL_RCSID(0, "$NetBSD: ffs_wapbl.c,v 1.36 2016/11/10 20:56:32 jdolecek Exp $");
 
 #define WAPBL_INTERNAL
 
@@ -182,7 +182,7 @@ ffs_wapbl_sync_metadata(struct mount *mp
 	ufs_wapbl_verify_inodes(mp, __func__);
 #endif
 
-	for (wd = fdealloc; wd != NULL; wd = SIMPLEQ_NEXT(wd, wd_entries)) {
+	for (wd = fdealloc; wd != NULL; wd = TAILQ_NEXT(wd, wd_entries)) {
 		/*
 		 * blkfree errors are unreported, might silently fail
 		 * if it cannot read the cylinder group block
@@ -206,7 +206,7 @@ ffs_wapbl_abort_sync_metadata(struct mou
 	struct fs *fs = ump->um_fs;
 	struct wapbl_dealloc *wd;
 
-	for (wd = fdealloc; wd != NULL; wd = SIMPLEQ_NEXT(wd, wd_entries)) {
+	for (wd = fdealloc; wd != NULL; wd = TAILQ_NEXT(wd, wd_entries)) {
 		/*
 		 * Since the above blkfree may have failed, this blkalloc might
 		 * fail as well, so don't check its error.  Note that if the

Index: src/sys/ufs/ufs/ufs_wapbl.h
diff -u src/sys/ufs/ufs/ufs_wapbl.h:1.12 src/sys/ufs/ufs/ufs_wapbl.h:1.13
--- src/sys/ufs/ufs/ufs_wapbl.h:1.12	Fri Oct 28 20:38:12 2016
+++ src/sys/ufs/ufs/ufs_wapbl.h	Thu Nov 10 20:56:32 2016
@@ -1,4 +1,4 @@
-/*	$NetBSD: ufs_wapbl.h,v 1.12 2016/10/28 20:38:12 jdolecek Exp $	*/
+/*	$NetBSD: ufs_wapbl.h,v 1.13 2016/11/10 20:56:32 jdolecek Exp $	*/
 
 /*-
  * Copyright (c) 2003,2006,2008 The NetBSD Foundation, Inc.
@@ -146,16 +146,23 @@ ufs_wapbl_end(struct mount *mp)
 #define	UFS_WAPBL_UNREGISTER_INODE(mp, ino, mode)			\
 	if (mp->mnt_wapbl) wapbl_unregister_inode(mp->mnt_wapbl, ino, mode)
 
-#define	UFS_WAPBL_REGISTER_DEALLOCATION(mp, blk, len)			\
+#define	UFS_WAPBL_REGISTER_DEALLOCATION(mp, blk, len, cookiep)		\
 	(mp->mnt_wapbl)							\
-	    ? wapbl_register_deallocation(mp->mnt_wapbl, blk, len, false) : 0
+	    ? wapbl_register_deallocation(mp->mnt_wapbl, blk, len,	\
+		false, cookiep)						\
+	    : 0
 
 #define	UFS_WAPBL_REGISTER_DEALLOCATION_FORCE(mp, blk, len)		\
 	(								\
 	  (mp->mnt_wapbl)						\
-	    ? wapbl_register_deallocation(mp->mnt_wapbl, blk, len, true) : 0 \
+	    ? wapbl_register_deallocation(mp->mnt_wapbl, blk, len,	\
+		true, NULL)						\
+	    : 0								\
 	)
 
+#define	UFS_WAPBL_UNREGISTER_DEALLOCATION(mp, cookie)			\
+	if (mp->mnt_wapbl) wapbl_unregister_deallocation(mp->mnt_wapbl, cookie)
+
 #else /* ! WAPBL */
 #define	UFS_WAPBL_BEGIN(mp) (__USE(mp), 0)
 #define	UFS_WAPBL_END(mp)	do { } while (0)
@@ -166,6 +173,7 @@ ufs_wapbl_end(struct mount *mp)
 #define	UFS_WAPBL_UNREGISTER_INODE(mp, ino, mode)	do { } while (0)
 #define	UFS_WAPBL_REGISTER_DEALLOCATION(mp, blk, len)		0
 #define	UFS_WAPBL_REGISTER_DEALLOCATION_FORCE(mp, blk, len)	0
+#define	UFS_WAPBL_UNREGISTER_DEALLOCATION(mp, cookie)	do { } while (0)
 #endif
 
 #endif /* !_UFS_UFS_UFS_WAPBL_H_ */

Reply via email to