Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

xfs: hide log iovec alignment constraints

Callers currently have to round out the size of buffers to match the
aligment constraints of log iovecs and xlog_write(). They should not
need to know this detail, so introduce a new function to calculate
the iovec length (for use in ->iop_size implementations). Also
modify xlog_finish_iovec() to round up the length to the correct
alignment so the callers don't need to do this, either.

Convert the only user - inode forks - of this alignment rounding to
use the new interface.

Signed-off-by: Dave Chinner <dchinner@redhat.com>
Reviewed-by: Christoph Hellwig <hch@lst.de>
Reviewed-by: Allison Henderson <allison.henderson@oracle.com>
Reviewed-by: Darrick J. Wong <djwong@kernel.org>
Signed-off-by: Dave Chinner <david@fromorbit.com>

authored by

Dave Chinner and committed by
Dave Chinner
b2c28035 c230a4a8

+51 -38
+4 -16
fs/xfs/libxfs/xfs_inode_fork.c
··· 36 36 int64_t size) 37 37 { 38 38 struct xfs_ifork *ifp = XFS_IFORK_PTR(ip, whichfork); 39 - int mem_size = size, real_size = 0; 39 + int mem_size = size; 40 40 bool zero_terminate; 41 41 42 42 /* ··· 50 50 mem_size++; 51 51 52 52 if (size) { 53 - /* 54 - * As we round up the allocation here, we need to ensure the 55 - * bytes we don't copy data into are zeroed because the log 56 - * vectors still copy them into the journal. 57 - */ 58 - real_size = roundup(mem_size, 4); 59 - ifp->if_u1.if_data = kmem_zalloc(real_size, KM_NOFS); 53 + ifp->if_u1.if_data = kmem_alloc(mem_size, KM_NOFS); 60 54 memcpy(ifp->if_u1.if_data, data, size); 61 55 if (zero_terminate) 62 56 ifp->if_u1.if_data[size] = '\0'; ··· 496 502 return; 497 503 } 498 504 499 - /* 500 - * For inline data, the underlying buffer must be a multiple of 4 bytes 501 - * in size so that it can be logged and stay on word boundaries. 502 - * We enforce that here, and use __GFP_ZERO to ensure that size 503 - * extensions always zero the unused roundup area. 504 - */ 505 - ifp->if_u1.if_data = krealloc(ifp->if_u1.if_data, roundup(new_size, 4), 506 - GFP_NOFS | __GFP_NOFAIL | __GFP_ZERO); 505 + ifp->if_u1.if_data = krealloc(ifp->if_u1.if_data, new_size, 506 + GFP_NOFS | __GFP_NOFAIL); 507 507 ifp->if_bytes = new_size; 508 508 } 509 509
+7 -18
fs/xfs/xfs_inode_item.c
··· 71 71 case XFS_DINODE_FMT_LOCAL: 72 72 if ((iip->ili_fields & XFS_ILOG_DDATA) && 73 73 ip->i_df.if_bytes > 0) { 74 - *nbytes += roundup(ip->i_df.if_bytes, 4); 74 + *nbytes += xlog_calc_iovec_len(ip->i_df.if_bytes); 75 75 *nvecs += 1; 76 76 } 77 77 break; ··· 112 112 case XFS_DINODE_FMT_LOCAL: 113 113 if ((iip->ili_fields & XFS_ILOG_ADATA) && 114 114 ip->i_afp->if_bytes > 0) { 115 - *nbytes += roundup(ip->i_afp->if_bytes, 4); 115 + *nbytes += xlog_calc_iovec_len(ip->i_afp->if_bytes); 116 116 *nvecs += 1; 117 117 } 118 118 break; ··· 204 204 ~(XFS_ILOG_DEXT | XFS_ILOG_DBROOT | XFS_ILOG_DEV); 205 205 if ((iip->ili_fields & XFS_ILOG_DDATA) && 206 206 ip->i_df.if_bytes > 0) { 207 - /* 208 - * Round i_bytes up to a word boundary. 209 - * The underlying memory is guaranteed 210 - * to be there by xfs_idata_realloc(). 211 - */ 212 - data_bytes = roundup(ip->i_df.if_bytes, 4); 213 207 ASSERT(ip->i_df.if_u1.if_data != NULL); 214 208 ASSERT(ip->i_disk_size > 0); 215 209 xlog_copy_iovec(lv, vecp, XLOG_REG_TYPE_ILOCAL, 216 - ip->i_df.if_u1.if_data, data_bytes); 217 - ilf->ilf_dsize = (unsigned)data_bytes; 210 + ip->i_df.if_u1.if_data, 211 + ip->i_df.if_bytes); 212 + ilf->ilf_dsize = (unsigned)ip->i_df.if_bytes; 218 213 ilf->ilf_size++; 219 214 } else { 220 215 iip->ili_fields &= ~XFS_ILOG_DDATA; ··· 283 288 284 289 if ((iip->ili_fields & XFS_ILOG_ADATA) && 285 290 ip->i_afp->if_bytes > 0) { 286 - /* 287 - * Round i_bytes up to a word boundary. 288 - * The underlying memory is guaranteed 289 - * to be there by xfs_idata_realloc(). 290 - */ 291 - data_bytes = roundup(ip->i_afp->if_bytes, 4); 292 291 ASSERT(ip->i_afp->if_u1.if_data != NULL); 293 292 xlog_copy_iovec(lv, vecp, XLOG_REG_TYPE_IATTR_LOCAL, 294 293 ip->i_afp->if_u1.if_data, 295 - data_bytes); 296 - ilf->ilf_asize = (unsigned)data_bytes; 294 + ip->i_afp->if_bytes); 295 + ilf->ilf_asize = (unsigned)ip->i_afp->if_bytes; 297 296 ilf->ilf_size++; 298 297 } else { 299 298 iip->ili_fields &= ~XFS_ILOG_ADATA;
+2 -2
fs/xfs/xfs_inode_item_recover.c
··· 462 462 ASSERT(in_f->ilf_size <= 4); 463 463 ASSERT((in_f->ilf_size == 3) || (fields & XFS_ILOG_AFORK)); 464 464 ASSERT(!(fields & XFS_ILOG_DFORK) || 465 - (len == in_f->ilf_dsize)); 465 + (len == xlog_calc_iovec_len(in_f->ilf_dsize))); 466 466 467 467 switch (fields & XFS_ILOG_DFORK) { 468 468 case XFS_ILOG_DDATA: ··· 497 497 } 498 498 len = item->ri_buf[attr_index].i_len; 499 499 src = item->ri_buf[attr_index].i_addr; 500 - ASSERT(len == in_f->ilf_asize); 500 + ASSERT(len == xlog_calc_iovec_len(in_f->ilf_asize)); 501 501 502 502 switch (in_f->ilf_fields & XFS_ILOG_AFORK) { 503 503 case XFS_ILOG_ADATA:
+38 -2
fs/xfs/xfs_log.h
··· 21 21 22 22 #define XFS_LOG_VEC_ORDERED (-1) 23 23 24 + /* 25 + * Calculate the log iovec length for a given user buffer length. Intended to be 26 + * used by ->iop_size implementations when sizing buffers of arbitrary 27 + * alignments. 28 + */ 29 + static inline int 30 + xlog_calc_iovec_len(int len) 31 + { 32 + return roundup(len, sizeof(uint32_t)); 33 + } 34 + 24 35 void *xlog_prepare_iovec(struct xfs_log_vec *lv, struct xfs_log_iovec **vecp, 25 36 uint type); 26 37 27 38 static inline void 28 - xlog_finish_iovec(struct xfs_log_vec *lv, struct xfs_log_iovec *vec, int len) 39 + xlog_finish_iovec(struct xfs_log_vec *lv, struct xfs_log_iovec *vec, 40 + int data_len) 29 41 { 30 42 struct xlog_op_header *oph = vec->i_addr; 43 + int len; 31 44 32 - /* opheader tracks payload length, logvec tracks region length */ 45 + /* 46 + * Always round up the length to the correct alignment so callers don't 47 + * need to know anything about this log vec layout requirement. This 48 + * means we have to zero the area the data to be written does not cover. 49 + * This is complicated by fact the payload region is offset into the 50 + * logvec region by the opheader that tracks the payload. 51 + */ 52 + len = xlog_calc_iovec_len(data_len); 53 + if (len - data_len != 0) { 54 + char *buf = vec->i_addr + sizeof(struct xlog_op_header); 55 + 56 + memset(buf + data_len, 0, len - data_len); 57 + } 58 + 59 + /* 60 + * The opheader tracks aligned payload length, whilst the logvec tracks 61 + * the overall region length. 62 + */ 33 63 oph->oh_len = cpu_to_be32(len); 34 64 35 65 len += sizeof(struct xlog_op_header); 36 66 lv->lv_buf_len += len; 37 67 lv->lv_bytes += len; 38 68 vec->i_len = len; 69 + 70 + /* Catch buffer overruns */ 71 + ASSERT((void *)lv->lv_buf + lv->lv_bytes <= (void *)lv + lv->lv_size); 39 72 } 40 73 74 + /* 75 + * Copy the amount of data requested by the caller into a new log iovec. 76 + */ 41 77 static inline void * 42 78 xlog_copy_iovec(struct xfs_log_vec *lv, struct xfs_log_iovec **vecp, 43 79 uint type, void *data, int len)