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
This commit is contained in:
jdolecek 2016-11-10 20:56:32 +00:00
parent 6730f31e00
commit 86e8a3aae2
5 changed files with 117 additions and 55 deletions

View File

@ -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_get(struct wapbl *wl, ino_t ino);
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 mount *mp, struct vnode *vp,
/* 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 waitfor)
* 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 waitfor)
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 *wl, daddr_t blk, int len, bool force)
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 *wl, daddr_t blk, int len, bool force)
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, off_t *offp)
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, off_t *offp)
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, off_t *offp)
/* 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;

View File

@ -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 *, ino_t, mode_t);
* 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);

View File

@ -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 length, int ioflag, kauth_cred_t cred)
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 length, int ioflag, kauth_cred_t cred)
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 length, int ioflag, kauth_cred_t cred)
(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 length, int ioflag, kauth_cred_t cred)
(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 lbn, daddr_t dbn, daddr_t lastbn,
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 lbn, daddr_t dbn, daddr_t lastbn,
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);

View File

@ -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, struct wapbl_dealloc *fdealloc)
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 mount *mp, struct wapbl_dealloc *fdealloc)
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

View File

@ -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_ */