Skip to content

Commit 55f9aa5

Browse files
authored
dcp: avoid truncate when not needed (#642)
ftruncate is expensive on many file systems. Avoid calling truncate in unecessary situation like when using O_DIRECT but the file size is what is expected, or when not using O_DIRECT. The logic is implemented as follows with dcp: create file if EEXIST: # only need to truncate if it existed # ... and if using sparse or the size is larger than what the new size will be truncate file to 0 write data if O_DIRECT: # only need to truncate if O_DIRECT writes beyond EOF or with sparse in case we copy an all NULL buffer to be safe Signed-off-by: Mohamad Chaarawi <[email protected]>
1 parent 03b1122 commit 55f9aa5

File tree

1 file changed

+43
-19
lines changed

1 file changed

+43
-19
lines changed

src/common/mfu_flist_copy.c

Lines changed: 43 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1242,13 +1242,15 @@ static int mfu_create_file(
12421242
* for regular files, dev argument is supposed to be ignored,
12431243
* see makedev() to create valid dev */
12441244
dev_t dev;
1245+
bool file_exists = false;
12451246
memset(&dev, 0, sizeof(dev_t));
12461247
int mknod_rc = mfu_file_mknod(dest_path, DCOPY_DEF_PERMS_FILE | S_IFREG, dev, mfu_dst_file);
12471248
if(mknod_rc < 0) {
12481249
if(errno == EEXIST) {
12491250
/* destination already exists, no big deal, but print warning */
12501251
MFU_LOG(MFU_LOG_WARN, "Original file exists, skip the creation: `%s' (errno=%d %s)",
12511252
dest_path, errno, strerror(errno));
1253+
file_exists = true;
12521254
} else {
12531255
/* failed to create inode, that's a problem */
12541256
MFU_LOG(MFU_LOG_ERR, "File `%s' mknod() failed (errno=%d %s)",
@@ -1269,31 +1271,46 @@ static int mfu_create_file(
12691271
}
12701272
}
12711273

1272-
/* Truncate destination files to 0 bytes when sparse file is enabled,
1273-
* this is because we will not overwrite sections corresponding to holes
1274-
* and we need those to be set to 0 */
1275-
if (copy_opts->sparse) {
1276-
/* truncate destination file to 0 bytes */
1274+
if (file_exists) {
12771275
struct stat st;
12781276
int status = mfu_file_lstat(dest_path, &st, mfu_dst_file);
1279-
if (status == 0) {
1280-
/* destination exists, truncate it to 0 bytes */
1277+
if (status != 0) {
1278+
MFU_LOG(MFU_LOG_ERR, "mfu_file_lstat() file: `%s' (errno=%d %s)",
1279+
dest_path, errno, strerror(errno));
1280+
mfu_free(&dest_path);
1281+
return -1;
1282+
}
1283+
bool need_truncate = false;
1284+
/* Truncate destination files to 0 bytes when sparse file is enabled,
1285+
* this is because we will not overwrite sections corresponding to holes
1286+
* and we need those to be set to 0 */
1287+
if (copy_opts->sparse) {
1288+
need_truncate = true;
1289+
} else {
1290+
/* if src_file size < dest_file size, we also truncate */
1291+
struct stat src_st;
1292+
status = mfu_file_lstat(src_path, &src_st, mfu_src_file);
1293+
if (status != 0) {
1294+
MFU_LOG(MFU_LOG_ERR, "mfu_file_lstat() file: `%s' (errno=%d %s)",
1295+
src_path, errno, strerror(errno));
1296+
mfu_free(&dest_path);
1297+
return -1;
1298+
}
1299+
if (src_st.st_size < st.st_size)
1300+
need_truncate = true;
1301+
}
1302+
1303+
if (need_truncate) {
1304+
/* truncate destination file to 0 bytes */
12811305
status = mfu_file_truncate(dest_path, 0, mfu_dst_file);
12821306
if (status) {
12831307
/* when using sparse file optimization, consider this to be an error,
12841308
* since we will not be overwriting the holes */
12851309
MFU_LOG(MFU_LOG_ERR, "Failed to truncate destination file: `%s' (errno=%d %s)",
12861310
dest_path, errno, strerror(errno));
1287-
rc = -1;
1288-
}
1289-
} else if (errno == -ENOENT) {
1290-
/* destination does not exist, which is fine */
1291-
status = 0;
1292-
} else {
1293-
/* had an error stating destination file */
1294-
MFU_LOG(MFU_LOG_ERR, "mfu_file_lstat() file: `%s' (errno=%d %s)",
1295-
dest_path, errno, strerror(errno));
1296-
}
1311+
rc = -1;
1312+
}
1313+
}
12971314
}
12981315

12991316
#ifdef HPSS_SUPPORT
@@ -1722,7 +1739,7 @@ static int mfu_copy_file_normal(
17221739

17231740
/* initialize our starting offset within the file */
17241741
off_t off = offset;
1725-
1742+
bool need_truncate = false;
17261743
/* write data */
17271744
uint64_t total_bytes = 0;
17281745
while (total_bytes < length) {
@@ -1787,6 +1804,8 @@ static int mfu_copy_file_normal(
17871804
* current file if we fail before truncating */
17881805
char* bufzero = ((char*)buf + bytes_read);
17891806
memset(bufzero, 0, remainder);
1807+
/* truncate only in this case, otherwise truncate is not necessary */
1808+
need_truncate = true;
17901809
}
17911810

17921811
/* assumes buf_size is magic size for O_DIRECT */
@@ -1800,6 +1819,7 @@ static int mfu_copy_file_normal(
18001819
int skip_write = 0;
18011820
if (copy_opts->sparse && mfu_is_all_null(buf, bytes_to_write)) {
18021821
skip_write = 1;
1822+
need_truncate = true;
18031823
}
18041824

18051825
/* write data to destination file if needed */
@@ -1847,7 +1867,11 @@ static int mfu_copy_file_normal(
18471867
}
18481868
#endif
18491869

1850-
/* if we wrote the last chunk, truncate the file */
1870+
/* if not using O_DIRECT, we already truncated the file at the beginning, we do not need to do
1871+
* it again. If using O_DIRECT, truncate only if we wrote beyond the src EOF. */
1872+
if (!need_truncate)
1873+
return 0;
1874+
18511875
off_t last_written = offset + length;
18521876
off_t file_size_offt = (off_t) file_size;
18531877
if (last_written >= file_size_offt || file_size == 0) {

0 commit comments

Comments
 (0)