Implement FIDEDUPERANGE via block cloning · openzfs/zfs@afb4010

GitHub

3 min read Original article ↗

@@ -25,6 +25,40 @@

2525

#include <sys/zfs_vnops.h>

2626

#include <sys/zfeature.h>

272728+

/*

29+

* Take the source and destination inode locks for a remap (clone or dedupe).

30+

*

31+

* Since Linux 4.20 the VFS does not lock the inodes for ->remap_file_range();

32+

* the filesystem must, and it must impose an order on the two, or two remaps

33+

* running in opposite directions deadlock: each would hold one inode and wait

34+

* for the other, and an rwsem writer queues behind the existing readers. Order

35+

* by inode address, as btrfs does. The destination is taken exclusively and

36+

* the source shared, so concurrent remaps out of one hot source still proceed.

37+

* Only the acquisition needs the order: releasing never waits, so the unlock

38+

* side does not mirror it.

39+

*/

40+

static void

41+

zpl_remap_lock_two(struct inode *src_i, struct inode *dst_i)

42+

{

43+

if (src_i == dst_i) {

44+

spl_inode_lock(dst_i);

45+

} else if (src_i < dst_i) {

46+

spl_inode_lock_shared(src_i);

47+

spl_inode_lock(dst_i);

48+

} else {

49+

spl_inode_lock(dst_i);

50+

spl_inode_lock_shared(src_i);

51+

}

52+

}

53+54+

static void

55+

zpl_remap_unlock_two(struct inode *src_i, struct inode *dst_i)

56+

{

57+

spl_inode_unlock(dst_i);

58+

if (src_i != dst_i)

59+

spl_inode_unlock_shared(src_i);

60+

}

61+2862

/*

2963

* Clone part of a file via block cloning.

3064

*

@@ -51,9 +85,7 @@ zpl_clone_file_range_impl(struct file *src_file, loff_t src_off,

5185

dmu_objset_spa(ITOZSB(dst_i)->z_os), SPA_FEATURE_BLOCK_CLONING))

5286

return (-EOPNOTSUPP);

538754-

if (src_i != dst_i)

55-

spl_inode_lock_shared(src_i);

56-

spl_inode_lock(dst_i);

88+

zpl_remap_lock_two(src_i, dst_i);

57895890

crhold(cr);

5991

cookie = spl_fstrans_mark();

@@ -64,16 +96,67 @@ zpl_clone_file_range_impl(struct file *src_file, loff_t src_off,

6496

spl_fstrans_unmark(cookie);

6597

crfree(cr);

669867-

spl_inode_unlock(dst_i);

68-

if (src_i != dst_i)

69-

spl_inode_unlock_shared(src_i);

99+

zpl_remap_unlock_two(src_i, dst_i);

7010071101

if (err < 0)

72102

return (err);

7310374104

return ((ssize_t)len_o);

75105

}

76106107+

#if defined(HAVE_VFS_REMAP_FILE_RANGE) || \

108+

defined(HAVE_VFS_DEDUPE_FILE_RANGE)

109+

/*

110+

* Logic shared by the FIDEDUPERANGE entry points. Compare len bytes at

111+

* src_off in src_file with dst_off in dst_file and, if they are identical,

112+

* share the underlying blocks via zfs_dedupe_range(). Returns the number of

113+

* bytes deduped, -EBADE if the ranges differ (which the VFS reports as

114+

* FILE_DEDUPE_RANGE_DIFFERS), or another negative errno on failure.

115+

*/

116+

static ssize_t

117+

zpl_dedupe_file_range_impl(struct file *src_file, loff_t src_off,

118+

struct file *dst_file, loff_t dst_off, size_t len, boolean_t can_shorten)

119+

{

120+

struct inode *src_i = file_inode(src_file);

121+

struct inode *dst_i = file_inode(dst_file);

122+

uint64_t src_off_o = (uint64_t)src_off;

123+

uint64_t dst_off_o = (uint64_t)dst_off;

124+

uint64_t len_o = (uint64_t)len;

125+

cred_t *cr = CRED();

126+

fstrans_cookie_t cookie;

127+

boolean_t same = B_FALSE;

128+

int err;

129+130+

if (!zfs_bclone_enabled)

131+

return (-EOPNOTSUPP);

132+133+

if (!spa_feature_is_enabled(

134+

dmu_objset_spa(ITOZSB(dst_i)->z_os), SPA_FEATURE_BLOCK_CLONING))

135+

return (-EOPNOTSUPP);

136+137+

zpl_remap_lock_two(src_i, dst_i);

138+139+

crhold(cr);

140+

cookie = spl_fstrans_mark();

141+142+

err = -zfs_dedupe_range(ITOZ(src_i), src_off_o, ITOZ(dst_i),

143+

dst_off_o, &len_o, cr, can_shorten, &same);

144+145+

spl_fstrans_unmark(cookie);

146+

crfree(cr);

147+148+

zpl_remap_unlock_two(src_i, dst_i);

149+150+

if (err < 0)

151+

return (err);

152+153+

if (!same)

154+

return (-EBADE);

155+156+

return ((ssize_t)len_o);

157+

}

158+

#endif

159+77160

/*

78161

* Entry point for copy_file_range(). Copy len bytes from src_off in src_file

79162

* to dst_off in dst_file. We are permitted to do this however we like, so we

@@ -149,9 +232,9 @@ zpl_remap_file_range(struct file *src_file, loff_t src_off,

149232

if (flags & ~(REMAP_FILE_DEDUP | REMAP_FILE_CAN_SHORTEN))

150233

return (-EINVAL);

151234152-

/* No support for dedup yet */

153235

if (flags & REMAP_FILE_DEDUP)

154-

return (-EOPNOTSUPP);

236+

return (zpl_dedupe_file_range_impl(src_file, src_off, dst_file,

237+

dst_off, len, !!(flags & REMAP_FILE_CAN_SHORTEN)));

155238156239

/* Zero length means to clone everything to the end of the file */

157240

if (len == 0)

198281

zpl_dedupe_file_range(struct file *src_file, loff_t src_off,

199282

struct file *dst_file, loff_t dst_off, uint64_t len)

200283

{

201-

/* No support for dedup yet */

202-

return (-EOPNOTSUPP);

284+

/*

285+

* The pre-4.20 dedupe interface has no way to signal that a short

286+

* dedupe is acceptable, so the whole range must match.

287+

*/

288+

return (zpl_dedupe_file_range_impl(src_file, src_off, dst_file,

289+

dst_off, len, B_FALSE));

203290

}

204291

#endif /* HAVE_VFS_DEDUPE_FILE_RANGE */