Attilio Rao 84887fa362 - Add real assertions to lockmgr locking primitives.
A couple of notes for this:
  * WITNESS support, when enabled, is only used for shared locks in order
    to avoid problems with the "disowned" locks
  * KA_HELD and KA_UNHELD only exists in the lockmgr namespace in order
    to assert for a generic thread (not curthread) owning or not the
    lock.  Really, this kind of check is bogus but it seems very
    widespread in the consumers code.  So, for the moment, we cater this
    untrusted behaviour, until the consumers are not fixed and the
    options could be removed (hopefully during 8.0-CURRENT lifecycle)
  * Implementing KA_HELD and KA_UNHELD (not surported natively by
    WITNESS) made necessary the introduction of LA_MASKASSERT which
    specifies the range for default lock assertion flags
  * About other aspects, lockmgr_assert() follows exactly what other
    locking primitives offer about this operation.

- Build real assertions for buffer cache locks on the top of
  lockmgr_assert().  They can be used with the BUF_ASSERT_*(bp)
  paradigm.

- Add checks at lock destruction time and use a cookie for verifying
  lock integrity at any operation.

- Redefine BUF_LOCKFREE() in order to not use a direct assert but
  let it rely on the aforementioned destruction time check.

KPI results evidently broken, so __FreeBSD_version bumping and
manpage update result necessary and will be committed soon.

Side note: lockmgr_assert() will be used soon in order to implement
real assertions in the vnode namespace replacing the legacy and still
bogus "VOP_ISLOCKED()" way.

Tested by:      kris (earlier version)
Reviewed by:    jhb
2008-02-13 20:44:19 +00:00

335 lines
6.7 KiB
C

/*
* Copyright (c) 2001,2005 Russell Cattelan
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* $FreeBSD$
*/
#include "xfs.h"
#include "xfs_types.h"
#include "xfs_inum.h"
#include "xfs_log.h"
#include "xfs_trans.h"
#include "xfs_sb.h"
#include "xfs_ag.h"
#include "xfs_dir.h"
#include "xfs_dir2.h"
#include "xfs_dmapi.h"
#include "xfs_mount.h"
#include "xfs_clnt.h"
#include "xfs_mountops.h"
#include <geom/geom.h>
#include <geom/geom_vfs.h>
xfs_buf_t *
xfs_buf_read_flags(xfs_buftarg_t *target, xfs_daddr_t blkno, size_t len, int flags)
{
struct buf *bp;
KASSERT((target != NULL), ("got NULL buftarg_t"));
if (bread(target->specvp, blkno, BBTOB(len), NOCRED, &bp)) {
printf("bread failed specvp %p blkno %qd BBTOB(len) %ld\n",
target->specvp, blkno, (long)BBTOB(len));
bp = NULL;
}
/* not really sure what B_MANAGED really does for us
* maybe we should drop this and just stick with a locked buf
*/
if (flags & B_MANAGED)
bp->b_flags |= B_MANAGED;
xfs_buf_set_target(bp, target);
return (bp);
}
xfs_buf_t *
xfs_buf_get_flags(xfs_buftarg_t *target, xfs_daddr_t blkno, size_t len, int flags)
{
struct buf *bp = NULL;
KASSERT((target != NULL), ("got NULL buftarg_t"));
bp = getblk(target->specvp, blkno, BBTOB(len), 0, 0, 0);
if (bp != NULL)
xfs_buf_set_target(bp, target);
return (bp);
}
xfs_buf_t*
xfs_buf_get_empty(size_t size, xfs_buftarg_t *target)
{
struct buf *bp;
bp = geteblk(0);
if (bp != NULL) {
bp->b_bufsize = size;
bp->b_bcount = size;
BUF_ASSERT_HELD(bp);
xfs_buf_set_target(bp, target);
}
return (bp);
}
xfs_buf_t*
xfs_buf_get_noaddr(size_t len, xfs_buftarg_t *target)
{
struct buf *bp;
if (len >= MAXPHYS)
return (NULL);
bp = geteblk(len);
if (bp != NULL) {
BUF_ASSERT_HELD(bp);
xfs_buf_set_target(bp, target);
}
return (bp);
}
void
xfs_buf_free(xfs_buf_t *bp)
{
bp->b_flags |= B_INVAL;
BUF_KERNPROC(bp); /* ugly hack #1 */
if (bp->b_kvasize == 0) {
bp->b_saveaddr = bp->b_kvabase; /* ugly hack #2 */
bp->b_data = bp->b_saveaddr;
bp->b_bcount = 0;
bp->b_bufsize = 0;
}
brelse(bp);
}
void
xfs_buf_readahead(
xfs_buftarg_t *target,
xfs_daddr_t ioff,
size_t isize,
xfs_buf_flags_t flags)
{
daddr_t rablkno;
int rabsize;
rablkno = ioff;
rabsize = BBTOB(isize);
breada(target->specvp, &rablkno, &rabsize, 1, NOCRED);
}
void
xfs_buf_set_target(xfs_buf_t *bp, xfs_buftarg_t *targ)
{
bp->b_bufobj = &targ->specvp->v_bufobj;
bp->b_caller1 = targ;
}
xfs_buftarg_t *
xfs_buf_get_target(xfs_buf_t *bp)
{
return (xfs_buftarg_t *)bp->b_caller1;
}
int
XFS_bwrite(xfs_buf_t *bp)
{
int error;
if (bp->b_vp == NULL) {
error = xfs_buf_iorequest(bp);
if ((bp->b_flags & B_ASYNC) == 0) {
error = bufwait(bp);
#if 0
if (BUF_LOCKRECURSED(bp))
BUF_UNLOCK(bp);
else
brelse(bp);
#endif
brelse(bp);
}
return (error);
}
error = bwrite(bp);
return (error);
}
void
xfs_buf_pin(xfs_buf_t *bp)
{
bpin(bp);
}
void
xfs_buf_unpin(xfs_buf_t *bp)
{
bunpin(bp);
}
int
xfs_buf_ispin(xfs_buf_t *bp)
{
return bp->b_pin_count;
}
#if 0
void
xfs_buf_wait_unpin(
xfs_buf_t *bp)
{
bunpin_wait(bp);
}
#endif
/*
* Move data into or out of a buffer.
*/
void
xfs_buf_iomove(
xfs_buf_t *bp, /* buffer to process */
size_t boff, /* starting buffer offset */
size_t bsize, /* length to copy */
caddr_t data, /* data address */
xfs_buf_rw_t mode) /* read/write/zero flag */
{
printf("xfs_buf_iomove NI\n");
#ifdef RMC
size_t bend, cpoff, csize;
struct page *page;
bend = boff + bsize;
while (boff < bend) {
page = bp->b_pages[xfs_buf_btoct(boff + bp->b_offset)];
cpoff = xfs_buf_poff(boff + bp->b_offset);
csize = min_t(size_t,
PAGE_CACHE_SIZE-cpoff, bp->b_count_desired-boff);
ASSERT(((csize + cpoff) <= PAGE_CACHE_SIZE));
switch (mode) {
case XBRW_ZERO:
memset(page_address(page) + cpoff, 0, csize);
break;
case XBRW_READ:
memcpy(data, page_address(page) + cpoff, csize);
break;
case XBRW_WRITE:
memcpy(page_address(page) + cpoff, data, csize);
}
boff += csize;
data += csize;
}
#endif
}
/*
* Handling of buffer targets (buftargs).
*/
/*
* Wait for any bufs with callbacks that have been submitted but
* have not yet returned... walk the hash list for the target.
*/
void
xfs_wait_buftarg(
xfs_buftarg_t *bp)
{
printf("xfs_wait_buftarg(%p) NI\n", bp);
}
int
xfs_flush_buftarg(
xfs_buftarg_t *btp,
int wait)
{
int error = 0;
error = vinvalbuf(btp->specvp, V_SAVE|V_NORMAL, curthread, 0, 0);
return error;
}
void
xfs_free_buftarg(
xfs_buftarg_t *btp,
int external)
{
xfs_flush_buftarg(btp, /* wait */ 0);
kmem_free(btp, sizeof(*btp));
}
int
xfs_readonly_buftarg(
xfs_buftarg_t *btp)
{
struct g_consumer *cp;
KASSERT(btp->specvp->v_bufobj.bo_ops == &xfs_bo_ops,
("Bogus xfs_buftarg_t pointer"));
cp = btp->specvp->v_bufobj.bo_private;
return (cp->acw == 0);
}
#if 0
void
xfs_relse_buftarg(
xfs_buftarg_t *btp)
{
printf("xfs_relse_buftargNI %p\n",btp);
}
#endif
unsigned int
xfs_getsize_buftarg(
xfs_buftarg_t *btp)
{
struct g_consumer *cp;
cp = btp->specvp->v_bufobj.bo_private;
return (cp->provider->sectorsize);
}
int
xfs_setsize_buftarg(
xfs_buftarg_t *btp,
unsigned int blocksize,
unsigned int sectorsize)
{
printf("xfs_setsize_buftarg NI %p\n",btp);
return 0;
}
xfs_buftarg_t *
xfs_alloc_buftarg(
struct vnode *bdev,
int external)
{
xfs_buftarg_t *btp;
btp = kmem_zalloc(sizeof(*btp), KM_SLEEP);
btp->dev = bdev->v_rdev;
btp->specvp = bdev;
return btp;
}