Exemplo n.º 1
0
/*
 * This is called to unpin the buffer associated with the buf log
 * item which was previously pinned with a call to xfs_buf_item_pin().
 *
 * Also drop the reference to the buf item for the current transaction.
 * If the XFS_BLI_STALE flag is set and we are the last reference,
 * then free up the buf log item and unlock the buffer.
 *
 * If the remove flag is set we are called from uncommit in the
 * forced-shutdown path.  If that is true and the reference count on
 * the log item is going to drop to zero we need to free the item's
 * descriptor in the transaction.
 */
STATIC void
xfs_buf_item_unpin(
    struct xfs_log_item	*lip,
    int			remove)
{
    struct xfs_buf_log_item	*bip = BUF_ITEM(lip);
    xfs_buf_t	*bp = bip->bli_buf;
    struct xfs_ail	*ailp = lip->li_ailp;
    int		stale = bip->bli_flags & XFS_BLI_STALE;
    int		freed;

    ASSERT(bp->b_fspriv == bip);
    ASSERT(atomic_read(&bip->bli_refcount) > 0);

    trace_xfs_buf_item_unpin(bip);

    freed = atomic_dec_and_test(&bip->bli_refcount);

    if (atomic_dec_and_test(&bp->b_pin_count))
        wake_up_all(&bp->b_waiters);

    if (freed && stale) {
        ASSERT(bip->bli_flags & XFS_BLI_STALE);
        ASSERT(xfs_buf_islocked(bp));
        ASSERT(XFS_BUF_ISSTALE(bp));
        ASSERT(bip->__bli_format.blf_flags & XFS_BLF_CANCEL);

        trace_xfs_buf_item_unpin_stale(bip);

        if (remove) {
            /*
             * If we are in a transaction context, we have to
             * remove the log item from the transaction as we are
             * about to release our reference to the buffer.  If we
             * don't, the unlock that occurs later in
             * xfs_trans_uncommit() will try to reference the
             * buffer which we no longer have a hold on.
             */
            if (lip->li_desc)
                xfs_trans_del_item(lip);

            /*
             * Since the transaction no longer refers to the buffer,
             * the buffer should no longer refer to the transaction.
             */
            bp->b_transp = NULL;
        }

        /*
         * If we get called here because of an IO error, we may
         * or may not have the item on the AIL. xfs_trans_ail_delete()
         * will take care of that situation.
         * xfs_trans_ail_delete() drops the AIL lock.
         */
        if (bip->bli_flags & XFS_BLI_STALE_INODE) {
            xfs_buf_do_callbacks(bp);
            bp->b_fspriv = NULL;
            bp->b_iodone = NULL;
        } else {
            spin_lock(&ailp->xa_lock);
            xfs_trans_ail_delete(ailp, lip, SHUTDOWN_LOG_IO_ERROR);
            xfs_buf_item_relse(bp);
            ASSERT(bp->b_fspriv == NULL);
        }
        xfs_buf_relse(bp);
    } else if (freed && remove) {
        /*
         * There are currently two references to the buffer - the active
         * LRU reference and the buf log item. What we are about to do
         * here - simulate a failed IO completion - requires 3
         * references.
         *
         * The LRU reference is removed by the xfs_buf_stale() call. The
         * buf item reference is removed by the xfs_buf_iodone()
         * callback that is run by xfs_buf_do_callbacks() during ioend
         * processing (via the bp->b_iodone callback), and then finally
         * the ioend processing will drop the IO reference if the buffer
         * is marked XBF_ASYNC.
         *
         * Hence we need to take an additional reference here so that IO
         * completion processing doesn't free the buffer prematurely.
         */
        xfs_buf_lock(bp);
        xfs_buf_hold(bp);
        bp->b_flags |= XBF_ASYNC;
        xfs_buf_ioerror(bp, EIO);
        XFS_BUF_UNDONE(bp);
        xfs_buf_stale(bp);
        xfs_buf_ioend(bp, 0);
    }
}
Exemplo n.º 2
0
/*
 * This is called to unpin the buffer associated with the buf log
 * item which was previously pinned with a call to xfs_buf_item_pin().
 *
 * Also drop the reference to the buf item for the current transaction.
 * If the XFS_BLI_STALE flag is set and we are the last reference,
 * then free up the buf log item and unlock the buffer.
 *
 * If the remove flag is set we are called from uncommit in the
 * forced-shutdown path.  If that is true and the reference count on
 * the log item is going to drop to zero we need to free the item's
 * descriptor in the transaction.
 */
STATIC void
xfs_buf_item_unpin(
	struct xfs_log_item	*lip,
	int			remove)
{
	struct xfs_buf_log_item	*bip = BUF_ITEM(lip);
	xfs_buf_t	*bp = bip->bli_buf;
	struct xfs_ail	*ailp = lip->li_ailp;
	int		stale = bip->bli_flags & XFS_BLI_STALE;
	int		freed;

	ASSERT(XFS_BUF_FSPRIVATE(bp, xfs_buf_log_item_t *) == bip);
	ASSERT(atomic_read(&bip->bli_refcount) > 0);

	trace_xfs_buf_item_unpin(bip);

	freed = atomic_dec_and_test(&bip->bli_refcount);

	if (atomic_dec_and_test(&bp->b_pin_count))
		wake_up_all(&bp->b_waiters);

	if (freed && stale) {
		ASSERT(bip->bli_flags & XFS_BLI_STALE);
		ASSERT(XFS_BUF_VALUSEMA(bp) <= 0);
		ASSERT(!(XFS_BUF_ISDELAYWRITE(bp)));
		ASSERT(XFS_BUF_ISSTALE(bp));
		ASSERT(bip->bli_format.blf_flags & XFS_BLF_CANCEL);

		trace_xfs_buf_item_unpin_stale(bip);

		if (remove) {
			/*
			 * If we are in a transaction context, we have to
			 * remove the log item from the transaction as we are
			 * about to release our reference to the buffer.  If we
			 * don't, the unlock that occurs later in
			 * xfs_trans_uncommit() will try to reference the
			 * buffer which we no longer have a hold on.
			 */
			if (lip->li_desc)
				xfs_trans_del_item(lip);

			/*
			 * Since the transaction no longer refers to the buffer,
			 * the buffer should no longer refer to the transaction.
			 */
			XFS_BUF_SET_FSPRIVATE2(bp, NULL);
		}

		/*
		 * If we get called here because of an IO error, we may
		 * or may not have the item on the AIL. xfs_trans_ail_delete()
		 * will take care of that situation.
		 * xfs_trans_ail_delete() drops the AIL lock.
		 */
		if (bip->bli_flags & XFS_BLI_STALE_INODE) {
			xfs_buf_do_callbacks(bp);
			XFS_BUF_SET_FSPRIVATE(bp, NULL);
			XFS_BUF_CLR_IODONE_FUNC(bp);
		} else {
			spin_lock(&ailp->xa_lock);
			xfs_trans_ail_delete(ailp, (xfs_log_item_t *)bip);
			xfs_buf_item_relse(bp);
			ASSERT(XFS_BUF_FSPRIVATE(bp, void *) == NULL);
		}
		xfs_buf_relse(bp);
	}
Exemplo n.º 3
0
/*
 * This is the iodone() function for buffers which have had callbacks
 * attached to them by xfs_buf_attach_iodone().  It should remove each
 * log item from the buffer's list and call the callback of each in turn.
 * When done, the buffer's fsprivate field is set to NULL and the buffer
 * is unlocked with a call to iodone().
 */
void
xfs_buf_iodone_callbacks(
    struct xfs_buf		*bp)
{
    struct xfs_log_item	*lip = bp->b_fspriv;
    struct xfs_mount	*mp = lip->li_mountp;
    static ulong		lasttime;
    static xfs_buftarg_t	*lasttarg;

    if (likely(!xfs_buf_geterror(bp)))
        goto do_callbacks;

    /*
     * If we've already decided to shutdown the filesystem because of
     * I/O errors, there's no point in giving this a retry.
     */
    if (XFS_FORCED_SHUTDOWN(mp)) {
        xfs_buf_stale(bp);
        XFS_BUF_DONE(bp);
        trace_xfs_buf_item_iodone(bp, _RET_IP_);
        goto do_callbacks;
    }

    if (bp->b_target != lasttarg ||
            time_after(jiffies, (lasttime + 5*HZ))) {
        lasttime = jiffies;
        xfs_buf_ioerror_alert(bp, __func__);
    }
    lasttarg = bp->b_target;

    /*
     * If the write was asynchronous then no one will be looking for the
     * error.  Clear the error state and write the buffer out again.
     *
     * XXX: This helps against transient write errors, but we need to find
     * a way to shut the filesystem down if the writes keep failing.
     *
     * In practice we'll shut the filesystem down soon as non-transient
     * erorrs tend to affect the whole device and a failing log write
     * will make us give up.  But we really ought to do better here.
     */
    if (XFS_BUF_ISASYNC(bp)) {
        ASSERT(bp->b_iodone != NULL);

        trace_xfs_buf_item_iodone_async(bp, _RET_IP_);

        xfs_buf_ioerror(bp, 0); /* errno of 0 unsets the flag */

        if (!XFS_BUF_ISSTALE(bp)) {
            bp->b_flags |= XBF_WRITE | XBF_ASYNC | XBF_DONE;
            xfs_buf_iorequest(bp);
        } else {
            xfs_buf_relse(bp);
        }

        return;
    }

    /*
     * If the write of the buffer was synchronous, we want to make
     * sure to return the error to the caller of xfs_bwrite().
     */
    xfs_buf_stale(bp);
    XFS_BUF_DONE(bp);

    trace_xfs_buf_error_relse(bp, _RET_IP_);

do_callbacks:
    xfs_buf_do_callbacks(bp);
    bp->b_fspriv = NULL;
    bp->b_iodone = NULL;
    xfs_buf_ioend(bp, 0);
}