X-Git-Url: https://git.whamcloud.com/?a=blobdiff_plain;f=lustre%2Fosd-ldiskfs%2Fosd_io.c;h=f8568c7e0d1776ad01d608c6641d4bbb615a13d7;hb=0f81c5ae973bf7fba45b6ba7f9c5f4fb1f6eadcb;hp=dac36d241d4ca660fb201e7ac42377d61c8f25ed;hpb=8f793f14bf9928352623e61122f005252605b136;p=fs%2Flustre-release.git diff --git a/lustre/osd-ldiskfs/osd_io.c b/lustre/osd-ldiskfs/osd_io.c index dac36d2..f8568c7 100644 --- a/lustre/osd-ldiskfs/osd_io.c +++ b/lustre/osd-ldiskfs/osd_io.c @@ -27,7 +27,6 @@ */ /* * This file is part of Lustre, http://www.lustre.org/ - * Lustre is a trademark of Sun Microsystems, Inc. * * lustre/osd/osd_io.c * @@ -159,7 +158,7 @@ void osd_fini_iobuf(struct osd_device *d, struct osd_iobuf *iobuf) #ifdef HAVE_BIO_ENDIO_USES_ONE_ARG static void dio_complete_routine(struct bio *bio) { - int error = bio->bi_status; + int error = blk_status_to_errno(bio->bi_status); #else static void dio_complete_routine(struct bio *bio, int error) { @@ -440,6 +439,29 @@ static int osd_bio_init(struct bio *bio, struct osd_iobuf *iobuf, RETURN(0); } +static void osd_mark_page_io_done(struct osd_iobuf *iobuf, + struct inode *inode, + sector_t start_blocks, + sector_t count) +{ + struct niobuf_local *lnb; + int blocks_per_page = PAGE_SIZE >> inode->i_blkbits; + pgoff_t pg_start, pg_end; + + pg_start = start_blocks / blocks_per_page; + if (start_blocks % blocks_per_page) + pg_start++; + if (count >= blocks_per_page) + pg_end = (start_blocks + count - + blocks_per_page) / blocks_per_page; + else + return; /* nothing to mark */ + for ( ; pg_start <= pg_end; pg_start++) { + lnb = iobuf->dr_lnbs[pg_start]; + lnb->lnb_flags |= OBD_BRW_DONE; + } +} + static int osd_do_bio(struct osd_device *osd, struct inode *inode, struct osd_iobuf *iobuf, sector_t start_blocks, sector_t count) @@ -612,6 +634,11 @@ out: OBD_FREE_PTR(bio_private); } + /* Write only now */ + if (rc == 0 && iobuf->dr_rw) + osd_mark_page_io_done(iobuf, inode, + start_blocks, count); + RETURN(rc); } @@ -931,25 +958,36 @@ static int osd_chunk_trans_blocks(struct inode *inode, int nrblocks) return ret; } -static int osd_extend_trans(handle_t *handle, int needed) +#ifdef HAVE_LDISKFS_JOURNAL_ENSURE_CREDITS +static int osd_extend_restart_trans(handle_t *handle, int needed, + struct inode *inode) { - if (ldiskfs_handle_has_enough_credits(handle, needed)) - return 0; + int rc; - return ldiskfs_journal_extend(handle, - needed - handle->h_buffer_credits); -} + rc = ldiskfs_journal_ensure_credits(handle, needed, + ldiskfs_trans_default_revoke_credits(inode->i_sb)); + /* this means journal has been restarted */ + if (rc > 0) + rc = 0; -static int osd_extend_restart_trans(handle_t *handle, int needed) + return rc; +} +#else +static int osd_extend_restart_trans(handle_t *handle, int needed, + struct inode *inode) { + int rc; - int rc = osd_extend_trans(handle, needed); - + if (ldiskfs_handle_has_enough_credits(handle, needed)) + return 0; + rc = ldiskfs_journal_extend(handle, + needed - handle->h_buffer_credits); if (rc <= 0) return rc; return ldiskfs_journal_restart(handle, needed); } +#endif /* HAVE_LDISKFS_JOURNAL_ENSURE_CREDITS */ static int osd_ldiskfs_map_write(struct inode *inode, struct osd_iobuf *iobuf, struct osd_device *osd, sector_t start_blocks, @@ -977,12 +1015,45 @@ static int osd_ldiskfs_map_write(struct inode *inode, struct osd_iobuf *iobuf, return osd_do_bio(osd, inode, iobuf, start_blocks, count); } +static unsigned int osd_extent_bytes(const struct osd_device *o) +{ + unsigned int *extent_bytes_ptr = + raw_cpu_ptr(o->od_extent_bytes_percpu); + + if (likely(*extent_bytes_ptr)) + return *extent_bytes_ptr; + + /* initialize on first access or CPU hotplug */ + if (!ldiskfs_has_feature_extents(osd_sb(o))) + *extent_bytes_ptr = 1 << osd_sb(o)->s_blocksize_bits; + else + *extent_bytes_ptr = OSD_DEFAULT_EXTENT_BYTES; + + return *extent_bytes_ptr; +} + +#define EXTENT_BYTES_DECAY 64 +static void osd_decay_extent_bytes(struct osd_device *osd, + unsigned int new_bytes) +{ + unsigned int old_bytes; + + if (!ldiskfs_has_feature_extents(osd_sb(osd))) + return; + + old_bytes = osd_extent_bytes(osd); + *raw_cpu_ptr(osd->od_extent_bytes_percpu) = + (old_bytes * (EXTENT_BYTES_DECAY - 1) + + min(new_bytes, OSD_DEFAULT_EXTENT_BYTES) + + EXTENT_BYTES_DECAY - 1) / EXTENT_BYTES_DECAY; +} static int osd_ldiskfs_map_inode_pages(struct inode *inode, struct osd_iobuf *iobuf, struct osd_device *osd, int create, __u64 user_size, - int check_credits) + int check_credits, + struct thandle *thandle) { int blocks_per_page = PAGE_SIZE >> inode->i_blkbits; int rc = 0, i = 0, mapped_index = 0; @@ -990,7 +1061,6 @@ static int osd_ldiskfs_map_inode_pages(struct inode *inode, int clen = 0; pgoff_t max_page_index; handle_t *handle = NULL; - int credits; sector_t start_blocks = 0, count = 0; loff_t disk_size = 0; struct page **page = iobuf->dr_pages; @@ -1050,32 +1120,30 @@ cont_map: * transaction to make sure consistency. */ if (handle && check_credits) { - /* - * credits to insert 1 extent into extent tree. - */ - credits = osd_chunk_trans_blocks(inode, blen); - rc = osd_extend_trans(handle, credits); - if (rc < 0) - GOTO(cleanup, rc); + struct osd_thandle *oh; + + LASSERT(thandle != NULL); + oh = container_of(thandle, struct osd_thandle, + ot_super); /* * only issue IO if restart transaction needed, * as update disk size need hold inode lock, we * want to avoid that as much as possible. */ - if (rc > 0) { - WARN_ON_ONCE(start_blocks == 0); + if (oh->oh_declared_ext <= 0) { rc = osd_ldiskfs_map_write(inode, iobuf, osd, start_blocks, count, &disk_size, user_size); if (rc) GOTO(cleanup, rc); - rc = ldiskfs_journal_restart(handle, credits); - if (rc) - GOTO(cleanup, rc); - start_blocks += count; - /* reset IO block count */ - count = 0; + thandle->th_restart_tran = 1; + GOTO(cleanup, rc = -EAGAIN); } + + if (OBD_FAIL_CHECK(OBD_FAIL_OST_RESTART_IO)) + oh->oh_declared_ext = 0; + else + oh->oh_declared_ext--; } rc = ldiskfs_map_blocks(handle, inode, &map, create); if (rc >= 0) { @@ -1087,6 +1155,14 @@ cont_map: total++; break; } + if ((map.m_flags & LDISKFS_MAP_UNWRITTEN) && + !create) { + /* don't try to read allocated, but + * unwritten blocks, instead fill the + * patches with zeros in osd_do_bio() */ + *(blocks + total) = 0; + continue; + } *(blocks + total) = map.m_pblk + c; /* unmap any possible underlying * metadata from the block device @@ -1118,6 +1194,12 @@ cont_map: } if (rc == 0 && total < blen) { + /* + * decay extent blocks if we could not + * allocate extent once. + */ + osd_decay_extent_bytes(osd, + (total - previous_total) << inode->i_blkbits); map.m_lblk = fp->index * blocks_per_page + total; map.m_len = blen - total; previous_total = total; @@ -1125,7 +1207,14 @@ cont_map: } if (rc != 0) GOTO(cleanup, rc); - + /* + * decay extent blocks if we could allocate + * good large extent. + */ + if (total - previous_total >= + osd_extent_bytes(osd) >> inode->i_blkbits) + osd_decay_extent_bytes(osd, + (total - previous_total) << inode->i_blkbits); /* look for next extent */ fp = NULL; blocks += blocks_per_page * clen; @@ -1197,7 +1286,7 @@ static int osd_write_prep(const struct lu_env *env, struct dt_object *dt, if (iobuf->dr_npages) { rc = osd_ldiskfs_map_inode_pages(inode, iobuf, osd, 0, - 0, 0); + 0, 0, NULL); if (likely(rc == 0)) { rc = osd_do_bio(osd, inode, iobuf, 0, 0); /* do IO stats for preparation reads */ @@ -1221,7 +1310,6 @@ static int osd_is_mapped(struct dt_object *dt, __u64 offset, sector_t start; struct fiemap_extent_info fei = { 0 }; struct fiemap_extent fe = { 0 }; - mm_segment_t saved_fs; int rc; if (block >= cached_extent->start && block < cached_extent->end) @@ -1237,14 +1325,21 @@ static int osd_is_mapped(struct dt_object *dt, __u64 offset, fei.fi_extents_max = 1; fei.fi_extents_start = &fe; - saved_fs = get_fs(); - set_fs(KERNEL_DS); rc = inode->i_op->fiemap(inode, &fei, offset, FIEMAP_MAX_OFFSET-offset); - set_fs(saved_fs); if (rc != 0) return 0; start = fe.fe_logical >> inode->i_blkbits; + if (fei.fi_extents_mapped == 0) { + /* a special case - no extent found at this offset and forward. + * we can consider this as a hole to EOF. it's safe to cache + * as other threads can not allocate/punch blocks this thread + * is working on (LDLM). */ + cached_extent->start = block; + cached_extent->end = i_size_read(inode) >> inode->i_blkbits; + cached_extent->mapped = 0; + return 0; + } if (start > block) { cached_extent->start = block; @@ -1260,6 +1355,7 @@ static int osd_is_mapped(struct dt_object *dt, __u64 offset, return cached_extent->mapped; } +#define MAX_EXTENTS_PER_WRITE 100 static int osd_declare_write_commit(const struct lu_env *env, struct dt_object *dt, struct niobuf_local *lnb, int npages, @@ -1278,12 +1374,21 @@ static int osd_declare_write_commit(const struct lu_env *env, struct osd_fextent mapped = { 0 }, extent = { 0 }; enum osd_quota_local_flags local_flags = 0; enum osd_qid_declare_flags declare_flags = OSD_QID_BLK; + unsigned int extent_bytes; ENTRY; LASSERT(handle != NULL); oh = container_of(handle, struct osd_thandle, ot_super); LASSERT(oh->ot_handle == NULL); + /* + * We track a decaying average extent blocks per filesystem, + * for most of time, it will be 1M, with filesystem becoming + * heavily-fragmented, it will be reduced to 4K at the worst. + */ + extent_bytes = osd_extent_bytes(osd); + LASSERT(extent_bytes >= (1 << osd_sb(osd)->s_blocksize)); + /* calculate number of extents (probably better to pass nb) */ for (i = 0; i < npages; i++) { /* ignore quota for the whole request if any page is from @@ -1306,10 +1411,18 @@ static int osd_declare_write_commit(const struct lu_env *env, continue; } + if (lnb[i].lnb_flags & OBD_BRW_DONE) { + lnb[i].lnb_flags |= OBD_BRW_MAPPED; + continue; + } + /* count only unmapped changes */ newblocks++; if (lnb[i].lnb_file_offset != extent.end || extent.end == 0) { - extents++; + if (extent.end != 0) + extents += (extent.end - extent.start + + extent_bytes - 1) / extent_bytes; + extent.start = lnb[i].lnb_file_offset; extent.end = lnb[i].lnb_file_offset + lnb[i].lnb_len; } else { extent.end += lnb[i].lnb_len; @@ -1325,6 +1438,21 @@ static int osd_declare_write_commit(const struct lu_env *env, */ if (!newblocks) goto out_declare; + + extents += (extent.end - extent.start + + extent_bytes - 1) / extent_bytes; + /** + * with system space usage growing up, mballoc codes won't + * try best to scan block group to align best free extent as + * we can. So extent bytes per extent could be decayed to a + * very small value, this could make us reserve too many credits. + * We could be more optimistic in the credit reservations, even + * in a case where the filesystem is nearly full, it is extremely + * unlikely that the worst case would ever be hit. + */ + if (extents > MAX_EXTENTS_PER_WRITE) + extents = MAX_EXTENTS_PER_WRITE; + /* * each extent can go into new leaf causing a split * 5 is max tree depth: inode + 4 index blocks @@ -1345,12 +1473,7 @@ static int osd_declare_write_commit(const struct lu_env *env, credits += depth * extents; } - /* - * try a bit more extents to avoid restart - * as much as possible in normal case. - */ - if (npages > 1 && extents) - extents <<= 1; + oh->oh_declared_ext = extents; /* quota space for metadata blocks */ quota_space += depth * extents * LDISKFS_BLOCK_SIZE(osd_sb(osd)); @@ -1372,6 +1495,11 @@ static int osd_declare_write_commit(const struct lu_env *env, else credits += extents; + CDEBUG(D_INODE, + "%s: inode #%lu extent_bytes %u extents %d credits %d\n", + osd_ino2name(inode), inode->i_ino, extent_bytes, extents, + credits); + out_declare: osd_trans_declare_op(env, oh, OSD_OT_WRITE, credits); @@ -1409,9 +1537,6 @@ static int osd_write_commit(const struct lu_env *env, struct dt_object *dt, struct inode *inode = osd_dt_obj(dt)->oo_inode; struct osd_device *osd = osd_obj2dev(osd_dt_obj(dt)); int rc = 0, i, check_credits = 0; - struct osd_thandle *oh = container_of(thandle, - struct osd_thandle, ot_super); - unsigned int save_credits = oh->ot_credits; LASSERT(inode); @@ -1439,6 +1564,9 @@ static int osd_write_commit(const struct lu_env *env, struct dt_object *dt, continue; } + if (lnb[i].lnb_flags & OBD_BRW_DONE) + continue; + if (!(lnb[i].lnb_flags & OBD_BRW_MAPPED)) check_credits = 1; @@ -1464,28 +1592,19 @@ static int osd_write_commit(const struct lu_env *env, struct dt_object *dt, } else if (iobuf->dr_npages > 0) { rc = osd_ldiskfs_map_inode_pages(inode, iobuf, osd, 1, user_size, - check_credits); - /* - * Write might restart transaction, extend credits - * if needed for operations such as attribute set. - */ - if (rc == 0) { - handle_t *handle = ldiskfs_journal_current_handle(); - - LASSERT(handle != NULL); - rc = osd_extend_restart_trans(handle, save_credits); - } + check_credits, + thandle); } else { /* no pages to write, no transno is needed */ thandle->th_local = 1; } - if (rc != 0) + if (rc != 0 && !thandle->th_restart_tran) osd_fini_iobuf(osd, iobuf); osd_trans_exec_check(env, thandle, OSD_OT_WRITE); - if (unlikely(rc != 0)) { + if (unlikely(rc != 0 && !thandle->th_restart_tran)) { /* if write fails, we should drop pages from the cache */ for (i = 0; i < npages; i++) { if (lnb[i].lnb_page == NULL) @@ -1570,7 +1689,7 @@ static int osd_read_prep(const struct lu_env *env, struct dt_object *dt, if (iobuf->dr_npages) { rc = osd_ldiskfs_map_inode_pages(inode, iobuf, osd, 0, - 0, 0); + 0, 0, NULL); if (!rc) rc = osd_do_bio(osd, inode, iobuf, 0, 0); @@ -1890,7 +2009,8 @@ static int osd_ldiskfs_write_record(struct dt_object *dt, void *buf, ++bufsize; } - dirty_inode = test_and_set_bit(LDISKFS_INODE_JOURNAL_DATA, + /* only the first flag-set matters */ + dirty_inode = !test_and_set_bit(LDISKFS_INODE_JOURNAL_DATA, &ei->i_flags); /* sparse checking is racy, but sparse is very rare case, leave as is */ @@ -2067,15 +2187,29 @@ static int osd_declare_fallocate(const struct lu_env *env, ENTRY; /* - * Only mode == 0 (which is standard prealloc) is supported now. + * mode == 0 (which is standard prealloc) and PUNCH is supported * Rest of mode options is not supported yet. */ - if (mode & ~FALLOC_FL_KEEP_SIZE) + if (mode & ~(FALLOC_FL_KEEP_SIZE | FALLOC_FL_PUNCH_HOLE)) + RETURN(-EOPNOTSUPP); + + /* disable fallocate completely */ + if (osd_dev(dt->do_lu.lo_dev)->od_fallocate_zero_blocks < 0) RETURN(-EOPNOTSUPP); LASSERT(th); LASSERT(inode); + if (mode & FALLOC_FL_PUNCH_HOLE) { + rc = osd_declare_inode_qid(env, i_uid_read(inode), + i_gid_read(inode), + i_projid_read(inode), 0, oh, + osd_dt_obj(dt), NULL, OSD_QID_BLK); + if (rc == 0) + rc = osd_trunc_lock(osd_dt_obj(dt), oh, false); + RETURN(rc); + } + /* quota space for metadata blocks * approximate metadata estimate should be good enough. */ @@ -2096,8 +2230,10 @@ static int osd_declare_fallocate(const struct lu_env *env, RETURN(rc); } -static int osd_fallocate(const struct lu_env *env, struct dt_object *dt, - __u64 start, __u64 end, int mode, struct thandle *th) +static int osd_fallocate_preallocate(const struct lu_env *env, + struct dt_object *dt, + __u64 start, __u64 end, int mode, + struct thandle *th) { struct osd_thandle *oh = container_of(th, struct osd_thandle, ot_super); handle_t *handle = ldiskfs_journal_current_handle(); @@ -2129,7 +2265,10 @@ static int osd_fallocate(const struct lu_env *env, struct dt_object *dt, boff = start >> inode->i_blkbits; blen = (ALIGN(end, 1 << inode->i_blkbits) >> inode->i_blkbits) - boff; - flags = LDISKFS_GET_BLOCKS_CREATE; + /* Create and mark new extents as either zero or unwritten */ + flags = osd_dev(dt->do_lu.lo_dev)->od_fallocate_zero_blocks ? + LDISKFS_GET_BLOCKS_CREATE_ZERO : + LDISKFS_GET_BLOCKS_CREATE_UNWRIT_EXT; if (mode & FALLOC_FL_KEEP_SIZE) flags |= LDISKFS_GET_BLOCKS_KEEP_SIZE; @@ -2178,7 +2317,7 @@ static int osd_fallocate(const struct lu_env *env, struct dt_object *dt, } /* TODO: quota check */ - rc = osd_extend_restart_trans(handle, credits); + rc = osd_extend_restart_trans(handle, credits, inode); if (rc) break; @@ -2210,15 +2349,70 @@ static int osd_fallocate(const struct lu_env *env, struct dt_object *dt, } out: - inode_unlock(inode); - /* extand credits if needed for operations such as attribute set */ if (rc >= 0) - rc = osd_extend_restart_trans(handle, save_credits); + rc = osd_extend_restart_trans(handle, save_credits, inode); + + inode_unlock(inode); + + RETURN(rc); +} + +static int osd_fallocate_punch(const struct lu_env *env, struct dt_object *dt, + __u64 start, __u64 end, int mode, + struct thandle *th) +{ + struct osd_object *obj = osd_dt_obj(dt); + struct inode *inode = obj->oo_inode; + struct osd_access_lock *al; + struct osd_thandle *oh; + int rc = 0, found = 0; + + ENTRY; + + LASSERT(dt_object_exists(dt)); + LASSERT(osd_invariant(obj)); + LASSERT(inode != NULL); + + dquot_initialize(inode); + + LASSERT(th); + oh = container_of(th, struct osd_thandle, ot_super); + LASSERT(oh->ot_handle->h_transaction != NULL); + + list_for_each_entry(al, &oh->ot_trunc_locks, tl_list) { + if (obj != al->tl_obj) + continue; + LASSERT(al->tl_shared == 0); + found = 1; + /* do actual punch in osd_trans_stop() */ + al->tl_start = start; + al->tl_end = end; + al->tl_mode = mode; + al->tl_punch = true; + break; + } RETURN(rc); } +static int osd_fallocate(const struct lu_env *env, struct dt_object *dt, + __u64 start, __u64 end, int mode, struct thandle *th) +{ + int rc; + + ENTRY; + + if (mode & FALLOC_FL_PUNCH_HOLE) { + /* punch */ + rc = osd_fallocate_punch(env, dt, start, end, mode, th); + } else { + /* standard preallocate */ + rc = osd_fallocate_preallocate(env, dt, start, end, mode, th); + } + RETURN(rc); +} + static int osd_declare_punch(const struct lu_env *env, struct dt_object *dt, __u64 start, __u64 end, struct thandle *th) { @@ -2371,7 +2565,6 @@ static int osd_fiemap_get(const struct lu_env *env, struct dt_object *dt, struct inode *inode = osd_dt_obj(dt)->oo_inode; u64 len; int rc; - mm_segment_t cur_fs; LASSERT(inode); if (inode->i_op->fiemap == NULL) @@ -2391,18 +2584,10 @@ static int osd_fiemap_get(const struct lu_env *env, struct dt_object *dt, if (fieinfo.fi_flags & FIEMAP_FLAG_SYNC) filemap_write_and_wait(inode->i_mapping); - /* Save previous value address limit */ - cur_fs = get_fs(); - /* Set the address limit of the kernel */ - set_fs(KERNEL_DS); - rc = inode->i_op->fiemap(inode, &fieinfo, fm->fm_start, len); fm->fm_flags = fieinfo.fi_flags; fm->fm_mapped_extents = fieinfo.fi_extents_mapped; - /* Restore the previous address limt */ - set_fs(cur_fs); - return rc; } @@ -2550,6 +2735,27 @@ void osd_trunc_unlock_all(const struct lu_env *env, struct list_head *list) } } +/* + * For a partial-page truncate, flush the page to disk immediately to + * avoid data corruption during direct disk write. b=17397 + */ +static void osd_partial_page_flush(struct osd_device *d, struct inode *inode, + loff_t offset) +{ + if (!(offset & ~PAGE_MASK)) + return; + + if (osd_use_page_cache(d)) { + filemap_fdatawrite_range(inode->i_mapping, offset, offset + 1); + } else { + /* Notice we use "wait" version to ensure I/O is complete */ + filemap_write_and_wait_range(inode->i_mapping, offset, + offset + 1); + invalidate_mapping_pages(inode->i_mapping, offset >> PAGE_SHIFT, + offset >> PAGE_SHIFT); + } +} + void osd_execute_truncate(struct osd_object *obj) { struct osd_device *d = osd_obj2dev(obj); @@ -2585,24 +2791,22 @@ void osd_execute_truncate(struct osd_object *obj) spin_unlock(&inode->i_lock); osd_dirty_inode(inode, I_DIRTY_DATASYNC); } + osd_partial_page_flush(d, inode, size); +} - /* - * For a partial-page truncate, flush the page to disk immediately to - * avoid data corruption during direct disk write. b=17397 - */ - if ((size & ~PAGE_MASK) == 0) - return; - if (osd_use_page_cache(d)) { - filemap_fdatawrite_range(inode->i_mapping, size, size + 1); - } else { - /* Notice we use "wait" version to ensure I/O is complete */ - filemap_write_and_wait_range(inode->i_mapping, size, size + 1); - invalidate_mapping_pages(inode->i_mapping, size >> PAGE_SHIFT, - size >> PAGE_SHIFT); - } +void osd_execute_punch(const struct lu_env *env, struct osd_object *obj, + loff_t start, loff_t end, int mode) +{ + struct osd_device *d = osd_obj2dev(obj); + struct inode *inode = obj->oo_inode; + struct file *file = osd_quasi_file(env, inode); + + file->f_op->fallocate(file, mode, start, end - start); + osd_partial_page_flush(d, inode, start); + osd_partial_page_flush(d, inode, end - 1); } -void osd_process_truncates(struct list_head *list) +void osd_process_truncates(const struct lu_env *env, struct list_head *list) { struct osd_access_lock *al; @@ -2611,8 +2815,10 @@ void osd_process_truncates(struct list_head *list) list_for_each_entry(al, list, tl_list) { if (al->tl_shared) continue; - if (!al->tl_truncate) - continue; - osd_execute_truncate(al->tl_obj); + if (al->tl_truncate) + osd_execute_truncate(al->tl_obj); + else if (al->tl_punch) + osd_execute_punch(env, al->tl_obj, al->tl_start, + al->tl_end, al->tl_mode); } }