| /* |
| * Copyright (c) 2000-2001,2005 Silicon Graphics, Inc. |
| * All Rights Reserved. |
| * |
| * This program is free software; you can redistribute it and/or |
| * modify it under the terms of the GNU General Public License as |
| * published by the Free Software Foundation. |
| * |
| * This program is distributed in the hope that it would be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| * GNU General Public License for more details. |
| * |
| * You should have received a copy of the GNU General Public License |
| * along with this program; if not, write the Free Software Foundation, |
| * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
| */ |
| |
| #include <xfs.h> |
| |
| kmem_zone_t *xfs_buf_item_zone; |
| kmem_zone_t *xfs_ili_zone; /* inode log item zone */ |
| |
| /* |
| * Following functions from fs/xfs/xfs_trans_item.c |
| */ |
| |
| /* |
| * This is called to add the given log item to the transaction's |
| * list of log items. It must find a free log item descriptor |
| * or allocate a new one and add the item to that descriptor. |
| * The function returns a pointer to item descriptor used to point |
| * to the new item. The log item will now point to its new descriptor |
| * with its li_desc field. |
| */ |
| xfs_log_item_desc_t * |
| xfs_trans_add_item( |
| xfs_trans_t *tp, |
| xfs_log_item_t *lip) |
| { |
| xfs_log_item_desc_t *lidp; |
| xfs_log_item_chunk_t *licp; |
| int i = 0; |
| |
| /* |
| * If there are no free descriptors, allocate a new chunk |
| * of them and put it at the front of the chunk list. |
| */ |
| if (tp->t_items_free == 0) { |
| licp = (xfs_log_item_chunk_t*) |
| kmem_alloc(sizeof(xfs_log_item_chunk_t), KM_SLEEP); |
| ASSERT(licp != NULL); |
| /* |
| * Initialize the chunk, and then |
| * claim the first slot in the newly allocated chunk. |
| */ |
| xfs_lic_init(licp); |
| xfs_lic_claim(licp, 0); |
| licp->lic_unused = 1; |
| xfs_lic_init_slot(licp, 0); |
| lidp = xfs_lic_slot(licp, 0); |
| |
| /* |
| * Link in the new chunk and update the free count. |
| */ |
| licp->lic_next = tp->t_items.lic_next; |
| tp->t_items.lic_next = licp; |
| tp->t_items_free = XFS_LIC_NUM_SLOTS - 1; |
| |
| /* |
| * Initialize the descriptor and the generic portion |
| * of the log item. |
| * |
| * Point the new slot at this item and return it. |
| * Also point the log item at its currently active |
| * descriptor and set the item's mount pointer. |
| */ |
| lidp->lid_item = lip; |
| lidp->lid_flags = 0; |
| lidp->lid_size = 0; |
| lip->li_desc = lidp; |
| lip->li_mountp = tp->t_mountp; |
| return lidp; |
| } |
| |
| /* |
| * Find the free descriptor. It is somewhere in the chunklist |
| * of descriptors. |
| */ |
| licp = &tp->t_items; |
| while (licp != NULL) { |
| if (xfs_lic_vacancy(licp)) { |
| if (licp->lic_unused <= XFS_LIC_MAX_SLOT) { |
| i = licp->lic_unused; |
| ASSERT(xfs_lic_isfree(licp, i)); |
| break; |
| } |
| for (i = 0; i <= XFS_LIC_MAX_SLOT; i++) { |
| if (xfs_lic_isfree(licp, i)) |
| break; |
| } |
| ASSERT(i <= XFS_LIC_MAX_SLOT); |
| break; |
| } |
| licp = licp->lic_next; |
| } |
| ASSERT(licp != NULL); |
| /* |
| * If we find a free descriptor, claim it, |
| * initialize it, and return it. |
| */ |
| xfs_lic_claim(licp, i); |
| if (licp->lic_unused <= i) { |
| licp->lic_unused = i + 1; |
| xfs_lic_init_slot(licp, i); |
| } |
| lidp = xfs_lic_slot(licp, i); |
| tp->t_items_free--; |
| lidp->lid_item = lip; |
| lidp->lid_flags = 0; |
| lidp->lid_size = 0; |
| lip->li_desc = lidp; |
| lip->li_mountp = tp->t_mountp; |
| return lidp; |
| } |
| |
| /* |
| * Free the given descriptor. |
| * |
| * This requires setting the bit in the chunk's free mask corresponding |
| * to the given slot. |
| */ |
| void |
| xfs_trans_free_item( |
| xfs_trans_t *tp, |
| xfs_log_item_desc_t *lidp) |
| { |
| uint slot; |
| xfs_log_item_chunk_t *licp; |
| xfs_log_item_chunk_t **licpp; |
| |
| slot = xfs_lic_desc_to_slot(lidp); |
| licp = xfs_lic_desc_to_chunk(lidp); |
| xfs_lic_relse(licp, slot); |
| lidp->lid_item->li_desc = NULL; |
| tp->t_items_free++; |
| |
| /* |
| * If there are no more used items in the chunk and this is not |
| * the chunk embedded in the transaction structure, then free |
| * the chunk. First pull it from the chunk list and then |
| * free it back to the heap. We didn't bother with a doubly |
| * linked list here because the lists should be very short |
| * and this is not a performance path. It's better to save |
| * the memory of the extra pointer. |
| * |
| * Also decrement the transaction structure's count of free items |
| * by the number in a chunk since we are freeing an empty chunk. |
| */ |
| if (xfs_lic_are_all_free(licp) && (licp != &(tp->t_items))) { |
| licpp = &(tp->t_items.lic_next); |
| while (*licpp != licp) { |
| ASSERT(*licpp != NULL); |
| licpp = &((*licpp)->lic_next); |
| } |
| *licpp = licp->lic_next; |
| kmem_free(licp); |
| tp->t_items_free -= XFS_LIC_NUM_SLOTS; |
| } |
| } |
| |
| /* |
| * This is called to find the descriptor corresponding to the given |
| * log item. It returns a pointer to the descriptor. |
| * The log item MUST have a corresponding descriptor in the given |
| * transaction. This routine does not return NULL, it panics. |
| * |
| * The descriptor pointer is kept in the log item's li_desc field. |
| * Just return it. |
| */ |
| xfs_log_item_desc_t * |
| xfs_trans_find_item( |
| xfs_trans_t *tp, |
| xfs_log_item_t *lip) |
| { |
| ASSERT(lip->li_desc != NULL); |
| |
| return lip->li_desc; |
| } |
| |
| /* |
| * This is called to unlock all of the items of a transaction and to free |
| * all the descriptors of that transaction. |
| * |
| * It walks the list of descriptors and unlocks each item. It frees |
| * each chunk except that embedded in the transaction as it goes along. |
| */ |
| void |
| xfs_trans_free_items( |
| xfs_trans_t *tp, |
| int flags) |
| { |
| xfs_log_item_chunk_t *licp; |
| xfs_log_item_chunk_t *next_licp; |
| int abort; |
| |
| abort = flags & XFS_TRANS_ABORT; |
| licp = &tp->t_items; |
| /* |
| * Special case the embedded chunk so we don't free it below. |
| */ |
| if (!xfs_lic_are_all_free(licp)) { |
| (void) xfs_trans_unlock_chunk(licp, 1, abort, NULLCOMMITLSN); |
| xfs_lic_all_free(licp); |
| licp->lic_unused = 0; |
| } |
| licp = licp->lic_next; |
| |
| /* |
| * Unlock each item in each chunk and free the chunks. |
| */ |
| while (licp != NULL) { |
| ASSERT(!xfs_lic_are_all_free(licp)); |
| (void) xfs_trans_unlock_chunk(licp, 1, abort, NULLCOMMITLSN); |
| next_licp = licp->lic_next; |
| kmem_free(licp); |
| licp = next_licp; |
| } |
| |
| /* |
| * Reset the transaction structure's free item count. |
| */ |
| tp->t_items_free = XFS_LIC_NUM_SLOTS; |
| tp->t_items.lic_next = NULL; |
| } |
| |
| /* |
| * Following functions from fs/xfs/xfs_trans_buf.c |
| */ |
| |
| /* |
| * Check to see if a buffer matching the given parameters is already |
| * a part of the given transaction. Only check the first, embedded |
| * chunk, since we don't want to spend all day scanning large transactions. |
| */ |
| xfs_buf_t * |
| xfs_trans_buf_item_match( |
| xfs_trans_t *tp, |
| xfs_buftarg_t *target, |
| xfs_daddr_t blkno, |
| int len) |
| { |
| xfs_log_item_chunk_t *licp; |
| xfs_log_item_desc_t *lidp; |
| xfs_buf_log_item_t *blip; |
| xfs_buf_t *bp; |
| int i; |
| |
| #ifdef LI_DEBUG |
| fprintf(stderr, "buf_item_match (fast) log items for xact %p\n", tp); |
| #endif |
| |
| bp = NULL; |
| len = BBTOB(len); |
| licp = &tp->t_items; |
| if (!xfs_lic_are_all_free(licp)) { |
| for (i = 0; i < licp->lic_unused; i++) { |
| /* |
| * Skip unoccupied slots. |
| */ |
| if (xfs_lic_isfree(licp, i)) { |
| continue; |
| } |
| |
| lidp = xfs_lic_slot(licp, i); |
| blip = (xfs_buf_log_item_t *)lidp->lid_item; |
| #ifdef LI_DEBUG |
| fprintf(stderr, |
| "\tfound log item, xact %p, blip=%p (%d/%d)\n", |
| tp, blip, i, licp->lic_unused); |
| #endif |
| if (blip->bli_item.li_type != XFS_LI_BUF) { |
| continue; |
| } |
| |
| bp = blip->bli_buf; |
| #ifdef LI_DEBUG |
| fprintf(stderr, |
| "\tfound buf %p log item, xact %p, blip=%p (%d)\n", |
| bp, tp, blip, i); |
| #endif |
| if ((XFS_BUF_TARGET(bp) == target->dev) && |
| (XFS_BUF_ADDR(bp) == blkno) && |
| (XFS_BUF_COUNT(bp) == len)) { |
| /* |
| * We found it. Break out and |
| * return the pointer to the buffer. |
| */ |
| #ifdef LI_DEBUG |
| fprintf(stderr, |
| "\tfound REAL buf log item, bp=%p\n", |
| bp); |
| #endif |
| break; |
| } else { |
| bp = NULL; |
| } |
| } |
| } |
| #ifdef LI_DEBUG |
| if (!bp) fprintf(stderr, "\tfast search - got nothing\n"); |
| #endif |
| return bp; |
| } |
| |
| /* |
| * Check to see if a buffer matching the given parameters is already |
| * a part of the given transaction. Check all the chunks, we |
| * want to be thorough. |
| */ |
| xfs_buf_t * |
| xfs_trans_buf_item_match_all( |
| xfs_trans_t *tp, |
| xfs_buftarg_t *target, |
| xfs_daddr_t blkno, |
| int len) |
| { |
| xfs_log_item_chunk_t *licp; |
| xfs_log_item_desc_t *lidp; |
| xfs_buf_log_item_t *blip; |
| xfs_buf_t *bp; |
| int i; |
| |
| #ifdef LI_DEBUG |
| fprintf(stderr, "buf_item_match_all (slow) log items for xact %p\n", |
| tp); |
| #endif |
| |
| bp = NULL; |
| len = BBTOB(len); |
| for (licp = &tp->t_items; licp != NULL; licp = licp->lic_next) { |
| if (xfs_lic_are_all_free(licp)) { |
| ASSERT(licp == &tp->t_items); |
| ASSERT(licp->lic_next == NULL); |
| return NULL; |
| } |
| for (i = 0; i < licp->lic_unused; i++) { |
| /* |
| * Skip unoccupied slots. |
| */ |
| if (xfs_lic_isfree(licp, i)) { |
| continue; |
| } |
| |
| lidp = xfs_lic_slot(licp, i); |
| blip = (xfs_buf_log_item_t *)lidp->lid_item; |
| #ifdef LI_DEBUG |
| fprintf(stderr, |
| "\tfound log item, xact %p, blip=%p (%d/%d)\n", |
| tp, blip, i, licp->lic_unused); |
| #endif |
| if (blip->bli_item.li_type != XFS_LI_BUF) { |
| continue; |
| } |
| |
| bp = blip->bli_buf; |
| ASSERT(bp); |
| ASSERT(XFS_BUF_ADDR(bp)); |
| #ifdef LI_DEBUG |
| fprintf(stderr, |
| "\tfound buf %p log item, xact %p, blip=%p (%d)\n", |
| bp, tp, blip, i); |
| #endif |
| if ((XFS_BUF_TARGET(bp) == target->dev) && |
| (XFS_BUF_ADDR(bp) == blkno) && |
| (XFS_BUF_COUNT(bp) == len)) { |
| /* |
| * We found it. Break out and |
| * return the pointer to the buffer. |
| */ |
| #ifdef LI_DEBUG |
| fprintf(stderr, |
| "\tfound REAL buf log item, bp=%p\n", |
| bp); |
| #endif |
| return bp; |
| } |
| } |
| } |
| #ifdef LI_DEBUG |
| if (!bp) fprintf(stderr, "slow search - got nothing\n"); |
| #endif |
| return NULL; |
| } |
| |
| /* |
| * The following are from fs/xfs/xfs_buf_item.c |
| */ |
| |
| /* |
| * Allocate a new buf log item to go with the given buffer. |
| * Set the buffer's b_fsprivate field to point to the new |
| * buf log item. If there are other item's attached to the |
| * buffer (see xfs_buf_attach_iodone() below), then put the |
| * buf log item at the front. |
| */ |
| void |
| xfs_buf_item_init( |
| xfs_buf_t *bp, |
| xfs_mount_t *mp) |
| { |
| xfs_log_item_t *lip; |
| xfs_buf_log_item_t *bip; |
| |
| #ifdef LI_DEBUG |
| fprintf(stderr, "buf_item_init for buffer %p\n", bp); |
| #endif |
| |
| /* |
| * Check to see if there is already a buf log item for |
| * this buffer. If there is, it is guaranteed to be |
| * the first. If we do already have one, there is |
| * nothing to do here so return. |
| */ |
| if (XFS_BUF_FSPRIVATE3(bp, xfs_mount_t *) != mp) |
| XFS_BUF_SET_FSPRIVATE3(bp, mp); |
| XFS_BUF_SET_BDSTRAT_FUNC(bp, xfs_bdstrat_cb); |
| if (XFS_BUF_FSPRIVATE(bp, void *) != NULL) { |
| lip = XFS_BUF_FSPRIVATE(bp, xfs_log_item_t *); |
| if (lip->li_type == XFS_LI_BUF) { |
| #ifdef LI_DEBUG |
| fprintf(stderr, |
| "reused buf item %p for pre-logged buffer %p\n", |
| lip, bp); |
| #endif |
| return; |
| } |
| } |
| |
| bip = (xfs_buf_log_item_t *)kmem_zone_zalloc(xfs_buf_item_zone, |
| KM_SLEEP); |
| #ifdef LI_DEBUG |
| fprintf(stderr, "adding buf item %p for not-logged buffer %p\n", |
| bip, bp); |
| #endif |
| bip->bli_item.li_type = XFS_LI_BUF; |
| bip->bli_item.li_mountp = mp; |
| bip->bli_buf = bp; |
| bip->bli_format.blf_type = XFS_LI_BUF; |
| bip->bli_format.blf_blkno = (__int64_t)XFS_BUF_ADDR(bp); |
| bip->bli_format.blf_len = (ushort)BTOBB(XFS_BUF_COUNT(bp)); |
| XFS_BUF_SET_FSPRIVATE(bp, bip); |
| } |
| |
| |
| /* |
| * Mark bytes first through last inclusive as dirty in the buf |
| * item's bitmap. |
| */ |
| void |
| xfs_buf_item_log( |
| xfs_buf_log_item_t *bip, |
| uint first, |
| uint last) |
| { |
| /* |
| * Mark the item as having some dirty data for |
| * quick reference in xfs_buf_item_dirty. |
| */ |
| bip->bli_flags |= XFS_BLI_DIRTY; |
| } |
| |
| /* |
| * Initialize the inode log item for a newly allocated (in-core) inode. |
| */ |
| void |
| xfs_inode_item_init( |
| xfs_inode_t *ip, |
| xfs_mount_t *mp) |
| { |
| xfs_inode_log_item_t *iip; |
| |
| ASSERT(ip->i_itemp == NULL); |
| iip = ip->i_itemp = (xfs_inode_log_item_t *) |
| kmem_zone_zalloc(xfs_ili_zone, KM_SLEEP); |
| #ifdef LI_DEBUG |
| fprintf(stderr, "inode_item_init for inode %llu, iip=%p\n", |
| ip->i_ino, iip); |
| #endif |
| |
| iip->ili_item.li_type = XFS_LI_INODE; |
| iip->ili_item.li_mountp = mp; |
| iip->ili_inode = ip; |
| iip->ili_format.ilf_type = XFS_LI_INODE; |
| iip->ili_format.ilf_ino = ip->i_ino; |
| iip->ili_format.ilf_blkno = ip->i_blkno; |
| iip->ili_format.ilf_len = ip->i_len; |
| iip->ili_format.ilf_boffset = ip->i_boffset; |
| } |