diff options
Diffstat (limited to 'fs/xfs/xfs_trans.c')
| -rw-r--r-- | fs/xfs/xfs_trans.c | 475 | 
1 files changed, 4 insertions, 471 deletions
diff --git a/fs/xfs/xfs_trans.c b/fs/xfs/xfs_trans.c index 1f35b2feca9..329b06aba1c 100644 --- a/fs/xfs/xfs_trans.c +++ b/fs/xfs/xfs_trans.c @@ -1158,7 +1158,6 @@ xfs_trans_add_item(  	lidp->lid_item = lip;  	lidp->lid_flags = 0; -	lidp->lid_size = 0;  	list_add_tail(&lidp->lid_trans, &tp->t_items);  	lip->li_desc = lidp; @@ -1210,219 +1209,6 @@ xfs_trans_free_items(  	}  } -/* - * Unlock the items associated with a transaction. - * - * Items which were not logged should be freed.  Those which were logged must - * still be tracked so they can be unpinned when the transaction commits. - */ -STATIC void -xfs_trans_unlock_items( -	struct xfs_trans	*tp, -	xfs_lsn_t		commit_lsn) -{ -	struct xfs_log_item_desc *lidp, *next; - -	list_for_each_entry_safe(lidp, next, &tp->t_items, lid_trans) { -		struct xfs_log_item	*lip = lidp->lid_item; - -		lip->li_desc = NULL; - -		if (commit_lsn != NULLCOMMITLSN) -			IOP_COMMITTING(lip, commit_lsn); -		IOP_UNLOCK(lip); - -		/* -		 * Free the descriptor if the item is not dirty -		 * within this transaction. -		 */ -		if (!(lidp->lid_flags & XFS_LID_DIRTY)) -			xfs_trans_free_item_desc(lidp); -	} -} - -/* - * Total up the number of log iovecs needed to commit this - * transaction.  The transaction itself needs one for the - * transaction header.  Ask each dirty item in turn how many - * it needs to get the total. - */ -static uint -xfs_trans_count_vecs( -	struct xfs_trans	*tp) -{ -	int			nvecs; -	struct xfs_log_item_desc *lidp; - -	nvecs = 1; - -	/* In the non-debug case we need to start bailing out if we -	 * didn't find a log_item here, return zero and let trans_commit -	 * deal with it. -	 */ -	if (list_empty(&tp->t_items)) { -		ASSERT(0); -		return 0; -	} - -	list_for_each_entry(lidp, &tp->t_items, lid_trans) { -		/* -		 * Skip items which aren't dirty in this transaction. -		 */ -		if (!(lidp->lid_flags & XFS_LID_DIRTY)) -			continue; -		lidp->lid_size = IOP_SIZE(lidp->lid_item); -		nvecs += lidp->lid_size; -	} - -	return nvecs; -} - -/* - * Fill in the vector with pointers to data to be logged - * by this transaction.  The transaction header takes - * the first vector, and then each dirty item takes the - * number of vectors it indicated it needed in xfs_trans_count_vecs(). - * - * As each item fills in the entries it needs, also pin the item - * so that it cannot be flushed out until the log write completes. - */ -static void -xfs_trans_fill_vecs( -	struct xfs_trans	*tp, -	struct xfs_log_iovec	*log_vector) -{ -	struct xfs_log_item_desc *lidp; -	struct xfs_log_iovec	*vecp; -	uint			nitems; - -	/* -	 * Skip over the entry for the transaction header, we'll -	 * fill that in at the end. -	 */ -	vecp = log_vector + 1; - -	nitems = 0; -	ASSERT(!list_empty(&tp->t_items)); -	list_for_each_entry(lidp, &tp->t_items, lid_trans) { -		/* Skip items which aren't dirty in this transaction. */ -		if (!(lidp->lid_flags & XFS_LID_DIRTY)) -			continue; - -		/* -		 * The item may be marked dirty but not log anything.  This can -		 * be used to get called when a transaction is committed. -		 */ -		if (lidp->lid_size) -			nitems++; -		IOP_FORMAT(lidp->lid_item, vecp); -		vecp += lidp->lid_size; -		IOP_PIN(lidp->lid_item); -	} - -	/* -	 * Now that we've counted the number of items in this transaction, fill -	 * in the transaction header. Note that the transaction header does not -	 * have a log item. -	 */ -	tp->t_header.th_magic = XFS_TRANS_HEADER_MAGIC; -	tp->t_header.th_type = tp->t_type; -	tp->t_header.th_num_items = nitems; -	log_vector->i_addr = (xfs_caddr_t)&tp->t_header; -	log_vector->i_len = sizeof(xfs_trans_header_t); -	log_vector->i_type = XLOG_REG_TYPE_TRANSHDR; -} - -/* - * The committed item processing consists of calling the committed routine of - * each logged item, updating the item's position in the AIL if necessary, and - * unpinning each item.  If the committed routine returns -1, then do nothing - * further with the item because it may have been freed. - * - * Since items are unlocked when they are copied to the incore log, it is - * possible for two transactions to be completing and manipulating the same - * item simultaneously.  The AIL lock will protect the lsn field of each item. - * The value of this field can never go backwards. - * - * We unpin the items after repositioning them in the AIL, because otherwise - * they could be immediately flushed and we'd have to race with the flusher - * trying to pull the item from the AIL as we add it. - */ -static void -xfs_trans_item_committed( -	struct xfs_log_item	*lip, -	xfs_lsn_t		commit_lsn, -	int			aborted) -{ -	xfs_lsn_t		item_lsn; -	struct xfs_ail		*ailp; - -	if (aborted) -		lip->li_flags |= XFS_LI_ABORTED; -	item_lsn = IOP_COMMITTED(lip, commit_lsn); - -	/* item_lsn of -1 means the item needs no further processing */ -	if (XFS_LSN_CMP(item_lsn, (xfs_lsn_t)-1) == 0) -		return; - -	/* -	 * If the returned lsn is greater than what it contained before, update -	 * the location of the item in the AIL.  If it is not, then do nothing. -	 * Items can never move backwards in the AIL. -	 * -	 * While the new lsn should usually be greater, it is possible that a -	 * later transaction completing simultaneously with an earlier one -	 * using the same item could complete first with a higher lsn.  This -	 * would cause the earlier transaction to fail the test below. -	 */ -	ailp = lip->li_ailp; -	spin_lock(&ailp->xa_lock); -	if (XFS_LSN_CMP(item_lsn, lip->li_lsn) > 0) { -		/* -		 * This will set the item's lsn to item_lsn and update the -		 * position of the item in the AIL. -		 * -		 * xfs_trans_ail_update() drops the AIL lock. -		 */ -		xfs_trans_ail_update(ailp, lip, item_lsn); -	} else { -		spin_unlock(&ailp->xa_lock); -	} - -	/* -	 * Now that we've repositioned the item in the AIL, unpin it so it can -	 * be flushed. Pass information about buffer stale state down from the -	 * log item flags, if anyone else stales the buffer we do not want to -	 * pay any attention to it. -	 */ -	IOP_UNPIN(lip, 0); -} - -/* - * This is typically called by the LM when a transaction has been fully - * committed to disk.  It needs to unpin the items which have - * been logged by the transaction and update their positions - * in the AIL if necessary. - * - * This also gets called when the transactions didn't get written out - * because of an I/O error. Abortflag & XFS_LI_ABORTED is set then. - */ -STATIC void -xfs_trans_committed( -	void			*arg, -	int			abortflag) -{ -	struct xfs_trans	*tp = arg; -	struct xfs_log_item_desc *lidp, *next; - -	list_for_each_entry_safe(lidp, next, &tp->t_items, lid_trans) { -		xfs_trans_item_committed(lidp->lid_item, tp->t_lsn, abortflag); -		xfs_trans_free_item_desc(lidp); -	} - -	xfs_trans_free(tp); -} -  static inline void  xfs_log_item_batch_insert(  	struct xfs_ail		*ailp, @@ -1538,258 +1324,6 @@ xfs_trans_committed_bulk(  }  /* - * Called from the trans_commit code when we notice that the filesystem is in - * the middle of a forced shutdown. - * - * When we are called here, we have already pinned all the items in the - * transaction. However, neither IOP_COMMITTING or IOP_UNLOCK has been called - * so we can simply walk the items in the transaction, unpin them with an abort - * flag and then free the items. Note that unpinning the items can result in - * them being freed immediately, so we need to use a safe list traversal method - * here. - */ -STATIC void -xfs_trans_uncommit( -	struct xfs_trans	*tp, -	uint			flags) -{ -	struct xfs_log_item_desc *lidp, *n; - -	list_for_each_entry_safe(lidp, n, &tp->t_items, lid_trans) { -		if (lidp->lid_flags & XFS_LID_DIRTY) -			IOP_UNPIN(lidp->lid_item, 1); -	} - -	xfs_trans_unreserve_and_mod_sb(tp); -	xfs_trans_unreserve_and_mod_dquots(tp); - -	xfs_trans_free_items(tp, NULLCOMMITLSN, flags); -	xfs_trans_free(tp); -} - -/* - * Format the transaction direct to the iclog. This isolates the physical - * transaction commit operation from the logical operation and hence allows - * other methods to be introduced without affecting the existing commit path. - */ -static int -xfs_trans_commit_iclog( -	struct xfs_mount	*mp, -	struct xfs_trans	*tp, -	xfs_lsn_t		*commit_lsn, -	int			flags) -{ -	int			shutdown; -	int			error; -	int			log_flags = 0; -	struct xlog_in_core	*commit_iclog; -#define XFS_TRANS_LOGVEC_COUNT  16 -	struct xfs_log_iovec	log_vector_fast[XFS_TRANS_LOGVEC_COUNT]; -	struct xfs_log_iovec	*log_vector; -	uint			nvec; - - -	/* -	 * 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) { -		return ENOMEM;	/* triggers a shutdown! */ -	} 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); - -	if (flags & XFS_TRANS_RELEASE_LOG_RES) -		log_flags = XFS_LOG_REL_PERM_RESERV; - -	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; -	trace_xfs_trans_commit_lsn(tp); - -	if (nvec > XFS_TRANS_LOGVEC_COUNT) -		kmem_free(log_vector); - -	/* -	 * 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) { -		current_restore_flags_nested(&tp->t_pflags, PF_FSTRANS); -		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); - -	/* -	 * 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 = 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 -	 */ -	current_restore_flags_nested(&tp->t_pflags, PF_FSTRANS); - -	/* -	 * 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. -	 */ -	return xfs_log_release_iclog(mp, commit_iclog); -} - -/* - * Walk the log items and allocate log vector structures for - * each item large enough to fit all the vectors they require. - * Note that this format differs from the old log vector format in - * that there is no transaction header in these log vectors. - */ -STATIC struct xfs_log_vec * -xfs_trans_alloc_log_vecs( -	xfs_trans_t	*tp) -{ -	struct xfs_log_item_desc *lidp; -	struct xfs_log_vec	*lv = NULL; -	struct xfs_log_vec	*ret_lv = NULL; - - -	/* Bail out if we didn't find a log item.  */ -	if (list_empty(&tp->t_items)) { -		ASSERT(0); -		return NULL; -	} - -	list_for_each_entry(lidp, &tp->t_items, lid_trans) { -		struct xfs_log_vec *new_lv; - -		/* Skip items which aren't dirty in this transaction. */ -		if (!(lidp->lid_flags & XFS_LID_DIRTY)) -			continue; - -		/* Skip items that do not have any vectors for writing */ -		lidp->lid_size = IOP_SIZE(lidp->lid_item); -		if (!lidp->lid_size) -			continue; - -		new_lv = kmem_zalloc(sizeof(*new_lv) + -				lidp->lid_size * sizeof(struct xfs_log_iovec), -				KM_SLEEP); - -		/* The allocated iovec region lies beyond the log vector. */ -		new_lv->lv_iovecp = (struct xfs_log_iovec *)&new_lv[1]; -		new_lv->lv_niovecs = lidp->lid_size; -		new_lv->lv_item = lidp->lid_item; -		if (!ret_lv) -			ret_lv = new_lv; -		else -			lv->lv_next = new_lv; -		lv = new_lv; -	} - -	return ret_lv; -} - -static int -xfs_trans_commit_cil( -	struct xfs_mount	*mp, -	struct xfs_trans	*tp, -	xfs_lsn_t		*commit_lsn, -	int			flags) -{ -	struct xfs_log_vec	*log_vector; - -	/* -	 * Get each log item to allocate a vector structure for -	 * the log item to to pass to the log write code. The -	 * CIL commit code will format the vector and save it away. -	 */ -	log_vector = xfs_trans_alloc_log_vecs(tp); -	if (!log_vector) -		return ENOMEM; - -	xfs_log_commit_cil(mp, tp, log_vector, commit_lsn, flags); - -	current_restore_flags_nested(&tp->t_pflags, PF_FSTRANS); -	xfs_trans_free(tp); -	return 0; -} - -/*   * Commit the given transaction to the log.   *   * XFS disk error handling mechanism is not based on a typical @@ -1845,17 +1379,16 @@ xfs_trans_commit(  		xfs_trans_apply_sb_deltas(tp);  	xfs_trans_apply_dquot_deltas(tp); -	if (mp->m_flags & XFS_MOUNT_DELAYLOG) -		error = xfs_trans_commit_cil(mp, tp, &commit_lsn, flags); -	else -		error = xfs_trans_commit_iclog(mp, tp, &commit_lsn, flags); - +	error = xfs_log_commit_cil(mp, tp, &commit_lsn, flags);  	if (error == ENOMEM) {  		xfs_force_shutdown(mp, SHUTDOWN_LOG_IO_ERROR);  		error = XFS_ERROR(EIO);  		goto out_unreserve;  	} +	current_restore_flags_nested(&tp->t_pflags, PF_FSTRANS); +	xfs_trans_free(tp); +  	/*  	 * If the transaction needs to be synchronous, then force the  	 * log out now and wait for it.  |