Пример #1
0
/*
 * Wrapper around bwrite() so that we can trap
 * write errors, and act accordingly.
 */
int
xfs_bwrite(
	struct xfs_mount *mp,
	struct xfs_buf	 *bp)
{
	int	error;

	/*
	 * XXXsup how does this work for quotas.
	 */
	XFS_BUF_SET_BDSTRAT_FUNC(bp, xfs_bdstrat_cb);
	XFS_BUF_SET_FSPRIVATE3(bp, mp);
	XFS_BUF_WRITE(bp);

	if ((error = XFS_bwrite(bp))) {
		ASSERT(mp);
		/*
		 * Cannot put a buftrace here since if the buffer is not
		 * B_HOLD then we will brelse() the buffer before returning
		 * from bwrite and we could be tracing a buffer that has
		 * been reused.
		 */
		xfs_force_shutdown(mp, SHUTDOWN_META_IO_ERROR);
	}
	return (error);
}
Пример #2
0
/*
 * Delete the given item from the AIL.  It must already be in
 * the AIL.
 *
 * Wakeup anyone with an lsn less than item's lsn.    If the item
 * we delete in the AIL is the minimum one, update the tail lsn in the
 * log manager.
 *
 * Clear the IN_AIL flag from the item, reset its lsn to 0, and
 * bump the AIL's generation count to indicate that the tree
 * has changed.
 *
 * This function must be called with the AIL lock held.  The lock
 * is dropped before returning, so the caller must pass in the
 * cookie returned by AIL_LOCK.
 */
void
xfs_trans_delete_ail(
	xfs_mount_t	*mp,
	xfs_log_item_t	*lip,
	unsigned long	s)
{
	xfs_ail_entry_t		*ailp;
	xfs_log_item_t		*dlip;
	xfs_log_item_t		*mlip;

	if (lip->li_flags & XFS_LI_IN_AIL) {
		ailp = &(mp->m_ail);
		mlip = xfs_ail_min(ailp);
		dlip = xfs_ail_delete(ailp, lip);
		ASSERT(dlip == lip);


		lip->li_flags &= ~XFS_LI_IN_AIL;
		lip->li_lsn = 0;
		mp->m_ail_gen++;

		if (mlip == dlip) {
			mlip = xfs_ail_min(&(mp->m_ail));
			AIL_UNLOCK(mp, s);
			xfs_log_move_tail(mp, (mlip ? mlip->li_lsn : 0));
		} else {
			AIL_UNLOCK(mp, s);
		}
	}
	else {
		/*
		 * If the file system is not being shutdown, we are in
		 * serious trouble if we get to this stage.
		 */
		if (XFS_FORCED_SHUTDOWN(mp))
			AIL_UNLOCK(mp, s);
		else {
			xfs_cmn_err(XFS_PTAG_AILDELETE, CE_ALERT, mp,
		"%s: attempting to delete a log item that is not in the AIL",
					__FUNCTION__);
			AIL_UNLOCK(mp, s);
			xfs_force_shutdown(mp, SHUTDOWN_CORRUPT_INCORE);
		}
	}
}
Пример #3
0
 /*ARGSUSED*/
int
_xfs_trans_commit(
	xfs_trans_t	*tp,
	uint		flags,
	xfs_lsn_t	*commit_lsn_p,
	int		*log_flushed)
{
	xfs_log_iovec_t		*log_vector;
	int			nvec;
	xfs_mount_t		*mp;
	xfs_lsn_t		commit_lsn;
	/* REFERENCED */
	int			error;
	int			log_flags;
	int			sync;
#define	XFS_TRANS_LOGVEC_COUNT	16
	xfs_log_iovec_t		log_vector_fast[XFS_TRANS_LOGVEC_COUNT];
	void			*commit_iclog;
	int			shutdown;

	commit_lsn = -1;

	/*
	 * Determine whether this commit is releasing a permanent
	 * log reservation or not.
	 */
	if (flags & XFS_TRANS_RELEASE_LOG_RES) {
		ASSERT(tp->t_flags & XFS_TRANS_PERM_LOG_RES);
		log_flags = XFS_LOG_REL_PERM_RESERV;
	} else {
		log_flags = 0;
	}
	mp = tp->t_mountp;

	/*
	 * If there is nothing to be logged by the transaction,
	 * then unlock all of the items associated with the
	 * transaction and free the transaction structure.
	 * Also make sure to return any reserved blocks to
	 * the free pool.
	 */
shut_us_down:
	shutdown = XFS_FORCED_SHUTDOWN(mp) ? EIO : 0;
	if (!(tp->t_flags & XFS_TRANS_DIRTY) || shutdown) {
		xfs_trans_unreserve_and_mod_sb(tp);
		/*
		 * It is indeed possible for the transaction to be
		 * not dirty but the dqinfo portion to be. All that
		 * means is that we have some (non-persistent) quota
		 * reservations that need to be unreserved.
		 */
		XFS_TRANS_UNRESERVE_AND_MOD_DQUOTS(mp, tp);
		if (tp->t_ticket) {
			commit_lsn = xfs_log_done(mp, tp->t_ticket,
							NULL, log_flags);
			if (commit_lsn == -1 && !shutdown)
				shutdown = XFS_ERROR(EIO);
		}
                PFLAGS_RESTORE_FSTRANS(&tp->t_pflags);
		xfs_trans_free_items(tp, shutdown? XFS_TRANS_ABORT : 0);
		xfs_trans_free_busy(tp);
		xfs_trans_free(tp);
		XFS_STATS_INC(xs_trans_empty);
		if (commit_lsn_p)
			*commit_lsn_p = commit_lsn;
		return (shutdown);
	}
	ASSERT(tp->t_ticket != NULL);

	/*
	 * If we need to update the superblock, then do it now.
	 */
	if (tp->t_flags & XFS_TRANS_SB_DIRTY) {
		xfs_trans_apply_sb_deltas(tp);
	}
	XFS_TRANS_APPLY_DQUOT_DELTAS(mp, tp);

	/*
	 * Ask each log item how many log_vector entries it will
	 * need so we can figure out how many to allocate.
	 * Try to avoid the kmem_alloc() call in the common case
	 * by using a vector from the stack when it fits.
	 */
	nvec = xfs_trans_count_vecs(tp);
	if (nvec == 0) {
		xfs_force_shutdown(mp, XFS_LOG_IO_ERROR);
		goto shut_us_down;
	} else if (nvec <= XFS_TRANS_LOGVEC_COUNT) {
		log_vector = log_vector_fast;
	} else {
		log_vector = (xfs_log_iovec_t *)kmem_alloc(nvec *
						   sizeof(xfs_log_iovec_t),
						   KM_SLEEP);
	}

	/*
	 * Fill in the log_vector and pin the logged items, and
	 * then write the transaction to the log.
	 */
	xfs_trans_fill_vecs(tp, log_vector);

	error = xfs_log_write(mp, log_vector, nvec, tp->t_ticket, &(tp->t_lsn));

	/*
	 * The transaction is committed incore here, and can go out to disk
	 * at any time after this call.  However, all the items associated
	 * with the transaction are still locked and pinned in memory.
	 */
	commit_lsn = xfs_log_done(mp, tp->t_ticket, &commit_iclog, log_flags);

	tp->t_commit_lsn = commit_lsn;
	if (nvec > XFS_TRANS_LOGVEC_COUNT) {
		kmem_free(log_vector, nvec * sizeof(xfs_log_iovec_t));
	}

	if (commit_lsn_p)
		*commit_lsn_p = commit_lsn;

	/*
	 * If we got a log write error. Unpin the logitems that we
	 * had pinned, clean up, free trans structure, and return error.
	 */
	if (error || commit_lsn == -1) {
                PFLAGS_RESTORE_FSTRANS(&tp->t_pflags);
		xfs_trans_uncommit(tp, flags|XFS_TRANS_ABORT);
		return XFS_ERROR(EIO);
	}

	/*
	 * Once the transaction has committed, unused
	 * reservations need to be released and changes to
	 * the superblock need to be reflected in the in-core
	 * version.  Do that now.
	 */
	xfs_trans_unreserve_and_mod_sb(tp);

	sync = tp->t_flags & XFS_TRANS_SYNC;

	/*
	 * Tell the LM to call the transaction completion routine
	 * when the log write with LSN commit_lsn completes (e.g.
	 * when the transaction commit really hits the on-disk log).
	 * After this call we cannot reference tp, because the call
	 * can happen at any time and the call will free the transaction
	 * structure pointed to by tp.  The only case where we call
	 * the completion routine (xfs_trans_committed) directly is
	 * if the log is turned off on a debug kernel or we're
	 * running in simulation mode (the log is explicitly turned
	 * off).
	 */
	tp->t_logcb.cb_func = (void(*)(void*, int))xfs_trans_committed;
	tp->t_logcb.cb_arg = tp;

	/*
	 * We need to pass the iclog buffer which was used for the
	 * transaction commit record into this function, and attach
	 * the callback to it. The callback must be attached before
	 * the items are unlocked to avoid racing with other threads
	 * waiting for an item to unlock.
	 */
	shutdown = xfs_log_notify(mp, commit_iclog, &(tp->t_logcb));

	/*
	 * Mark this thread as no longer being in a transaction
	 */
	PFLAGS_RESTORE_FSTRANS(&tp->t_pflags);

	/*
	 * Once all the items of the transaction have been copied
	 * to the in core log and the callback is attached, the
	 * items can be unlocked.
	 *
	 * This will free descriptors pointing to items which were
	 * not logged since there is nothing more to do with them.
	 * For items which were logged, we will keep pointers to them
	 * so they can be unpinned after the transaction commits to disk.
	 * This will also stamp each modified meta-data item with
	 * the commit lsn of this transaction for dependency tracking
	 * purposes.
	 */
	xfs_trans_unlock_items(tp, commit_lsn);

	/*
	 * If we detected a log error earlier, finish committing
	 * the transaction now (unpin log items, etc).
	 *
	 * Order is critical here, to avoid using the transaction
	 * pointer after its been freed (by xfs_trans_committed
	 * either here now, or as a callback).  We cannot do this
	 * step inside xfs_log_notify as was done earlier because
	 * of this issue.
	 */
	if (shutdown)
		xfs_trans_committed(tp, XFS_LI_ABORTED);

	/*
	 * Now that the xfs_trans_committed callback has been attached,
	 * and the items are released we can finally allow the iclog to
	 * go to disk.
	 */
	error = xfs_log_release_iclog(mp, commit_iclog);

	/*
	 * If the transaction needs to be synchronous, then force the
	 * log out now and wait for it.
	 */
	if (sync) {
		if (!error) {
			error = _xfs_log_force(mp, commit_lsn,
				      XFS_LOG_FORCE | XFS_LOG_SYNC,
				      log_flushed);
		}
		XFS_STATS_INC(xs_trans_sync);
	} else {
		XFS_STATS_INC(xs_trans_async);
	}

	return (error);
}
Пример #4
0
/*
 * Unlock all of the transaction's items and free the transaction.
 * The transaction must not have modified any of its items, because
 * there is no way to restore them to their previous state.
 *
 * If the transaction has made a log reservation, make sure to release
 * it as well.
 */
void
xfs_trans_cancel(
	xfs_trans_t		*tp,
	int			flags)
{
	int			log_flags;
#ifdef DEBUG
	xfs_log_item_chunk_t	*licp;
	xfs_log_item_desc_t	*lidp;
	xfs_log_item_t		*lip;
	int			i;
#endif
	xfs_mount_t		*mp = tp->t_mountp;

	/*
	 * See if the caller is being too lazy to figure out if
	 * the transaction really needs an abort.
	 */
	if ((flags & XFS_TRANS_ABORT) && !(tp->t_flags & XFS_TRANS_DIRTY))
		flags &= ~XFS_TRANS_ABORT;
	/*
	 * See if the caller is relying on us to shut down the
	 * filesystem.  This happens in paths where we detect
	 * corruption and decide to give up.
	 */
	if ((tp->t_flags & XFS_TRANS_DIRTY) && !XFS_FORCED_SHUTDOWN(mp)) {
		XFS_ERROR_REPORT("xfs_trans_cancel", XFS_ERRLEVEL_LOW, mp);
		xfs_force_shutdown(mp, XFS_CORRUPT_INCORE);
	}
#ifdef DEBUG
	if (!(flags & XFS_TRANS_ABORT)) {
		licp = &(tp->t_items);
		while (licp != NULL) {
			lidp = licp->lic_descs;
			for (i = 0; i < licp->lic_unused; i++, lidp++) {
				if (XFS_LIC_ISFREE(licp, i)) {
					continue;
				}

				lip = lidp->lid_item;
				if (!XFS_FORCED_SHUTDOWN(mp))
					ASSERT(!(lip->li_type == XFS_LI_EFD));
			}
			licp = licp->lic_next;
		}
	}
#endif
	xfs_trans_unreserve_and_mod_sb(tp);
	XFS_TRANS_UNRESERVE_AND_MOD_DQUOTS(mp, tp);

	if (tp->t_ticket) {
		if (flags & XFS_TRANS_RELEASE_LOG_RES) {
			ASSERT(tp->t_flags & XFS_TRANS_PERM_LOG_RES);
			log_flags = XFS_LOG_REL_PERM_RESERV;
		} else {
			log_flags = 0;
		}
		xfs_log_done(mp, tp->t_ticket, NULL, log_flags);
	}

	/* mark this thread as no longer being in a transaction */
        PFLAGS_RESTORE_FSTRANS(&tp->t_pflags);

	xfs_trans_free_items(tp, flags);
	xfs_trans_free_busy(tp);
	xfs_trans_free(tp);
}
Пример #5
0
/*
 * Routine to be called at transaction's end by xfs_bmapi, xfs_bunmapi
 * caller.  Frees all the extents that need freeing, which must be done
 * last due to locking considerations.  We never free any extents in
 * the first transaction.
 *
 * Return 1 if the given transaction was committed and a new one
 * started, and 0 otherwise in the committed parameter.
 */
int						/* error */
xfs_bmap_finish(
	xfs_trans_t		**tp,		/* transaction pointer addr */
	xfs_bmap_free_t		*flist,		/* i/o: list extents to free */
	int			*committed)	/* xact committed or not */
{
	xfs_efd_log_item_t	*efd;		/* extent free data */
	xfs_efi_log_item_t	*efi;		/* extent free intention */
	int			error;		/* error return value */
	xfs_bmap_free_item_t	*free;		/* free extent item */
	struct xfs_trans_res	tres;		/* new log reservation */
	xfs_mount_t		*mp;		/* filesystem mount structure */
	xfs_bmap_free_item_t	*next;		/* next item on free list */
	xfs_trans_t		*ntp;		/* new transaction pointer */

	ASSERT((*tp)->t_flags & XFS_TRANS_PERM_LOG_RES);
	if (flist->xbf_count == 0) {
		*committed = 0;
		return 0;
	}
	ntp = *tp;
	efi = xfs_trans_get_efi(ntp, flist->xbf_count);
	for (free = flist->xbf_first; free; free = free->xbfi_next)
		xfs_trans_log_efi_extent(ntp, efi, free->xbfi_startblock,
			free->xbfi_blockcount);

	tres.tr_logres = ntp->t_log_res;
	tres.tr_logcount = ntp->t_log_count;
	tres.tr_logflags = XFS_TRANS_PERM_LOG_RES;
	ntp = xfs_trans_dup(*tp);
	error = xfs_trans_commit(*tp, 0);
	*tp = ntp;
	*committed = 1;
	/*
	 * We have a new transaction, so we should return committed=1,
	 * even though we're returning an error.
	 */
	if (error)
		return error;

	/*
	 * transaction commit worked ok so we can drop the extra ticket
	 * reference that we gained in xfs_trans_dup()
	 */
	xfs_log_ticket_put(ntp->t_ticket);

	error = xfs_trans_reserve(ntp, &tres, 0, 0);
	if (error)
		return error;
	efd = xfs_trans_get_efd(ntp, efi, flist->xbf_count);
	for (free = flist->xbf_first; free != NULL; free = next) {
		next = free->xbfi_next;
		if ((error = xfs_free_extent(ntp, free->xbfi_startblock,
				free->xbfi_blockcount))) {
			/*
			 * The bmap free list will be cleaned up at a
			 * higher level.  The EFI will be canceled when
			 * this transaction is aborted.
			 * Need to force shutdown here to make sure it
			 * happens, since this transaction may not be
			 * dirty yet.
			 */
			mp = ntp->t_mountp;
			if (!XFS_FORCED_SHUTDOWN(mp))
				xfs_force_shutdown(mp,
						   (error == EFSCORRUPTED) ?
						   SHUTDOWN_CORRUPT_INCORE :
						   SHUTDOWN_META_IO_ERROR);
			return error;
		}
		xfs_trans_log_efd_extent(ntp, efd, free->xbfi_startblock,
			free->xbfi_blockcount);
		xfs_bmap_del_free(flist, NULL, free);
	}
	return 0;
}
/*
 * Write a modified dquot to disk.
 * The dquot must be locked and the flush lock too taken by caller.
 * The flush lock will not be unlocked until the dquot reaches the disk,
 * but the dquot is free to be unlocked and modified by the caller
 * in the interim. Dquot is still locked on return. This behavior is
 * identical to that of inodes.
 */
int
xfs_qm_dqflush(
	xfs_dquot_t		*dqp,
	uint			flags)
{
	xfs_mount_t		*mp;
	xfs_buf_t		*bp;
	xfs_disk_dquot_t	*ddqp;
	int			error;
	SPLDECL(s);

	ASSERT(XFS_DQ_IS_LOCKED(dqp));
	ASSERT(XFS_DQ_IS_FLUSH_LOCKED(dqp));
	xfs_dqtrace_entry(dqp, "DQFLUSH");

	/*
	 * If not dirty, nada.
	 */
	if (!XFS_DQ_IS_DIRTY(dqp)) {
		xfs_dqfunlock(dqp);
		return (0);
	}

	/*
	 * Cant flush a pinned dquot. Wait for it.
	 */
	xfs_qm_dqunpin_wait(dqp);

	/*
	 * This may have been unpinned because the filesystem is shutting
	 * down forcibly. If that's the case we must not write this dquot
	 * to disk, because the log record didn't make it to disk!
	 */
	if (XFS_FORCED_SHUTDOWN(dqp->q_mount)) {
		dqp->dq_flags &= ~(XFS_DQ_DIRTY);
		xfs_dqfunlock(dqp);
		return XFS_ERROR(EIO);
	}

	/*
	 * Get the buffer containing the on-disk dquot
	 * We don't need a transaction envelope because we know that the
	 * the ondisk-dquot has already been allocated for.
	 */
	if ((error = xfs_qm_dqtobp(NULL, dqp, &ddqp, &bp, XFS_QMOPT_DOWARN))) {
		xfs_dqtrace_entry(dqp, "DQTOBP FAIL");
		ASSERT(error != ENOENT);
		/*
		 * Quotas could have gotten turned off (ESRCH)
		 */
		xfs_dqfunlock(dqp);
		return (error);
	}

	if (xfs_qm_dqcheck(&dqp->q_core, be32_to_cpu(ddqp->d_id),
			   0, XFS_QMOPT_DOWARN, "dqflush (incore copy)")) {
		xfs_force_shutdown(dqp->q_mount, XFS_CORRUPT_INCORE);
		return XFS_ERROR(EIO);
	}

	/* This is the only portion of data that needs to persist */
	memcpy(ddqp, &(dqp->q_core), sizeof(xfs_disk_dquot_t));

	/*
	 * Clear the dirty field and remember the flush lsn for later use.
	 */
	dqp->dq_flags &= ~(XFS_DQ_DIRTY);
	mp = dqp->q_mount;

	/* lsn is 64 bits */
	AIL_LOCK(mp, s);
	dqp->q_logitem.qli_flush_lsn = dqp->q_logitem.qli_item.li_lsn;
	AIL_UNLOCK(mp, s);

	/*
	 * Attach an iodone routine so that we can remove this dquot from the
	 * AIL and release the flush lock once the dquot is synced to disk.
	 */
	xfs_buf_attach_iodone(bp, (void(*)(xfs_buf_t *, xfs_log_item_t *))
			      xfs_qm_dqflush_done, &(dqp->q_logitem.qli_item));
	/*
	 * If the buffer is pinned then push on the log so we won't
	 * get stuck waiting in the write for too long.
	 */
	if (XFS_BUF_ISPINNED(bp)) {
		xfs_dqtrace_entry(dqp, "DQFLUSH LOG FORCE");
		xfs_log_force(mp, (xfs_lsn_t)0, XFS_LOG_FORCE);
	}

	if (flags & XFS_QMOPT_DELWRI) {
		xfs_bdwrite(mp, bp);
	} else if (flags & XFS_QMOPT_ASYNC) {
		xfs_bawrite(mp, bp);
	} else {
		error = xfs_bwrite(mp, bp);
	}
	xfs_dqtrace_entry(dqp, "DQFLUSH END");
	/*
	 * dqp is still locked, but caller is free to unlock it now.
	 */
	return (error);

}
/*
 * Turn off quota accounting and/or enforcement for all udquots and/or
 * gdquots. Called only at unmount time.
 *
 * This assumes that there are no dquots of this file system cached
 * incore, and modifies the ondisk dquot directly. Therefore, for example,
 * it is an error to call this twice, without purging the cache.
 */
int
xfs_qm_scall_quotaoff(
    xfs_mount_t		*mp,
    uint			flags)
{
    struct xfs_quotainfo	*q = mp->m_quotainfo;
    uint			dqtype;
    int			error;
    uint			inactivate_flags;
    xfs_qoff_logitem_t	*qoffstart;

    /*
     * No file system can have quotas enabled on disk but not in core.
     * Note that quota utilities (like quotaoff) _expect_
     * errno == EEXIST here.
     */
    if ((mp->m_qflags & flags) == 0)
        return XFS_ERROR(EEXIST);
    error = 0;

    flags &= (XFS_ALL_QUOTA_ACCT | XFS_ALL_QUOTA_ENFD);

    /*
     * We don't want to deal with two quotaoffs messing up each other,
     * so we're going to serialize it. quotaoff isn't exactly a performance
     * critical thing.
     * If quotaoff, then we must be dealing with the root filesystem.
     */
    ASSERT(q);
    mutex_lock(&q->qi_quotaofflock);

    /*
     * If we're just turning off quota enforcement, change mp and go.
     */
    if ((flags & XFS_ALL_QUOTA_ACCT) == 0) {
        mp->m_qflags &= ~(flags);

        spin_lock(&mp->m_sb_lock);
        mp->m_sb.sb_qflags = mp->m_qflags;
        spin_unlock(&mp->m_sb_lock);
        mutex_unlock(&q->qi_quotaofflock);

        /* XXX what to do if error ? Revert back to old vals incore ? */
        error = xfs_qm_write_sb_changes(mp, XFS_SB_QFLAGS);
        return (error);
    }

    dqtype = 0;
    inactivate_flags = 0;
    /*
     * If accounting is off, we must turn enforcement off, clear the
     * quota 'CHKD' certificate to make it known that we have to
     * do a quotacheck the next time this quota is turned on.
     */
    if (flags & XFS_UQUOTA_ACCT) {
        dqtype |= XFS_QMOPT_UQUOTA;
        flags |= (XFS_UQUOTA_CHKD | XFS_UQUOTA_ENFD);
        inactivate_flags |= XFS_UQUOTA_ACTIVE;
    }
    if (flags & XFS_GQUOTA_ACCT) {
        dqtype |= XFS_QMOPT_GQUOTA;
        flags |= (XFS_GQUOTA_CHKD | XFS_GQUOTA_ENFD);
        inactivate_flags |= XFS_GQUOTA_ACTIVE;
    }
    if (flags & XFS_PQUOTA_ACCT) {
        dqtype |= XFS_QMOPT_PQUOTA;
        flags |= (XFS_PQUOTA_CHKD | XFS_PQUOTA_ENFD);
        inactivate_flags |= XFS_PQUOTA_ACTIVE;
    }

    /*
     * Nothing to do?  Don't complain. This happens when we're just
     * turning off quota enforcement.
     */
    if ((mp->m_qflags & flags) == 0)
        goto out_unlock;

    /*
     * Write the LI_QUOTAOFF log record, and do SB changes atomically,
     * and synchronously. If we fail to write, we should abort the
     * operation as it cannot be recovered safely if we crash.
     */
    error = xfs_qm_log_quotaoff(mp, &qoffstart, flags);
    if (error)
        goto out_unlock;

    /*
     * Next we clear the XFS_MOUNT_*DQ_ACTIVE bit(s) in the mount struct
     * to take care of the race between dqget and quotaoff. We don't take
     * any special locks to reset these bits. All processes need to check
     * these bits *after* taking inode lock(s) to see if the particular
     * quota type is in the process of being turned off. If *ACTIVE, it is
     * guaranteed that all dquot structures and all quotainode ptrs will all
     * stay valid as long as that inode is kept locked.
     *
     * There is no turning back after this.
     */
    mp->m_qflags &= ~inactivate_flags;

    /*
     * Give back all the dquot reference(s) held by inodes.
     * Here we go thru every single incore inode in this file system, and
     * do a dqrele on the i_udquot/i_gdquot that it may have.
     * Essentially, as long as somebody has an inode locked, this guarantees
     * that quotas will not be turned off. This is handy because in a
     * transaction once we lock the inode(s) and check for quotaon, we can
     * depend on the quota inodes (and other things) being valid as long as
     * we keep the lock(s).
     */
    xfs_qm_dqrele_all_inodes(mp, flags);

    /*
     * Next we make the changes in the quota flag in the mount struct.
     * This isn't protected by a particular lock directly, because we
     * don't want to take a mrlock every time we depend on quotas being on.
     */
    mp->m_qflags &= ~flags;

    /*
     * Go through all the dquots of this file system and purge them,
     * according to what was turned off.
     */
    xfs_qm_dqpurge_all(mp, dqtype);

    /*
     * Transactions that had started before ACTIVE state bit was cleared
     * could have logged many dquots, so they'd have higher LSNs than
     * the first QUOTAOFF log record does. If we happen to crash when
     * the tail of the log has gone past the QUOTAOFF record, but
     * before the last dquot modification, those dquots __will__
     * recover, and that's not good.
     *
     * So, we have QUOTAOFF start and end logitems; the start
     * logitem won't get overwritten until the end logitem appears...
     */
    error = xfs_qm_log_quotaoff_end(mp, qoffstart, flags);
    if (error) {
        /* We're screwed now. Shutdown is the only option. */
        xfs_force_shutdown(mp, SHUTDOWN_CORRUPT_INCORE);
        goto out_unlock;
    }

    /*
     * If all quotas are completely turned off, close shop.
     */
    if (mp->m_qflags == 0) {
        mutex_unlock(&q->qi_quotaofflock);
        xfs_qm_destroy_quotainfo(mp);
        return (0);
    }

    /*
     * Release our quotainode references if we don't need them anymore.
     */
    if ((dqtype & XFS_QMOPT_UQUOTA) && q->qi_uquotaip) {
        IRELE(q->qi_uquotaip);
        q->qi_uquotaip = NULL;
    }
    if ((dqtype & XFS_QMOPT_GQUOTA) && q->qi_gquotaip) {
        IRELE(q->qi_gquotaip);
        q->qi_gquotaip = NULL;
    }
    if ((dqtype & XFS_QMOPT_PQUOTA) && q->qi_pquotaip) {
        IRELE(q->qi_pquotaip);
        q->qi_pquotaip = NULL;
    }

out_unlock:
    mutex_unlock(&q->qi_quotaofflock);
    return error;
}
Пример #8
0
/*
 * Remap parts of a file's data fork after a successful CoW.
 */
int
xfs_reflink_end_cow(
	struct xfs_inode		*ip,
	xfs_off_t			offset,
	xfs_off_t			count)
{
	struct xfs_ifork		*ifp = XFS_IFORK_PTR(ip, XFS_COW_FORK);
	struct xfs_bmbt_irec		got, del;
	struct xfs_trans		*tp;
	xfs_fileoff_t			offset_fsb;
	xfs_fileoff_t			end_fsb;
	xfs_fsblock_t			firstfsb;
	struct xfs_defer_ops		dfops;
	int				error;
	unsigned int			resblks;
	xfs_filblks_t			rlen;
	xfs_extnum_t			idx;

	trace_xfs_reflink_end_cow(ip, offset, count);

	/* No COW extents?  That's easy! */
	if (ifp->if_bytes == 0)
		return 0;

	offset_fsb = XFS_B_TO_FSBT(ip->i_mount, offset);
	end_fsb = XFS_B_TO_FSB(ip->i_mount, offset + count);

	/*
	 * Start a rolling transaction to switch the mappings.  We're
	 * unlikely ever to have to remap 16T worth of single-block
	 * extents, so just cap the worst case extent count to 2^32-1.
	 * Stick a warning in just in case, and avoid 64-bit division.
	 */
	BUILD_BUG_ON(MAX_RW_COUNT > UINT_MAX);
	if (end_fsb - offset_fsb > UINT_MAX) {
		error = -EFSCORRUPTED;
		xfs_force_shutdown(ip->i_mount, SHUTDOWN_CORRUPT_INCORE);
		ASSERT(0);
		goto out;
	}
	resblks = XFS_NEXTENTADD_SPACE_RES(ip->i_mount,
			(unsigned int)(end_fsb - offset_fsb),
			XFS_DATA_FORK);
	error = xfs_trans_alloc(ip->i_mount, &M_RES(ip->i_mount)->tr_write,
			resblks, 0, 0, &tp);
	if (error)
		goto out;

	xfs_ilock(ip, XFS_ILOCK_EXCL);
	xfs_trans_ijoin(tp, ip, 0);

	/* If there is a hole at end_fsb - 1 go to the previous extent */
	if (!xfs_iext_lookup_extent(ip, ifp, end_fsb - 1, &idx, &got) ||
	    got.br_startoff > end_fsb) {
		/*
		 * In case of racing, overlapping AIO writes no COW extents
		 * might be left by the time I/O completes for the loser of
		 * the race.  In that case we are done.
		 */
		if (idx <= 0)
			goto out_cancel;
		xfs_iext_get_extent(ifp, --idx, &got);
	}

	/* Walk backwards until we're out of the I/O range... */
	while (got.br_startoff + got.br_blockcount > offset_fsb) {
		del = got;
		xfs_trim_extent(&del, offset_fsb, end_fsb - offset_fsb);

		/* Extent delete may have bumped idx forward */
		if (!del.br_blockcount) {
			idx--;
			goto next_extent;
		}

		ASSERT(!isnullstartblock(got.br_startblock));

		/*
		 * Don't remap unwritten extents; these are
		 * speculatively preallocated CoW extents that have been
		 * allocated but have not yet been involved in a write.
		 */
		if (got.br_state == XFS_EXT_UNWRITTEN) {
			idx--;
			goto next_extent;
		}

		/* Unmap the old blocks in the data fork. */
		xfs_defer_init(&dfops, &firstfsb);
		rlen = del.br_blockcount;
		error = __xfs_bunmapi(tp, ip, del.br_startoff, &rlen, 0, 1,
				&firstfsb, &dfops);
		if (error)
			goto out_defer;

		/* Trim the extent to whatever got unmapped. */
		if (rlen) {
			xfs_trim_extent(&del, del.br_startoff + rlen,
				del.br_blockcount - rlen);
		}
		trace_xfs_reflink_cow_remap(ip, &del);

		/* Free the CoW orphan record. */
		error = xfs_refcount_free_cow_extent(tp->t_mountp, &dfops,
				del.br_startblock, del.br_blockcount);
		if (error)
			goto out_defer;

		/* Map the new blocks into the data fork. */
		error = xfs_bmap_map_extent(tp->t_mountp, &dfops, ip, &del);
		if (error)
			goto out_defer;

		/* Remove the mapping from the CoW fork. */
		xfs_bmap_del_extent_cow(ip, &idx, &got, &del);

		xfs_defer_ijoin(&dfops, ip);
		error = xfs_defer_finish(&tp, &dfops);
		if (error)
			goto out_defer;
next_extent:
		if (!xfs_iext_get_extent(ifp, idx, &got))
			break;
	}

	error = xfs_trans_commit(tp);
	xfs_iunlock(ip, XFS_ILOCK_EXCL);
	if (error)
		goto out;
	return 0;

out_defer:
	xfs_defer_cancel(&dfops);
out_cancel:
	xfs_trans_cancel(tp);
	xfs_iunlock(ip, XFS_ILOCK_EXCL);
out:
	trace_xfs_reflink_end_cow_error(ip, error, _RET_IP_);
	return error;
}
Пример #9
0
/*
 * Write a modified dquot to disk.
 * The dquot must be locked and the flush lock too taken by caller.
 * The flush lock will not be unlocked until the dquot reaches the disk,
 * but the dquot is free to be unlocked and modified by the caller
 * in the interim. Dquot is still locked on return. This behavior is
 * identical to that of inodes.
 */
int
xfs_qm_dqflush(
	struct xfs_dquot	*dqp,
	struct xfs_buf		**bpp)
{
	struct xfs_mount	*mp = dqp->q_mount;
	struct xfs_buf		*bp;
	struct xfs_disk_dquot	*ddqp;
	int			error;

	ASSERT(XFS_DQ_IS_LOCKED(dqp));
	ASSERT(!completion_done(&dqp->q_flush));

	trace_xfs_dqflush(dqp);

	*bpp = NULL;

	xfs_qm_dqunpin_wait(dqp);

	/*
	 * This may have been unpinned because the filesystem is shutting
	 * down forcibly. If that's the case we must not write this dquot
	 * to disk, because the log record didn't make it to disk.
	 *
	 * We also have to remove the log item from the AIL in this case,
	 * as we wait for an emptry AIL as part of the unmount process.
	 */
	if (XFS_FORCED_SHUTDOWN(mp)) {
		struct xfs_log_item	*lip = &dqp->q_logitem.qli_item;
		dqp->dq_flags &= ~XFS_DQ_DIRTY;

		spin_lock(&mp->m_ail->xa_lock);
		if (lip->li_flags & XFS_LI_IN_AIL)
			xfs_trans_ail_delete(mp->m_ail, lip,
					     SHUTDOWN_CORRUPT_INCORE);
		else
			spin_unlock(&mp->m_ail->xa_lock);
		error = XFS_ERROR(EIO);
		goto out_unlock;
	}

	/*
	 * Get the buffer containing the on-disk dquot
	 */
	error = xfs_trans_read_buf(mp, NULL, mp->m_ddev_targp, dqp->q_blkno,
				   mp->m_quotainfo->qi_dqchunklen, 0, &bp, NULL);
	if (error)
		goto out_unlock;

	/*
	 * Calculate the location of the dquot inside the buffer.
	 */
	ddqp = bp->b_addr + dqp->q_bufoffset;

	/*
	 * A simple sanity check in case we got a corrupted dquot..
	 */
	error = xfs_dqcheck(mp, &dqp->q_core, be32_to_cpu(ddqp->d_id), 0,
			   XFS_QMOPT_DOWARN, "dqflush (incore copy)");
	if (error) {
		xfs_buf_relse(bp);
		xfs_dqfunlock(dqp);
		xfs_force_shutdown(mp, SHUTDOWN_CORRUPT_INCORE);
		return XFS_ERROR(EIO);
	}

	/* This is the only portion of data that needs to persist */
	memcpy(ddqp, &dqp->q_core, sizeof(xfs_disk_dquot_t));

	/*
	 * Clear the dirty field and remember the flush lsn for later use.
	 */
	dqp->dq_flags &= ~XFS_DQ_DIRTY;

	xfs_trans_ail_copy_lsn(mp->m_ail, &dqp->q_logitem.qli_flush_lsn,
					&dqp->q_logitem.qli_item.li_lsn);

	/*
	 * copy the lsn into the on-disk dquot now while we have the in memory
	 * dquot here. This can't be done later in the write verifier as we
	 * can't get access to the log item at that point in time.
	 *
	 * We also calculate the CRC here so that the on-disk dquot in the
	 * buffer always has a valid CRC. This ensures there is no possibility
	 * of a dquot without an up-to-date CRC getting to disk.
	 */
	if (xfs_sb_version_hascrc(&mp->m_sb)) {
		struct xfs_dqblk *dqb = (struct xfs_dqblk *)ddqp;

		dqb->dd_lsn = cpu_to_be64(dqp->q_logitem.qli_item.li_lsn);
		xfs_update_cksum((char *)dqb, sizeof(struct xfs_dqblk),
				 XFS_DQUOT_CRC_OFF);
	}

	/*
	 * Attach an iodone routine so that we can remove this dquot from the
	 * AIL and release the flush lock once the dquot is synced to disk.
	 */
	xfs_buf_attach_iodone(bp, xfs_qm_dqflush_done,
				  &dqp->q_logitem.qli_item);

	/*
	 * If the buffer is pinned then push on the log so we won't
	 * get stuck waiting in the write for too long.
	 */
	if (xfs_buf_ispinned(bp)) {
		trace_xfs_dqflush_force(dqp);
		xfs_log_force(mp, 0);
	}

	trace_xfs_dqflush_done(dqp);
	*bpp = bp;
	return 0;

out_unlock:
	xfs_dqfunlock(dqp);
	return XFS_ERROR(EIO);
}
Пример #10
0
/*
 * Write a modified dquot to disk.
 * The dquot must be locked and the flush lock too taken by caller.
 * The flush lock will not be unlocked until the dquot reaches the disk,
 * but the dquot is free to be unlocked and modified by the caller
 * in the interim. Dquot is still locked on return. This behavior is
 * identical to that of inodes.
 */
int
xfs_qm_dqflush(
	xfs_dquot_t		*dqp,
	uint			flags)
{
	struct xfs_mount	*mp = dqp->q_mount;
	struct xfs_buf		*bp;
	struct xfs_disk_dquot	*ddqp;
	int			error;

	ASSERT(XFS_DQ_IS_LOCKED(dqp));
	ASSERT(!completion_done(&dqp->q_flush));

	trace_xfs_dqflush(dqp);

	/*
	 * If not dirty, or it's pinned and we are not supposed to block, nada.
	 */
	if (!XFS_DQ_IS_DIRTY(dqp) ||
	    ((flags & SYNC_TRYLOCK) && atomic_read(&dqp->q_pincount) > 0)) {
		xfs_dqfunlock(dqp);
		return 0;
	}
	xfs_qm_dqunpin_wait(dqp);

	/*
	 * This may have been unpinned because the filesystem is shutting
	 * down forcibly. If that's the case we must not write this dquot
	 * to disk, because the log record didn't make it to disk!
	 */
	if (XFS_FORCED_SHUTDOWN(mp)) {
		dqp->dq_flags &= ~XFS_DQ_DIRTY;
		xfs_dqfunlock(dqp);
		return XFS_ERROR(EIO);
	}

	/*
	 * Get the buffer containing the on-disk dquot
	 */
	error = xfs_trans_read_buf(mp, NULL, mp->m_ddev_targp, dqp->q_blkno,
				   mp->m_quotainfo->qi_dqchunklen, 0, &bp);
	if (error) {
		ASSERT(error != ENOENT);
		xfs_dqfunlock(dqp);
		return error;
	}

	/*
	 * Calculate the location of the dquot inside the buffer.
	 */
	ddqp = bp->b_addr + dqp->q_bufoffset;

	/*
	 * A simple sanity check in case we got a corrupted dquot..
	 */
	error = xfs_qm_dqcheck(mp, &dqp->q_core, be32_to_cpu(ddqp->d_id), 0,
			   XFS_QMOPT_DOWARN, "dqflush (incore copy)");
	if (error) {
		xfs_buf_relse(bp);
		xfs_dqfunlock(dqp);
		xfs_force_shutdown(mp, SHUTDOWN_CORRUPT_INCORE);
		return XFS_ERROR(EIO);
	}

	/* This is the only portion of data that needs to persist */
	memcpy(ddqp, &dqp->q_core, sizeof(xfs_disk_dquot_t));

	/*
	 * Clear the dirty field and remember the flush lsn for later use.
	 */
	dqp->dq_flags &= ~XFS_DQ_DIRTY;

	xfs_trans_ail_copy_lsn(mp->m_ail, &dqp->q_logitem.qli_flush_lsn,
					&dqp->q_logitem.qli_item.li_lsn);

	/*
	 * Attach an iodone routine so that we can remove this dquot from the
	 * AIL and release the flush lock once the dquot is synced to disk.
	 */
	xfs_buf_attach_iodone(bp, xfs_qm_dqflush_done,
				  &dqp->q_logitem.qli_item);

	/*
	 * If the buffer is pinned then push on the log so we won't
	 * get stuck waiting in the write for too long.
	 */
	if (xfs_buf_ispinned(bp)) {
		trace_xfs_dqflush_force(dqp);
		xfs_log_force(mp, 0);
	}

	if (flags & SYNC_WAIT)
		error = xfs_bwrite(bp);
	else
		xfs_buf_delwri_queue(bp);

	xfs_buf_relse(bp);

	trace_xfs_dqflush_done(dqp);

	/*
	 * dqp is still locked, but caller is free to unlock it now.
	 */
	return error;

}
int
xfs_qm_scall_quotaoff(
	xfs_mount_t		*mp,
	uint			flags)
{
	struct xfs_quotainfo	*q = mp->m_quotainfo;
	uint			dqtype;
	int			error;
	uint			inactivate_flags;
	xfs_qoff_logitem_t	*qoffstart;

	if ((mp->m_qflags & flags) == 0)
		return XFS_ERROR(EEXIST);
	error = 0;

	flags &= (XFS_ALL_QUOTA_ACCT | XFS_ALL_QUOTA_ENFD);

	ASSERT(q);
	mutex_lock(&q->qi_quotaofflock);

	if ((flags & XFS_ALL_QUOTA_ACCT) == 0) {
		mp->m_qflags &= ~(flags);

		spin_lock(&mp->m_sb_lock);
		mp->m_sb.sb_qflags = mp->m_qflags;
		spin_unlock(&mp->m_sb_lock);
		mutex_unlock(&q->qi_quotaofflock);

		
		error = xfs_qm_write_sb_changes(mp, XFS_SB_QFLAGS);
		return (error);
	}

	dqtype = 0;
	inactivate_flags = 0;
	if (flags & XFS_UQUOTA_ACCT) {
		dqtype |= XFS_QMOPT_UQUOTA;
		flags |= (XFS_UQUOTA_CHKD | XFS_UQUOTA_ENFD);
		inactivate_flags |= XFS_UQUOTA_ACTIVE;
	}
	if (flags & XFS_GQUOTA_ACCT) {
		dqtype |= XFS_QMOPT_GQUOTA;
		flags |= (XFS_OQUOTA_CHKD | XFS_OQUOTA_ENFD);
		inactivate_flags |= XFS_GQUOTA_ACTIVE;
	} else if (flags & XFS_PQUOTA_ACCT) {
		dqtype |= XFS_QMOPT_PQUOTA;
		flags |= (XFS_OQUOTA_CHKD | XFS_OQUOTA_ENFD);
		inactivate_flags |= XFS_PQUOTA_ACTIVE;
	}

	if ((mp->m_qflags & flags) == 0)
		goto out_unlock;

	error = xfs_qm_log_quotaoff(mp, &qoffstart, flags);
	if (error)
		goto out_unlock;

	mp->m_qflags &= ~inactivate_flags;

	xfs_qm_dqrele_all_inodes(mp, flags);

	mp->m_qflags &= ~flags;

	xfs_qm_dqpurge_all(mp, dqtype);

	/*
	 * Transactions that had started before ACTIVE state bit was cleared
	 * could have logged many dquots, so they'd have higher LSNs than
	 * the first QUOTAOFF log record does. If we happen to crash when
	 * the tail of the log has gone past the QUOTAOFF record, but
	 * before the last dquot modification, those dquots __will__
	 * recover, and that's not good.
	 *
	 * So, we have QUOTAOFF start and end logitems; the start
	 * logitem won't get overwritten until the end logitem appears...
	 */
	error = xfs_qm_log_quotaoff_end(mp, qoffstart, flags);
	if (error) {
		
		xfs_force_shutdown(mp, SHUTDOWN_CORRUPT_INCORE);
		goto out_unlock;
	}

	if (((flags & XFS_MOUNT_QUOTA_ALL) == XFS_MOUNT_QUOTA_SET1) ||
	    ((flags & XFS_MOUNT_QUOTA_ALL) == XFS_MOUNT_QUOTA_SET2)) {
		mutex_unlock(&q->qi_quotaofflock);
		xfs_qm_destroy_quotainfo(mp);
		return (0);
	}

	if ((dqtype & XFS_QMOPT_UQUOTA) && q->qi_uquotaip) {
		IRELE(q->qi_uquotaip);
		q->qi_uquotaip = NULL;
	}
	if ((dqtype & (XFS_QMOPT_GQUOTA|XFS_QMOPT_PQUOTA)) && q->qi_gquotaip) {
		IRELE(q->qi_gquotaip);
		q->qi_gquotaip = NULL;
	}

out_unlock:
	mutex_unlock(&q->qi_quotaofflock);
	return error;
}