xfs: fix getbmap vs mmap deadlock

xfs_getbmap (or rather the formatters called by it) copy out the getbmap
structures under the ilock, which can deadlock against mmap. This has
been reported via bugzilla a while ago (#717) and has recently also
shown up via lockdep.

So allocate a temporary buffer to format the kernel getbmap structures
into and then copy them out after dropping the locks.

A little problem with this is that we limit the number of extents we
can copy out by the maximum allocation size, but I see no real way
around that.

Signed-off-by: Christoph Hellwig <hch@lst.de>
Reviewed-by: Eric Sandeen <sandeen@sandeen.net>
Reviewed-by: Felix Blyakher <felixb@sgi.com>
Signed-off-by: Felix Blyakher <felixb@sgi.com>

authored by

Christoph Hellwig and committed by
Felix Blyakher
6321e3ed 4be4a00f

+35 -17
+35 -17
fs/xfs/xfs_bmap.c
··· 5890 int nexleft; /* # of user extents left */ 5891 int subnex; /* # of bmapi's can do */ 5892 int nmap; /* number of map entries */ 5893 - struct getbmapx out; /* output structure */ 5894 int whichfork; /* data or attr fork */ 5895 int prealloced; /* this is a file with 5896 * preallocated data space */ 5897 int iflags; /* interface flags */ 5898 int bmapi_flags; /* flags for xfs_bmapi */ 5899 5900 mp = ip->i_mount; 5901 iflags = bmv->bmv_iflags; ··· 5972 return XFS_ERROR(EINVAL); 5973 bmvend = bmv->bmv_offset + bmv->bmv_length; 5974 5975 xfs_ilock(ip, XFS_IOLOCK_SHARED); 5976 if (whichfork == XFS_DATA_FORK && !(iflags & BMV_IF_DELALLOC)) { 5977 if (ip->i_delayed_blks || ip->i_size > ip->i_d.di_size) { ··· 6033 ASSERT(nmap <= subnex); 6034 6035 for (i = 0; i < nmap && nexleft && bmv->bmv_length; i++) { 6036 - int full = 0; /* user array is full */ 6037 - 6038 - out.bmv_oflags = 0; 6039 if (map[i].br_state == XFS_EXT_UNWRITTEN) 6040 - out.bmv_oflags |= BMV_OF_PREALLOC; 6041 else if (map[i].br_startblock == DELAYSTARTBLOCK) 6042 - out.bmv_oflags |= BMV_OF_DELALLOC; 6043 - out.bmv_offset = XFS_FSB_TO_BB(mp, map[i].br_startoff); 6044 - out.bmv_length = XFS_FSB_TO_BB(mp, map[i].br_blockcount); 6045 - out.bmv_unused1 = out.bmv_unused2 = 0; 6046 ASSERT(((iflags & BMV_IF_DELALLOC) != 0) || 6047 (map[i].br_startblock != DELAYSTARTBLOCK)); 6048 if (map[i].br_startblock == HOLESTARTBLOCK && 6049 whichfork == XFS_ATTR_FORK) { 6050 /* came to the end of attribute fork */ 6051 - out.bmv_oflags |= BMV_OF_LAST; 6052 goto out_free_map; 6053 } 6054 6055 - if (!xfs_getbmapx_fix_eof_hole(ip, &out, prealloced, 6056 - bmvend, map[i].br_startblock)) 6057 goto out_free_map; 6058 6059 - /* format results & advance arg */ 6060 - error = formatter(&arg, &out, &full); 6061 - if (error || full) 6062 - goto out_free_map; 6063 nexleft--; 6064 bmv->bmv_offset = 6065 - out.bmv_offset + out.bmv_length; 6066 bmv->bmv_length = 6067 max_t(__int64_t, 0, bmvend - bmv->bmv_offset); 6068 bmv->bmv_entries++; 6069 } 6070 } while (nmap && nexleft && bmv->bmv_length); 6071 ··· 6075 xfs_iunlock_map_shared(ip, lock); 6076 out_unlock_iolock: 6077 xfs_iunlock(ip, XFS_IOLOCK_SHARED); 6078 return error; 6079 } 6080
··· 5890 int nexleft; /* # of user extents left */ 5891 int subnex; /* # of bmapi's can do */ 5892 int nmap; /* number of map entries */ 5893 + struct getbmapx *out; /* output structure */ 5894 int whichfork; /* data or attr fork */ 5895 int prealloced; /* this is a file with 5896 * preallocated data space */ 5897 int iflags; /* interface flags */ 5898 int bmapi_flags; /* flags for xfs_bmapi */ 5899 + int cur_ext = 0; 5900 5901 mp = ip->i_mount; 5902 iflags = bmv->bmv_iflags; ··· 5971 return XFS_ERROR(EINVAL); 5972 bmvend = bmv->bmv_offset + bmv->bmv_length; 5973 5974 + 5975 + if (bmv->bmv_count > ULONG_MAX / sizeof(struct getbmapx)) 5976 + return XFS_ERROR(ENOMEM); 5977 + out = kmem_zalloc(bmv->bmv_count * sizeof(struct getbmapx), KM_MAYFAIL); 5978 + if (!out) 5979 + return XFS_ERROR(ENOMEM); 5980 + 5981 xfs_ilock(ip, XFS_IOLOCK_SHARED); 5982 if (whichfork == XFS_DATA_FORK && !(iflags & BMV_IF_DELALLOC)) { 5983 if (ip->i_delayed_blks || ip->i_size > ip->i_d.di_size) { ··· 6025 ASSERT(nmap <= subnex); 6026 6027 for (i = 0; i < nmap && nexleft && bmv->bmv_length; i++) { 6028 + out[cur_ext].bmv_oflags = 0; 6029 if (map[i].br_state == XFS_EXT_UNWRITTEN) 6030 + out[cur_ext].bmv_oflags |= BMV_OF_PREALLOC; 6031 else if (map[i].br_startblock == DELAYSTARTBLOCK) 6032 + out[cur_ext].bmv_oflags |= BMV_OF_DELALLOC; 6033 + out[cur_ext].bmv_offset = 6034 + XFS_FSB_TO_BB(mp, map[i].br_startoff); 6035 + out[cur_ext].bmv_length = 6036 + XFS_FSB_TO_BB(mp, map[i].br_blockcount); 6037 + out[cur_ext].bmv_unused1 = 0; 6038 + out[cur_ext].bmv_unused2 = 0; 6039 ASSERT(((iflags & BMV_IF_DELALLOC) != 0) || 6040 (map[i].br_startblock != DELAYSTARTBLOCK)); 6041 if (map[i].br_startblock == HOLESTARTBLOCK && 6042 whichfork == XFS_ATTR_FORK) { 6043 /* came to the end of attribute fork */ 6044 + out[cur_ext].bmv_oflags |= BMV_OF_LAST; 6045 goto out_free_map; 6046 } 6047 6048 + if (!xfs_getbmapx_fix_eof_hole(ip, &out[cur_ext], 6049 + prealloced, bmvend, 6050 + map[i].br_startblock)) 6051 goto out_free_map; 6052 6053 nexleft--; 6054 bmv->bmv_offset = 6055 + out[cur_ext].bmv_offset + 6056 + out[cur_ext].bmv_length; 6057 bmv->bmv_length = 6058 max_t(__int64_t, 0, bmvend - bmv->bmv_offset); 6059 bmv->bmv_entries++; 6060 + cur_ext++; 6061 } 6062 } while (nmap && nexleft && bmv->bmv_length); 6063 ··· 6067 xfs_iunlock_map_shared(ip, lock); 6068 out_unlock_iolock: 6069 xfs_iunlock(ip, XFS_IOLOCK_SHARED); 6070 + 6071 + for (i = 0; i < cur_ext; i++) { 6072 + int full = 0; /* user array is full */ 6073 + 6074 + /* format results & advance arg */ 6075 + error = formatter(&arg, &out[i], &full); 6076 + if (error || full) 6077 + break; 6078 + } 6079 + 6080 return error; 6081 } 6082