/* * Calculate how many blocks we need for the new attribute, */ STATIC int xfs_attr_calc_size( struct xfs_da_args *args, int *local) { struct xfs_mount *mp = args->dp->i_mount; int size; int nblks; /* * Determine space new attribute will use, and if it would be * "local" or "remote" (note: local != inline). */ size = xfs_attr_leaf_newentsize(args, local); nblks = XFS_DAENTER_SPACE_RES(mp, XFS_ATTR_FORK); if (*local) { if (size > (args->geo->blksize / 2)) { /* Double split possible */ nblks *= 2; } } else { /* * Out of line attribute, cannot double split, but * make room for the attribute value itself. */ uint dblocks = xfs_attr3_rmt_blocks(mp, args->valuelen); nblks += dblocks; nblks += XFS_NEXTENTADD_SPACE_RES(mp, dblocks, XFS_ATTR_FORK); } return nblks; }
/* * Calculate the maximum length in bytes that would be required for a local * attribute value as large attributes out of line are not logged. */ STATIC int xfs_log_calc_max_attrsetm_res( struct xfs_mount *mp) { int size; int nblks; size = xfs_attr_leaf_entsize_local_max(mp->m_sb.sb_blocksize) - MAXNAMELEN - 1; nblks = XFS_DAENTER_SPACE_RES(mp, XFS_ATTR_FORK); nblks += XFS_B_TO_FSB(mp, size); nblks += XFS_NEXTENTADD_SPACE_RES(mp, size, XFS_ATTR_FORK); return M_RES(mp)->tr_attrsetm.tr_logres + M_RES(mp)->tr_attrsetrt.tr_logres * nblks; }
/* * 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; }
static int max_trans_res( xfs_mount_t *mp, int *mul) { uint *p; uint *q; int rval; xfs_trans_reservations_t *tr; xfs_da_args_t args; int local; int size; int nblks; int res; nblks = XFS_DAENTER_SPACE_RES(mp, XFS_ATTR_FORK); /* * Fill in the arg structure for this request. */ bzero(&args, sizeof(args)); args.name = NULL; args.namelen = MAXNAMELEN; args.value = NULL; args.valuelen = 65536; args.flags = 0; args.hashval = 0; args.dp = NULL; args.firstblock = NULL; args.flist = NULL; args.whichfork = XFS_ATTR_FORK; args.oknoent = 1; /* * Determine space new attribute will use, and if it will be * inline or out of line. */ size = libxfs_attr_leaf_newentsize( &args, mp->m_sb.sb_blocksize, &local); if (local) { printf("Uh-oh.. attribute is local\n"); } else { /* Out of line attribute, cannot double split, but make * room for the attribute value itself. */ nblks += XFS_B_TO_FSB(mp, size); nblks += XFS_NEXTENTADD_SPACE_RES(mp, size, XFS_ATTR_FORK); } res = XFS_ATTRSET_LOG_RES(mp, nblks); #if 0 printf("size = %d nblks = %d res = %d\n", size, nblks, res); #endif mp->m_reservations.tr_attrset = res; for (rval = 0, tr = &mp->m_reservations, p = (uint *)tr, q = (uint *)&tr_count; p < (uint *)(tr + 1); p++, q++) { if ((int)*p > rval) { rval = (int)*p; *mul = (int)*q; } } return rval; }