From mboxrd@z Thu Jan 1 00:00:00 1970 From: Dmitry Monakhov Subject: Re: [PATCH 3/3] ext4: Add support IOC_MOV_DATA ioctl Date: Mon, 14 Jul 2014 13:12:53 +0400 Message-ID: <87tx6ktiay.fsf@openvz.org> References: <004001cf9aa4$2670e280$7352a780$@samsung.com> Mime-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: QUOTED-PRINTABLE Cc: Dave Chinner , Theodore Ts'o , linux-ext4 , linux-fsdevel@vger.kernel.org, linux-kernel@vger.kernel.org, Brian Foster , Christoph Hellwig , Ashish Sangwan , xfs@oss.sgi.com To: =?utf-8?B?THVrw6HFoQ==?= Czerner , Namjae Jeon Return-path: Received: from mail-la0-f46.google.com ([209.85.215.46]:60338 "EHLO mail-la0-f46.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1751204AbaGNJM6 convert rfc822-to-8bit (ORCPT ); Mon, 14 Jul 2014 05:12:58 -0400 In-Reply-To: Sender: linux-ext4-owner@vger.kernel.org List-ID: On Tue, 8 Jul 2014 16:02:28 +0200 (CEST), Luk=C3=A1=C5=A1 Czerner wrote: Non-text part: MULTIPART/MIXED > On Tue, 8 Jul 2014, Namjae Jeon wrote: >=20 > > Date: Tue, 08 Jul 2014 21:00:02 +0900 > > From: Namjae Jeon > > To: Dave Chinner , Theodore Ts'o > > Cc: linux-ext4 , linux-fsdevel@vger.ker= nel.org, > > linux-kernel@vger.kernel.org, Luk=C3=A1=C5=A1 Czerner , > > Brian Foster , Christoph Hellwig , > > Ashish Sangwan , xfs@oss.sgi.com > > Subject: [PATCH 3/3] ext4: Add support IOC_MOV_DATA ioctl > >=20 > > This patch implements fs ioctl's IOC_MOV_DATA for Ext4. >=20 > Hmm isn't this basically what ext4_move_extents() does ? eg. > EXT4_IOC_MOVE_EXT ? >=20 > I guess that the intention here is to do the move, without actually > moving the data right ? But nevertheless maybe some code can be > shared with ext4_move_extents() ? It definitely can be shared, because it has specific case for unwritten data see move_extent_per_page(). But I think we can observe another way to unify this two things. An idea inspired by the fact that ioc_move_data works only for regular inodes, where orig_offset =3D=3D donor_offset. This is showstop= per for my utility e4defrag2 ( new version of e4defrag which is able defra= gment=20 pack small files as described here : http://lists.openwall.net/linux-ext4/2014/04/28/3)=20 Proposed API is very similar to ext4_ext_migrate: Args:=20 orig_file: inode which we want to defragment donor_file: a file which will be used as a donor of blocks 1) fallocate big donor_file 2) a) Create tmp inode wich nlink =3D 0 b) move extents required extents from donor to tmp_donor_inode c) return file descriptor (tmp_fd) to that tmp_donor_inode 4) Mark orig_file's inode with EXT4_STATE_EXT_MIGRATE state 5) Copy data from orig_file to tmp_fd 6) IOC_SWAP_EX: atomically swap orig_file->i_data and tmp_fd->i_data if EXT4_STATE_EXT_MIGRATE was not cleared. =20 This approach can works not only for regular file w/o journaling enabled, but also for journaled ones, and directories. =20 >=20 > -Lukas >=20 > >=20 > > The semantics of this ioctl are: > > 1) Like collapse range, offsets and length should be file system bl= ock size > > aligned. > > 2) In the receiver file, atleast length size hole should be present= at > > receiver_offset > > 3) It does not change file size of any of donor or receiver file. > > 4) It leaves a hole at the place from where blocks are moved out in= donor file. > > 5) Both (donor_offset + length) and (receiver_offset + length) shou= ld be within > > size of donor file and receiver file respectively. > > Only unwritten extents resides beyond file size and it does not = make sense > > to transfer unwritten extents, leave apart the security issues i= t may raise. > > 6) If the range to be transfered from donor file contain any holes,= they are > > replicated as it is in receiver file. It mean holes are preserve= d and > > the length of hole will be added to moved_len signifying that th= e hole range > > is succesfully transfered. > >=20 > > Signed-off-by: Namjae Jeon > > Signed-off-by: Ashish Sangwan > > --- > > fs/ext4/ext4.h | 2 + > > fs/ext4/extents.c | 375 ++++++++++++++++++++++++++++++++++++++++++= ++++++++++++ > > fs/ext4/file.c | 1 + > > 3 files changed, 378 insertions(+) > >=20 > > diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h > > index 6386c5f..26478eb 100644 > > --- a/fs/ext4/ext4.h > > +++ b/fs/ext4/ext4.h > > @@ -2725,6 +2725,8 @@ extern int ext4_fiemap(struct inode *inode, s= truct fiemap_extent_info *fieinfo, > > extern int ext4_ext_precache(struct inode *inode); > > extern int ext4_collapse_range(struct inode *inode, loff_t offset,= loff_t len); > > extern int ext4_insert_range(struct file *file, loff_t offset, lof= f_t len); > > +extern int ext4_mov_data(struct inode *, struct inode *, loff_t, l= off_t, loff_t, > > + loff_t *); > > =20 > > /* move_extent.c */ > > extern void ext4_double_down_write_data_sem(struct inode *first, > > diff --git a/fs/ext4/extents.c b/fs/ext4/extents.c > > index 0c2432e..511db03 100644 > > --- a/fs/ext4/extents.c > > +++ b/fs/ext4/extents.c > > @@ -5811,3 +5811,378 @@ out_mutex: > > mutex_unlock(&inode->i_mutex); > > return ret; > > } > > + > > +/* > > + * If offset_lblk does not lie on the extent start boundary, split= extent > > + */ > > +int ext4_find_and_split_extent_at(struct inode *inode, ext4_lblk_t= offset_lblk) > > +{ > > + struct ext4_ext_path *path; > > + handle_t *handle; > > + int credits, err =3D 0, split_flag, ex_len; > > + struct ext4_extent *ex; > > + int depth =3D ext_depth(inode); > > + ext4_lblk_t ex_start; > > + > > + path =3D ext4_ext_find_extent(inode, offset_lblk, NULL, 0); > > + if (IS_ERR(path)) > > + return PTR_ERR(path); > > + > > + ex =3D path[depth].p_ext; > > + if (!ex) > > + goto free_path; > > + ex_start =3D le32_to_cpu(ex->ee_block); > > + ex_len =3D ext4_ext_get_actual_len(ex); > > + > > + if (offset_lblk > ex_start && offset_lblk < (ex_start + ex_len)) = { > > + credits =3D ext4_writepage_trans_blocks(inode); > > + handle =3D ext4_journal_start(inode, EXT4_HT_TRUNCATE, credits); > > + if (IS_ERR(handle)) { > > + err =3D PTR_ERR(handle); > > + goto free_path; > > + } > > + if (ext4_ext_is_unwritten(ex)) > > + split_flag =3D EXT4_EXT_MARK_UNWRIT1 | > > + EXT4_EXT_MARK_UNWRIT2; > > + else > > + split_flag =3D 0; > > + > > + err =3D ext4_split_extent_at(handle, inode, path, offset_lblk, > > + split_flag, EXT4_EX_NOCACHE | > > + EXT4_GET_BLOCKS_PRE_IO); > > + ext4_journal_stop(handle); > > + } > > + > > +free_path: > > + ext4_ext_drop_refs(path); > > + kfree(path); > > + return err; > > +} > > + > > +/* > > + * Compute the size of hole in terms of filesystem blocks present = at offset_lblk > > + * until the next extent is found OR till we reach the last block = within isize. > > + * Store the computed value in hole_blkcnt. > > + * offset_lblk should be within isize of inode. > > + */ > > +int ext4_compute_hole_size(struct inode *inode, ext4_lblk_t offset= _lblk, > > + ext4_lblk_t *hole_blkcnt) > > +{ > > + struct ext4_ext_path *path; > > + struct ext4_extent *ex; > > + ext4_lblk_t ex_start, isize_lblk; > > + int ret =3D 0, depth, ex_len; > > + > > + isize_lblk =3D (inode->i_size + EXT4_BLOCK_SIZE(inode->i_sb) - 1)= >> > > + EXT4_BLOCK_SIZE_BITS(inode->i_sb); > > + > > + if (offset_lblk > isize_lblk) > > + return -EINVAL; > > + > > + *hole_blkcnt =3D 0; > > + path =3D ext4_ext_find_extent(inode, offset_lblk, NULL, 0); > > + if (IS_ERR(path)) > > + return PTR_ERR(path); > > + > > + depth =3D ext_depth(inode); > > + ex =3D path[depth].p_ext; > > + if (!ex) { > > + /* No blocks allocated in this file */ > > + *hole_blkcnt =3D isize_lblk - offset_lblk; > > + goto out; > > + } > > + ex_start =3D le32_to_cpu(ex->ee_block); > > + ex_len =3D ext4_ext_get_actual_len(ex); > > + > > + /* if offset_lblk lies within extent? */ > > + if (offset_lblk >=3D ex_start && offset_lblk < (ex_start + ex_len= )) > > + goto out; > > + > > + if (ex_start < offset_lblk) { > > + ret =3D mext_next_extent(inode, path, &ex); > > + if (!ret) { > > + ex_start =3D le32_to_cpu(ex->ee_block); > > + } else { > > + if (ret =3D=3D 1) { > > + *hole_blkcnt =3D isize_lblk - offset_lblk; > > + ret =3D 0; > > + } > > + goto out; > > + } > > + } > > + *hole_blkcnt =3D (ex_start < isize_lblk) ? (ex_start - offset_lbl= k) : > > + (isize_lblk - offset_lblk); > > +out: > > + ext4_ext_drop_refs(path); > > + kfree(path); > > + > > + return ret; > > +} > > + > > +/* > > + * Remove a complete extent from in memory and on-disk extent tree > > + * without freeing any data blocks covered by the extent. Caller m= ust call > > + * ext4_mark_inode_dirty() to sync the changes to disk. > > + */ > > +int ext4_ext_rm_extent(handle_t *handle, struct inode *inode, > > + struct ext4_ext_path *path, struct ext4_extent *ex) > > +{ > > + struct ext4_extent_header *eh; > > + int depth =3D ext_depth(inode); > > + int credits, err, correct_index =3D 0; > > + int ex_ee_len =3D ext4_ext_get_actual_len(ex); > > + > > + if (!path[depth].p_hdr) > > + path[depth].p_hdr =3D ext_block_hdr(path[depth].p_bh); > > + eh =3D path[depth].p_hdr; > > + > > + credits =3D 7 + 2*(ex_ee_len/EXT4_BLOCKS_PER_GROUP(inode->i_sb)); > > + if (ex =3D=3D EXT_FIRST_EXTENT(eh)) { > > + correct_index =3D 1; > > + credits +=3D (ext_depth(inode)) + 1; > > + } > > + credits +=3D EXT4_MAXQUOTAS_TRANS_BLOCKS(inode->i_sb); > > + err =3D ext4_ext_truncate_extend_restart(handle, inode, credits); > > + if (err) > > + return err; > > + > > + err =3D ext4_ext_get_access(handle, inode, path + depth); > > + if (err) > > + return err; > > + > > + ext4_ext_store_pblock(ex, 0); > > + memmove(ex, ex+1, > > + (EXT_LAST_EXTENT(eh) - ex) * sizeof(struct ext4_extent)); > > + memset(EXT_LAST_EXTENT(eh), 0, sizeof(struct ext4_extent)); > > + le16_add_cpu(&eh->eh_entries, -1); > > + > > + err =3D ext4_ext_dirty(handle, inode, path + depth); > > + if (err) > > + return err; > > + > > + if (correct_index && eh->eh_entries) > > + err =3D ext4_ext_correct_indexes(handle, inode, path); > > + > > + if (err =3D=3D 0 && eh->eh_entries =3D=3D 0 && path[depth].p_bh != =3D NULL) > > + err =3D ext4_ext_rm_idx(handle, inode, path, depth); > > + > > + return err; > > +} > > + > > +/* > > + * Move len_lblk amount of blocks from donor inode to receiver ino= de. > > + * Blocks are to be moved from doffset_lblk and moved to roffset_l= blk. > > + * Caller of this function must make sure there is atleast len_lbl= k size > > + * hole at roffset_lblk. Also doffset_lblk and doffset_lblk + len_= lblk > > + * should fall on extent boundary. > > + */ > > +int ext4_ext_mov_data(struct inode *donor, struct inode *receiver, > > + ext4_lblk_t doffset_lblk, ext4_lblk_t roffset_lblk, > > + ext4_lblk_t len_lblk, loff_t *bytes_moved) > > +{ > > + int error =3D 0, depth =3D ext_depth(donor); > > + struct ext4_ext_path *path; > > + struct ext4_extent *ex; > > + loff_t blocks_moved =3D 0; > > + handle_t *handle; > > + int credits =3D ext4_writepage_trans_blocks(donor) + > > + ext4_writepage_trans_blocks(receiver); > > + > > + while (blocks_moved < len_lblk && !error) { > > + struct ext4_ext_path *rpath =3D NULL; > > + ext4_lblk_t ex_start; > > + int ex_len; > > + > > + path =3D ext4_ext_find_extent(donor, doffset_lblk, NULL, 0); > > + if (IS_ERR(path)) { > > + error =3D PTR_ERR(path); > > + break; > > + } > > + ex =3D path[depth].p_ext; > > + /* > > + * No allocated blocks? This could only happen during > > + * 1st iteration. Otherwise it is en error. > > + */ > > + if (!ex) { > > + if (blocks_moved) > > + error =3D -EIO; > > + else > > + blocks_moved =3D len_lblk; > > + goto out; > > + } > > + ex_start =3D le32_to_cpu(ex->ee_block); > > + ex_len =3D ext4_ext_get_actual_len(ex); > > + > > + if (doffset_lblk !=3D ex_start) { > > + /* Hole within range, move to the next extent */ > > + if (ex_start < doffset_lblk) > > + error =3D mext_next_extent(donor, path, &ex); > > + /* Below if will also handle ex_start > doffset_lblk */ > > + if (error =3D=3D 0) { > > + ex_start =3D le32_to_cpu(ex->ee_block); > > + blocks_moved +=3D ex_start - doffset_lblk; > > + roffset_lblk +=3D ex_start - doffset_lblk; > > + doffset_lblk =3D ex_start; > > + } > > + if (error =3D=3D 1) { > > + /* doffset_lblk till EOF is hole. Success!! */ > > + blocks_moved =3D len_lblk; > > + error =3D 0; > > + } > > + goto out; > > + } > > + > > + /* Add this extent to receiver */ > > + handle =3D ext4_journal_start(donor, EXT4_HT_TRUNCATE, credits); > > + if (IS_ERR(handle)) { > > + error =3D PTR_ERR(handle); > > + goto out; > > + } > > + > > + rpath =3D ext4_ext_find_extent(receiver, roffset_lblk, NULL, 0); > > + if (IS_ERR(rpath)) { > > + error =3D PTR_ERR(rpath); > > + ext4_journal_stop(handle); > > + goto out; > > + } > > + ex->ee_block =3D cpu_to_le32(roffset_lblk); > > + error =3D ext4_ext_insert_extent(handle, receiver, rpath, ex, 0)= ; > > + if (error) > > + goto hout; > > + > > + /* Remove this extent from donor */ > > + error =3D ext4_ext_rm_extent(handle, donor, path, ex); > > + if (error) > > + goto hout; > > + > > + /* Extent moved successfully */ > > + roffset_lblk +=3D ex_len; > > + doffset_lblk +=3D ex_len; > > + blocks_moved +=3D ex_len; > > + > > + donor->i_blocks -=3D (ex_len << (donor->i_blkbits - 9)); > > + receiver->i_blocks +=3D (ex_len << (receiver->i_blkbits - 9)); > > + donor->i_mtime =3D donor->i_ctime =3D ext4_current_time(donor); > > + receiver->i_mtime =3D receiver->i_ctime =3D > > + ext4_current_time(receiver); > > + ext4_mark_inode_dirty(handle, donor); > > + ext4_mark_inode_dirty(handle, receiver); > > +hout: > > + ext4_journal_stop(handle); > > + ext4_ext_drop_refs(rpath); > > + kfree(rpath); > > +out: > > + ext4_ext_drop_refs(path); > > + kfree(path); > > + } > > + > > + /* This can happen when (doffset_lblk + len_lblk) is in a hole */ > > + if (blocks_moved > len_lblk) > > + blocks_moved =3D len_lblk; > > + > > + *bytes_moved =3D blocks_moved << EXT4_BLOCK_SIZE_BITS(donor->i_sb= ); > > + return error; > > +} > > + > > +int ext4_mov_data(struct inode *donor, struct inode *receiver, lof= f_t doffset, > > + loff_t roffset, loff_t len, loff_t *moved_len) > > +{ > > + struct super_block *sb =3D donor->i_sb; > > + loff_t d_pg_off, r_pg_off, pg_len; > > + ext4_lblk_t doffset_lblk, roffset_lblk, len_lblk, hole_size; > > + int error; > > + > > + if (doffset & (EXT4_BLOCK_SIZE(sb) - 1) || > > + roffset & (EXT4_BLOCK_SIZE(sb) - 1) || > > + len & (EXT4_BLOCK_SIZE(sb) - 1)) > > + return -EINVAL; > > + > > + if (EXT4_SB(sb)->s_cluster_ratio > 1) > > + return -EOPNOTSUPP; > > + > > + if (!ext4_test_inode_flag(donor, EXT4_INODE_EXTENTS) || > > + !ext4_test_inode_flag(receiver, EXT4_INODE_EXTENTS)) > > + return -EOPNOTSUPP; > > + > > + doffset_lblk =3D doffset >> EXT4_BLOCK_SIZE_BITS(sb); > > + roffset_lblk =3D roffset >> EXT4_BLOCK_SIZE_BITS(sb); > > + len_lblk =3D len >> EXT4_BLOCK_SIZE_BITS(sb); > > + > > + d_pg_off =3D round_down(doffset, PAGE_SIZE); > > + r_pg_off =3D round_down(roffset, PAGE_SIZE); > > + pg_len =3D round_up(len, PAGE_SIZE); > > + > > + if (ext4_should_journal_data(donor)) { > > + error =3D ext4_force_commit(donor->i_sb); > > + if (error) > > + return error; > > + error =3D ext4_force_commit(receiver->i_sb); > > + if (error) > > + return error; > > + } > > + > > + error =3D filemap_write_and_wait_range(donor->i_mapping, > > + d_pg_off, d_pg_off + pg_len); > > + if (error) > > + return error; > > + error =3D filemap_write_and_wait_range(receiver->i_mapping, > > + r_pg_off, r_pg_off + pg_len); > > + if (error) > > + return error; > > + > > + lock_two_nondirectories(donor, receiver); > > + > > + /* Check for isize limits for both files */ > > + if (doffset + len > donor->i_size || > > + roffset + len > receiver->i_size) { > > + error =3D -EINVAL; > > + goto out_mutex; > > + } > > + > > + truncate_pagecache_range(donor, d_pg_off, d_pg_off + pg_len - 1); > > + truncate_pagecache_range(receiver, r_pg_off, r_pg_off + pg_len - = 1); > > + > > + ext4_inode_block_unlocked_dio(donor); > > + inode_dio_wait(donor); > > + ext4_inode_block_unlocked_dio(receiver); > > + inode_dio_wait(receiver); > > + > > + ext4_discard_preallocations(donor); > > + ext4_discard_preallocations(receiver); > > + > > + error =3D ext4_es_remove_extent(donor, doffset_lblk, len_lblk); > > + if (error) > > + goto out_sem; > > + error =3D ext4_es_remove_extent(receiver, roffset_lblk, len_lblk)= ; > > + if (error) > > + goto out_sem; > > + > > + error =3D ext4_compute_hole_size(receiver, roffset_lblk, &hole_si= ze); > > + if (error) > > + goto out_sem; > > + if (len_lblk > hole_size) { > > + error =3D -EINVAL; > > + goto out_sem; > > + } > > + > > + error =3D ext4_find_and_split_extent_at(donor, doffset_lblk); > > + if (error) > > + goto out_sem; > > + > > + error =3D ext4_find_and_split_extent_at(donor, doffset_lblk + len= _lblk); > > + if (error) > > + goto out_sem; > > + > > + error =3D ext4_ext_mov_data(donor, receiver, doffset_lblk, > > + roffset_lblk, len_lblk, moved_len); > > + > > + ext4_discard_preallocations(donor); > > + ext4_discard_preallocations(receiver); > > +out_sem: > > + ext4_inode_resume_unlocked_dio(donor); > > + ext4_inode_resume_unlocked_dio(receiver); > > + > > +out_mutex: > > + unlock_two_nondirectories(donor, receiver); > > + return error; > > +} > > diff --git a/fs/ext4/file.c b/fs/ext4/file.c > > index 8695f70..d2feaba 100644 > > --- a/fs/ext4/file.c > > +++ b/fs/ext4/file.c > > @@ -614,5 +614,6 @@ const struct inode_operations ext4_file_inode_o= perations =3D { > > .get_acl =3D ext4_get_acl, > > .set_acl =3D ext4_set_acl, > > .fiemap =3D ext4_fiemap, > > + .mov_data =3D ext4_mov_data, > > }; > > =20 > >=20 -- To unsubscribe from this list: send the line "unsubscribe linux-ext4" i= n the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from relay.sgi.com (relay1.corp.sgi.com [137.38.102.111]) by oss.sgi.com (Postfix) with ESMTP id E50897F83 for ; Mon, 14 Jul 2014 04:13:05 -0500 (CDT) Received: from cuda.sgi.com (cuda3.sgi.com [192.48.176.15]) by relay1.corp.sgi.com (Postfix) with ESMTP id D1C508F8035 for ; Mon, 14 Jul 2014 02:13:02 -0700 (PDT) Received: from mail-lb0-f178.google.com (mail-lb0-f178.google.com [209.85.217.178]) by cuda.sgi.com with ESMTP id pL9CcJtPCInkZa0L (version=TLSv1 cipher=RC4-SHA bits=128 verify=NO) for ; Mon, 14 Jul 2014 02:12:57 -0700 (PDT) Received: by mail-lb0-f178.google.com with SMTP id 10so2640921lbg.9 for ; Mon, 14 Jul 2014 02:12:56 -0700 (PDT) From: Dmitry Monakhov Subject: Re: [PATCH 3/3] ext4: Add support IOC_MOV_DATA ioctl In-Reply-To: References: <004001cf9aa4$2670e280$7352a780$@samsung.com> Date: Mon, 14 Jul 2014 13:12:53 +0400 Message-ID: <87tx6ktiay.fsf@openvz.org> MIME-Version: 1.0 List-Id: XFS Filesystem from SGI List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: base64 Errors-To: xfs-bounces@oss.sgi.com Sender: xfs-bounces@oss.sgi.com To: =?utf-8?B?THVrw6HFoQ==?= Czerner , Namjae Jeon Cc: Theodore Ts'o , Brian Foster , linux-kernel@vger.kernel.org, xfs@oss.sgi.com, Christoph Hellwig , Ashish Sangwan , linux-fsdevel@vger.kernel.org, linux-ext4 T24gVHVlLCA4IEp1bCAyMDE0IDE2OjAyOjI4ICswMjAwIChDRVNUKSwgTHVrw6HFoSBDemVybmVy IDxsY3plcm5lckByZWRoYXQuY29tPiB3cm90ZToKTm9uLXRleHQgcGFydDogTVVMVElQQVJUL01J WEVECj4gT24gVHVlLCA4IEp1bCAyMDE0LCBOYW1qYWUgSmVvbiB3cm90ZToKPiAKPiA+IERhdGU6 IFR1ZSwgMDggSnVsIDIwMTQgMjE6MDA6MDIgKzA5MDAKPiA+IEZyb206IE5hbWphZSBKZW9uIDxu YW1qYWUuamVvbkBzYW1zdW5nLmNvbT4KPiA+IFRvOiBEYXZlIENoaW5uZXIgPGRhdmlkQGZyb21v cmJpdC5jb20+LCBUaGVvZG9yZSBUcydvIDx0eXRzb0BtaXQuZWR1Pgo+ID4gQ2M6IGxpbnV4LWV4 dDQgPGxpbnV4LWV4dDRAdmdlci5rZXJuZWwub3JnPiwgbGludXgtZnNkZXZlbEB2Z2VyLmtlcm5l bC5vcmcsCj4gPiAgICAgbGludXgta2VybmVsQHZnZXIua2VybmVsLm9yZywgTHVrw6HFoSBDemVy bmVyIDxsY3plcm5lckByZWRoYXQuY29tPiwKPiA+ICAgICBCcmlhbiBGb3N0ZXIgPGJmb3N0ZXJA cmVkaGF0LmNvbT4sIENocmlzdG9waCBIZWxsd2lnIDxoY2hAaW5mcmFkZWFkLm9yZz4sCj4gPiAg ICAgQXNoaXNoIFNhbmd3YW4gPGEuc2FuZ3dhbkBzYW1zdW5nLmNvbT4sIHhmc0Bvc3Muc2dpLmNv bQo+ID4gU3ViamVjdDogW1BBVENIIDMvM10gZXh0NDogQWRkIHN1cHBvcnQgSU9DX01PVl9EQVRB IGlvY3RsCj4gPiAKPiA+IFRoaXMgcGF0Y2ggaW1wbGVtZW50cyBmcyBpb2N0bCdzIElPQ19NT1Zf REFUQSBmb3IgRXh0NC4KPiAKPiBIbW0gaXNuJ3QgdGhpcyBiYXNpY2FsbHkgd2hhdCBleHQ0X21v dmVfZXh0ZW50cygpIGRvZXMgPyBlZy4KPiBFWFQ0X0lPQ19NT1ZFX0VYVCA/Cj4gCj4gSSBndWVz cyB0aGF0IHRoZSBpbnRlbnRpb24gaGVyZSBpcyB0byBkbyB0aGUgbW92ZSwgd2l0aG91dCBhY3R1 YWxseQo+IG1vdmluZyB0aGUgZGF0YSByaWdodCA/IEJ1dCBuZXZlcnRoZWxlc3MgbWF5YmUgc29t ZSBjb2RlIGNhbiBiZQo+IHNoYXJlZCB3aXRoIGV4dDRfbW92ZV9leHRlbnRzKCkgPwpJdCBkZWZp bml0ZWx5IGNhbiBiZSBzaGFyZWQsIGJlY2F1c2UgaXQgaGFzIHNwZWNpZmljIGNhc2UgZm9yIHVu d3JpdHRlbgpkYXRhIHNlZSBtb3ZlX2V4dGVudF9wZXJfcGFnZSgpLgpCdXQgSSB0aGluayB3ZSBj YW4gb2JzZXJ2ZSBhbm90aGVyIHdheSB0byB1bmlmeSB0aGlzIHR3byB0aGluZ3MuCkFuIGlkZWEg aW5zcGlyZWQgYnkgdGhlIGZhY3QgdGhhdCBpb2NfbW92ZV9kYXRhIHdvcmtzIG9ubHkgZm9yCnJl Z3VsYXIgaW5vZGVzLCB3aGVyZSBvcmlnX29mZnNldCA9PSBkb25vcl9vZmZzZXQuIFRoaXMgaXMg c2hvd3N0b3BwZXIKZm9yICBteSB1dGlsaXR5IGU0ZGVmcmFnMiAoIG5ldyB2ZXJzaW9uIG9mIGU0 ZGVmcmFnIHdoaWNoIGlzIGFibGUgZGVmcmFnbWVudCAKcGFjayBzbWFsbCBmaWxlcyBhcyBkZXNj cmliZWQgaGVyZSA6Cmh0dHA6Ly9saXN0cy5vcGVud2FsbC5uZXQvbGludXgtZXh0NC8yMDE0LzA0 LzI4LzMpIAoKUHJvcG9zZWQgQVBJIGlzIHZlcnkgc2ltaWxhciB0byBleHQ0X2V4dF9taWdyYXRl OgpBcmdzOiAKICBvcmlnX2ZpbGU6IGlub2RlIHdoaWNoIHdlIHdhbnQgdG8gZGVmcmFnbWVudAog IGRvbm9yX2ZpbGU6IGEgZmlsZSB3aGljaCB3aWxsIGJlIHVzZWQgYXMgYSBkb25vciBvZiBibG9j a3MKMSkgZmFsbG9jYXRlIGJpZyBkb25vcl9maWxlCjIpIGEpIENyZWF0ZSB0bXAgaW5vZGUgd2lj aCBubGluayA9IDAKICAgYikgbW92ZSBleHRlbnRzIHJlcXVpcmVkIGV4dGVudHMgZnJvbSAgZG9u b3IgdG8gdG1wX2Rvbm9yX2lub2RlCiAgIGMpIHJldHVybiBmaWxlIGRlc2NyaXB0b3IgKHRtcF9m ZCkgdG8gdGhhdCB0bXBfZG9ub3JfaW5vZGUKNCkgTWFyayBvcmlnX2ZpbGUncyBpbm9kZSB3aXRo IEVYVDRfU1RBVEVfRVhUX01JR1JBVEUgc3RhdGUKNSkgQ29weSBkYXRhIGZyb20gb3JpZ19maWxl IHRvIHRtcF9mZAo2KSBJT0NfU1dBUF9FWDogYXRvbWljYWxseSBzd2FwICBvcmlnX2ZpbGUtPmlf ZGF0YSBhbmQgdG1wX2ZkLT5pX2RhdGEKICAgaWYgRVhUNF9TVEFURV9FWFRfTUlHUkFURSB3YXMg bm90IGNsZWFyZWQuCiAKVGhpcyBhcHByb2FjaCBjYW4gd29ya3Mgbm90IG9ubHkgZm9yIHJlZ3Vs YXIgZmlsZSB3L28gam91cm5hbGluZwplbmFibGVkLCBidXQgYWxzbyBmb3Igam91cm5hbGVkIG9u ZXMsIGFuZCBkaXJlY3Rvcmllcy4KICAgICAgIAoKCgo+IAo+IC1MdWthcwo+IAo+ID4gCj4gPiBU aGUgc2VtYW50aWNzIG9mIHRoaXMgaW9jdGwgYXJlOgo+ID4gMSkgTGlrZSBjb2xsYXBzZSByYW5n ZSwgb2Zmc2V0cyBhbmQgbGVuZ3RoIHNob3VsZCBiZSBmaWxlIHN5c3RlbSBibG9jayBzaXplCj4g PiAgICBhbGlnbmVkLgo+ID4gMikgSW4gdGhlIHJlY2VpdmVyIGZpbGUsIGF0bGVhc3QgbGVuZ3Ro IHNpemUgaG9sZSBzaG91bGQgYmUgcHJlc2VudCBhdAo+ID4gICAgcmVjZWl2ZXJfb2Zmc2V0Cj4g PiAzKSBJdCBkb2VzIG5vdCBjaGFuZ2UgZmlsZSBzaXplIG9mIGFueSBvZiBkb25vciBvciByZWNl aXZlciBmaWxlLgo+ID4gNCkgSXQgbGVhdmVzIGEgaG9sZSBhdCB0aGUgcGxhY2UgZnJvbSB3aGVy ZSBibG9ja3MgYXJlIG1vdmVkIG91dCBpbiBkb25vciBmaWxlLgo+ID4gNSkgQm90aCAoZG9ub3Jf b2Zmc2V0ICsgbGVuZ3RoKSBhbmQgKHJlY2VpdmVyX29mZnNldCArIGxlbmd0aCkgc2hvdWxkIGJl IHdpdGhpbgo+ID4gICAgc2l6ZSBvZiBkb25vciBmaWxlIGFuZCByZWNlaXZlciBmaWxlIHJlc3Bl Y3RpdmVseS4KPiA+ICAgIE9ubHkgdW53cml0dGVuIGV4dGVudHMgcmVzaWRlcyBiZXlvbmQgZmls ZSBzaXplIGFuZCBpdCBkb2VzIG5vdCBtYWtlIHNlbnNlCj4gPiAgICB0byB0cmFuc2ZlciB1bndy aXR0ZW4gZXh0ZW50cywgbGVhdmUgYXBhcnQgdGhlIHNlY3VyaXR5IGlzc3VlcyBpdCBtYXkgcmFp c2UuCj4gPiA2KSBJZiB0aGUgcmFuZ2UgdG8gYmUgdHJhbnNmZXJlZCBmcm9tIGRvbm9yIGZpbGUg Y29udGFpbiBhbnkgaG9sZXMsIHRoZXkgYXJlCj4gPiAgICByZXBsaWNhdGVkIGFzIGl0IGlzIGlu IHJlY2VpdmVyIGZpbGUuIEl0IG1lYW4gaG9sZXMgYXJlIHByZXNlcnZlZCBhbmQKPiA+ICAgIHRo ZSBsZW5ndGggb2YgaG9sZSB3aWxsIGJlIGFkZGVkIHRvIG1vdmVkX2xlbiBzaWduaWZ5aW5nIHRo YXQgdGhlIGhvbGUgcmFuZ2UKPiA+ICAgIGlzIHN1Y2Nlc2Z1bGx5IHRyYW5zZmVyZWQuCj4gPiAK PiA+IFNpZ25lZC1vZmYtYnk6IE5hbWphZSBKZW9uIDxuYW1qYWUuamVvbkBzYW1zdW5nLmNvbT4K PiA+IFNpZ25lZC1vZmYtYnk6IEFzaGlzaCBTYW5nd2FuIDxhLnNhbmd3YW5Ac2Ftc3VuZy5jb20+ Cj4gPiAtLS0KPiA+ICBmcy9leHQ0L2V4dDQuaCAgICB8ICAgMiArCj4gPiAgZnMvZXh0NC9leHRl bnRzLmMgfCAzNzUgKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysr KysrKysrKysrCj4gPiAgZnMvZXh0NC9maWxlLmMgICAgfCAgIDEgKwo+ID4gIDMgZmlsZXMgY2hh bmdlZCwgMzc4IGluc2VydGlvbnMoKykKPiA+IAo+ID4gZGlmZiAtLWdpdCBhL2ZzL2V4dDQvZXh0 NC5oIGIvZnMvZXh0NC9leHQ0LmgKPiA+IGluZGV4IDYzODZjNWYuLjI2NDc4ZWIgMTAwNjQ0Cj4g PiAtLS0gYS9mcy9leHQ0L2V4dDQuaAo+ID4gKysrIGIvZnMvZXh0NC9leHQ0LmgKPiA+IEBAIC0y NzI1LDYgKzI3MjUsOCBAQCBleHRlcm4gaW50IGV4dDRfZmllbWFwKHN0cnVjdCBpbm9kZSAqaW5v ZGUsIHN0cnVjdCBmaWVtYXBfZXh0ZW50X2luZm8gKmZpZWluZm8sCj4gPiAgZXh0ZXJuIGludCBl eHQ0X2V4dF9wcmVjYWNoZShzdHJ1Y3QgaW5vZGUgKmlub2RlKTsKPiA+ICBleHRlcm4gaW50IGV4 dDRfY29sbGFwc2VfcmFuZ2Uoc3RydWN0IGlub2RlICppbm9kZSwgbG9mZl90IG9mZnNldCwgbG9m Zl90IGxlbik7Cj4gPiAgZXh0ZXJuIGludCBleHQ0X2luc2VydF9yYW5nZShzdHJ1Y3QgZmlsZSAq ZmlsZSwgbG9mZl90IG9mZnNldCwgbG9mZl90IGxlbik7Cj4gPiArZXh0ZXJuIGludCBleHQ0X21v dl9kYXRhKHN0cnVjdCBpbm9kZSAqLCBzdHJ1Y3QgaW5vZGUgKiwgbG9mZl90LCBsb2ZmX3QsIGxv ZmZfdCwKPiA+ICsJCQkgbG9mZl90ICopOwo+ID4gIAo+ID4gIC8qIG1vdmVfZXh0ZW50LmMgKi8K PiA+ICBleHRlcm4gdm9pZCBleHQ0X2RvdWJsZV9kb3duX3dyaXRlX2RhdGFfc2VtKHN0cnVjdCBp bm9kZSAqZmlyc3QsCj4gPiBkaWZmIC0tZ2l0IGEvZnMvZXh0NC9leHRlbnRzLmMgYi9mcy9leHQ0 L2V4dGVudHMuYwo+ID4gaW5kZXggMGMyNDMyZS4uNTExZGIwMyAxMDA2NDQKPiA+IC0tLSBhL2Zz L2V4dDQvZXh0ZW50cy5jCj4gPiArKysgYi9mcy9leHQ0L2V4dGVudHMuYwo+ID4gQEAgLTU4MTEs MyArNTgxMSwzNzggQEAgb3V0X211dGV4Ogo+ID4gIAltdXRleF91bmxvY2soJmlub2RlLT5pX211 dGV4KTsKPiA+ICAJcmV0dXJuIHJldDsKPiA+ICB9Cj4gPiArCj4gPiArLyoKPiA+ICsgKiBJZiBv ZmZzZXRfbGJsayBkb2VzIG5vdCBsaWUgb24gdGhlIGV4dGVudCBzdGFydCBib3VuZGFyeSwgc3Bs aXQgZXh0ZW50Cj4gPiArICovCj4gPiAraW50IGV4dDRfZmluZF9hbmRfc3BsaXRfZXh0ZW50X2F0 KHN0cnVjdCBpbm9kZSAqaW5vZGUsIGV4dDRfbGJsa190IG9mZnNldF9sYmxrKQo+ID4gK3sKPiA+ ICsJc3RydWN0IGV4dDRfZXh0X3BhdGggKnBhdGg7Cj4gPiArCWhhbmRsZV90ICpoYW5kbGU7Cj4g PiArCWludCBjcmVkaXRzLCBlcnIgPSAwLCBzcGxpdF9mbGFnLCBleF9sZW47Cj4gPiArCXN0cnVj dCBleHQ0X2V4dGVudCAqZXg7Cj4gPiArCWludCBkZXB0aCA9IGV4dF9kZXB0aChpbm9kZSk7Cj4g PiArCWV4dDRfbGJsa190IGV4X3N0YXJ0Owo+ID4gKwo+ID4gKwlwYXRoID0gZXh0NF9leHRfZmlu ZF9leHRlbnQoaW5vZGUsIG9mZnNldF9sYmxrLCBOVUxMLCAwKTsKPiA+ICsJaWYgKElTX0VSUihw YXRoKSkKPiA+ICsJCXJldHVybiBQVFJfRVJSKHBhdGgpOwo+ID4gKwo+ID4gKwlleCA9IHBhdGhb ZGVwdGhdLnBfZXh0Owo+ID4gKwlpZiAoIWV4KQo+ID4gKwkJZ290byBmcmVlX3BhdGg7Cj4gPiAr CWV4X3N0YXJ0ID0gbGUzMl90b19jcHUoZXgtPmVlX2Jsb2NrKTsKPiA+ICsJZXhfbGVuID0gZXh0 NF9leHRfZ2V0X2FjdHVhbF9sZW4oZXgpOwo+ID4gKwo+ID4gKwlpZiAob2Zmc2V0X2xibGsgPiBl eF9zdGFydCAmJiBvZmZzZXRfbGJsayA8IChleF9zdGFydCArIGV4X2xlbikpIHsKPiA+ICsJCWNy ZWRpdHMgPSBleHQ0X3dyaXRlcGFnZV90cmFuc19ibG9ja3MoaW5vZGUpOwo+ID4gKwkJaGFuZGxl ID0gZXh0NF9qb3VybmFsX3N0YXJ0KGlub2RlLCBFWFQ0X0hUX1RSVU5DQVRFLCBjcmVkaXRzKTsK PiA+ICsJCWlmIChJU19FUlIoaGFuZGxlKSkgewo+ID4gKwkJCWVyciA9IFBUUl9FUlIoaGFuZGxl KTsKPiA+ICsJCQlnb3RvIGZyZWVfcGF0aDsKPiA+ICsJCX0KPiA+ICsJCWlmIChleHQ0X2V4dF9p c191bndyaXR0ZW4oZXgpKQo+ID4gKwkJCXNwbGl0X2ZsYWcgPSBFWFQ0X0VYVF9NQVJLX1VOV1JJ VDEgfAo+ID4gKwkJCQkgICAgIEVYVDRfRVhUX01BUktfVU5XUklUMjsKPiA+ICsJCWVsc2UKPiA+ ICsJCQlzcGxpdF9mbGFnID0gMDsKPiA+ICsKPiA+ICsJCWVyciA9IGV4dDRfc3BsaXRfZXh0ZW50 X2F0KGhhbmRsZSwgaW5vZGUsIHBhdGgsIG9mZnNldF9sYmxrLAo+ID4gKwkJCQkJICAgc3BsaXRf ZmxhZywgRVhUNF9FWF9OT0NBQ0hFIHwKPiA+ICsJCQkJCSAgIEVYVDRfR0VUX0JMT0NLU19QUkVf SU8pOwo+ID4gKwkJZXh0NF9qb3VybmFsX3N0b3AoaGFuZGxlKTsKPiA+ICsJfQo+ID4gKwo+ID4g K2ZyZWVfcGF0aDoKPiA+ICsJZXh0NF9leHRfZHJvcF9yZWZzKHBhdGgpOwo+ID4gKwlrZnJlZShw YXRoKTsKPiA+ICsJcmV0dXJuIGVycjsKPiA+ICt9Cj4gPiArCj4gPiArLyoKPiA+ICsgKiBDb21w dXRlIHRoZSBzaXplIG9mIGhvbGUgaW4gdGVybXMgb2YgZmlsZXN5c3RlbSBibG9ja3MgcHJlc2Vu dCBhdCBvZmZzZXRfbGJsawo+ID4gKyAqIHVudGlsIHRoZSBuZXh0IGV4dGVudCBpcyBmb3VuZCBP UiB0aWxsIHdlIHJlYWNoIHRoZSBsYXN0IGJsb2NrIHdpdGhpbiBpc2l6ZS4KPiA+ICsgKiBTdG9y ZSB0aGUgY29tcHV0ZWQgdmFsdWUgaW4gaG9sZV9ibGtjbnQuCj4gPiArICogb2Zmc2V0X2xibGsg c2hvdWxkIGJlIHdpdGhpbiBpc2l6ZSBvZiBpbm9kZS4KPiA+ICsgKi8KPiA+ICtpbnQgZXh0NF9j b21wdXRlX2hvbGVfc2l6ZShzdHJ1Y3QgaW5vZGUgKmlub2RlLCBleHQ0X2xibGtfdCBvZmZzZXRf bGJsaywKPiA+ICsJCQkgICBleHQ0X2xibGtfdCAqaG9sZV9ibGtjbnQpCj4gPiArewo+ID4gKwlz dHJ1Y3QgZXh0NF9leHRfcGF0aCAqcGF0aDsKPiA+ICsJc3RydWN0IGV4dDRfZXh0ZW50ICpleDsK PiA+ICsJZXh0NF9sYmxrX3QgZXhfc3RhcnQsIGlzaXplX2xibGs7Cj4gPiArCWludCByZXQgPSAw LCBkZXB0aCwgZXhfbGVuOwo+ID4gKwo+ID4gKwlpc2l6ZV9sYmxrID0gKGlub2RlLT5pX3NpemUg KyBFWFQ0X0JMT0NLX1NJWkUoaW5vZGUtPmlfc2IpIC0gMSkgPj4KPiA+ICsJCSAgICAgRVhUNF9C TE9DS19TSVpFX0JJVFMoaW5vZGUtPmlfc2IpOwo+ID4gKwo+ID4gKwlpZiAob2Zmc2V0X2xibGsg PiBpc2l6ZV9sYmxrKQo+ID4gKwkJcmV0dXJuIC1FSU5WQUw7Cj4gPiArCj4gPiArCSpob2xlX2Js a2NudCA9IDA7Cj4gPiArCXBhdGggPSBleHQ0X2V4dF9maW5kX2V4dGVudChpbm9kZSwgb2Zmc2V0 X2xibGssIE5VTEwsIDApOwo+ID4gKwlpZiAoSVNfRVJSKHBhdGgpKQo+ID4gKwkJcmV0dXJuIFBU Ul9FUlIocGF0aCk7Cj4gPiArCj4gPiArCWRlcHRoID0gZXh0X2RlcHRoKGlub2RlKTsKPiA+ICsJ ZXggPSBwYXRoW2RlcHRoXS5wX2V4dDsKPiA+ICsJaWYgKCFleCkgewo+ID4gKwkJLyogTm8gYmxv Y2tzIGFsbG9jYXRlZCBpbiB0aGlzIGZpbGUgKi8KPiA+ICsJCSpob2xlX2Jsa2NudCA9IGlzaXpl X2xibGsgLSBvZmZzZXRfbGJsazsKPiA+ICsJCWdvdG8gb3V0Owo+ID4gKwl9Cj4gPiArCWV4X3N0 YXJ0ID0gbGUzMl90b19jcHUoZXgtPmVlX2Jsb2NrKTsKPiA+ICsJZXhfbGVuID0gZXh0NF9leHRf Z2V0X2FjdHVhbF9sZW4oZXgpOwo+ID4gKwo+ID4gKwkvKiBpZiBvZmZzZXRfbGJsayBsaWVzIHdp dGhpbiBleHRlbnQ/ICovCj4gPiArCWlmIChvZmZzZXRfbGJsayA+PSBleF9zdGFydCAmJiBvZmZz ZXRfbGJsayA8IChleF9zdGFydCArIGV4X2xlbikpCj4gPiArCQlnb3RvIG91dDsKPiA+ICsKPiA+ ICsJaWYgKGV4X3N0YXJ0IDwgb2Zmc2V0X2xibGspIHsKPiA+ICsJCXJldCA9IG1leHRfbmV4dF9l eHRlbnQoaW5vZGUsIHBhdGgsICZleCk7Cj4gPiArCQlpZiAoIXJldCkgewo+ID4gKwkJCWV4X3N0 YXJ0ID0gbGUzMl90b19jcHUoZXgtPmVlX2Jsb2NrKTsKPiA+ICsJCX0gZWxzZSB7Cj4gPiArCQkJ aWYgKHJldCA9PSAxKSB7Cj4gPiArCQkJCSpob2xlX2Jsa2NudCA9IGlzaXplX2xibGsgLSBvZmZz ZXRfbGJsazsKPiA+ICsJCQkJcmV0ID0gMDsKPiA+ICsJCQl9Cj4gPiArCQkJZ290byBvdXQ7Cj4g PiArCQl9Cj4gPiArCX0KPiA+ICsJKmhvbGVfYmxrY250ID0gKGV4X3N0YXJ0IDwgaXNpemVfbGJs aykgPyAoZXhfc3RhcnQgLSBvZmZzZXRfbGJsaykgOgo+ID4gKwkJCQkJCSAoaXNpemVfbGJsayAt IG9mZnNldF9sYmxrKTsKPiA+ICtvdXQ6Cj4gPiArCWV4dDRfZXh0X2Ryb3BfcmVmcyhwYXRoKTsK PiA+ICsJa2ZyZWUocGF0aCk7Cj4gPiArCj4gPiArCXJldHVybiByZXQ7Cj4gPiArfQo+ID4gKwo+ ID4gKy8qCj4gPiArICogUmVtb3ZlIGEgY29tcGxldGUgZXh0ZW50IGZyb20gaW4gbWVtb3J5IGFu ZCBvbi1kaXNrIGV4dGVudCB0cmVlCj4gPiArICogd2l0aG91dCBmcmVlaW5nIGFueSBkYXRhIGJs b2NrcyBjb3ZlcmVkIGJ5IHRoZSBleHRlbnQuIENhbGxlciBtdXN0IGNhbGwKPiA+ICsgKiBleHQ0 X21hcmtfaW5vZGVfZGlydHkoKSB0byBzeW5jIHRoZSBjaGFuZ2VzIHRvIGRpc2suCj4gPiArICov Cj4gPiAraW50IGV4dDRfZXh0X3JtX2V4dGVudChoYW5kbGVfdCAqaGFuZGxlLCBzdHJ1Y3QgaW5v ZGUgKmlub2RlLAo+ID4gKwkJICAgICAgIHN0cnVjdCBleHQ0X2V4dF9wYXRoICpwYXRoLCBzdHJ1 Y3QgZXh0NF9leHRlbnQgKmV4KQo+ID4gK3sKPiA+ICsJc3RydWN0IGV4dDRfZXh0ZW50X2hlYWRl ciAqZWg7Cj4gPiArCWludCBkZXB0aCA9IGV4dF9kZXB0aChpbm9kZSk7Cj4gPiArCWludCBjcmVk aXRzLCBlcnIsIGNvcnJlY3RfaW5kZXggPSAwOwo+ID4gKwlpbnQgZXhfZWVfbGVuID0gZXh0NF9l eHRfZ2V0X2FjdHVhbF9sZW4oZXgpOwo+ID4gKwo+ID4gKwlpZiAoIXBhdGhbZGVwdGhdLnBfaGRy KQo+ID4gKwkJcGF0aFtkZXB0aF0ucF9oZHIgPSBleHRfYmxvY2tfaGRyKHBhdGhbZGVwdGhdLnBf YmgpOwo+ID4gKwllaCA9IHBhdGhbZGVwdGhdLnBfaGRyOwo+ID4gKwo+ID4gKwljcmVkaXRzID0g NyArIDIqKGV4X2VlX2xlbi9FWFQ0X0JMT0NLU19QRVJfR1JPVVAoaW5vZGUtPmlfc2IpKTsKPiA+ ICsJaWYgKGV4ID09IEVYVF9GSVJTVF9FWFRFTlQoZWgpKSB7Cj4gPiArCQljb3JyZWN0X2luZGV4 ID0gMTsKPiA+ICsJCWNyZWRpdHMgKz0gKGV4dF9kZXB0aChpbm9kZSkpICsgMTsKPiA+ICsJfQo+ ID4gKwljcmVkaXRzICs9IEVYVDRfTUFYUVVPVEFTX1RSQU5TX0JMT0NLUyhpbm9kZS0+aV9zYik7 Cj4gPiArCWVyciA9IGV4dDRfZXh0X3RydW5jYXRlX2V4dGVuZF9yZXN0YXJ0KGhhbmRsZSwgaW5v ZGUsIGNyZWRpdHMpOwo+ID4gKwlpZiAoZXJyKQo+ID4gKwkJcmV0dXJuIGVycjsKPiA+ICsKPiA+ ICsJZXJyID0gZXh0NF9leHRfZ2V0X2FjY2VzcyhoYW5kbGUsIGlub2RlLCBwYXRoICsgZGVwdGgp Owo+ID4gKwlpZiAoZXJyKQo+ID4gKwkJcmV0dXJuIGVycjsKPiA+ICsKPiA+ICsJZXh0NF9leHRf c3RvcmVfcGJsb2NrKGV4LCAwKTsKPiA+ICsJbWVtbW92ZShleCwgZXgrMSwKPiA+ICsJCShFWFRf TEFTVF9FWFRFTlQoZWgpIC0gZXgpICogc2l6ZW9mKHN0cnVjdCBleHQ0X2V4dGVudCkpOwo+ID4g KwltZW1zZXQoRVhUX0xBU1RfRVhURU5UKGVoKSwgMCwgc2l6ZW9mKHN0cnVjdCBleHQ0X2V4dGVu dCkpOwo+ID4gKwlsZTE2X2FkZF9jcHUoJmVoLT5laF9lbnRyaWVzLCAtMSk7Cj4gPiArCj4gPiAr CWVyciA9IGV4dDRfZXh0X2RpcnR5KGhhbmRsZSwgaW5vZGUsIHBhdGggKyBkZXB0aCk7Cj4gPiAr CWlmIChlcnIpCj4gPiArCQlyZXR1cm4gZXJyOwo+ID4gKwo+ID4gKwlpZiAoY29ycmVjdF9pbmRl eCAmJiBlaC0+ZWhfZW50cmllcykKPiA+ICsJCWVyciA9IGV4dDRfZXh0X2NvcnJlY3RfaW5kZXhl cyhoYW5kbGUsIGlub2RlLCBwYXRoKTsKPiA+ICsKPiA+ICsJaWYgKGVyciA9PSAwICYmIGVoLT5l aF9lbnRyaWVzID09IDAgJiYgcGF0aFtkZXB0aF0ucF9iaCAhPSBOVUxMKQo+ID4gKwkJZXJyID0g ZXh0NF9leHRfcm1faWR4KGhhbmRsZSwgaW5vZGUsIHBhdGgsIGRlcHRoKTsKPiA+ICsKPiA+ICsJ cmV0dXJuIGVycjsKPiA+ICt9Cj4gPiArCj4gPiArLyoKPiA+ICsgKiBNb3ZlIGxlbl9sYmxrIGFt b3VudCBvZiBibG9ja3MgZnJvbSBkb25vciBpbm9kZSB0byByZWNlaXZlciBpbm9kZS4KPiA+ICsg KiBCbG9ja3MgYXJlIHRvIGJlIG1vdmVkIGZyb20gZG9mZnNldF9sYmxrIGFuZCBtb3ZlZCB0byBy b2Zmc2V0X2xibGsuCj4gPiArICogQ2FsbGVyIG9mIHRoaXMgZnVuY3Rpb24gbXVzdCBtYWtlIHN1 cmUgdGhlcmUgaXMgYXRsZWFzdCBsZW5fbGJsayBzaXplCj4gPiArICogaG9sZSBhdCByb2Zmc2V0 X2xibGsuIEFsc28gZG9mZnNldF9sYmxrIGFuZCBkb2Zmc2V0X2xibGsgKyBsZW5fbGJsawo+ID4g KyAqIHNob3VsZCBmYWxsIG9uIGV4dGVudCBib3VuZGFyeS4KPiA+ICsgKi8KPiA+ICtpbnQgZXh0 NF9leHRfbW92X2RhdGEoc3RydWN0IGlub2RlICpkb25vciwgc3RydWN0IGlub2RlICpyZWNlaXZl ciwKPiA+ICsJCSAgICAgIGV4dDRfbGJsa190IGRvZmZzZXRfbGJsaywgZXh0NF9sYmxrX3Qgcm9m ZnNldF9sYmxrLAo+ID4gKwkJICAgICAgZXh0NF9sYmxrX3QgbGVuX2xibGssIGxvZmZfdCAqYnl0 ZXNfbW92ZWQpCj4gPiArewo+ID4gKwlpbnQgZXJyb3IgPSAwLCBkZXB0aCA9IGV4dF9kZXB0aChk b25vcik7Cj4gPiArCXN0cnVjdCBleHQ0X2V4dF9wYXRoICpwYXRoOwo+ID4gKwlzdHJ1Y3QgZXh0 NF9leHRlbnQgKmV4Owo+ID4gKwlsb2ZmX3QgYmxvY2tzX21vdmVkID0gMDsKPiA+ICsJaGFuZGxl X3QgKmhhbmRsZTsKPiA+ICsJaW50IGNyZWRpdHMgPSBleHQ0X3dyaXRlcGFnZV90cmFuc19ibG9j a3MoZG9ub3IpICsKPiA+ICsJCSAgICAgIGV4dDRfd3JpdGVwYWdlX3RyYW5zX2Jsb2NrcyhyZWNl aXZlcik7Cj4gPiArCj4gPiArCXdoaWxlIChibG9ja3NfbW92ZWQgPCBsZW5fbGJsayAmJiAhZXJy b3IpIHsKPiA+ICsJCXN0cnVjdCBleHQ0X2V4dF9wYXRoICpycGF0aCA9IE5VTEw7Cj4gPiArCQll eHQ0X2xibGtfdCBleF9zdGFydDsKPiA+ICsJCWludCBleF9sZW47Cj4gPiArCj4gPiArCQlwYXRo ID0gZXh0NF9leHRfZmluZF9leHRlbnQoZG9ub3IsIGRvZmZzZXRfbGJsaywgTlVMTCwgMCk7Cj4g PiArCQlpZiAoSVNfRVJSKHBhdGgpKSB7Cj4gPiArCQkJZXJyb3IgPSBQVFJfRVJSKHBhdGgpOwo+ ID4gKwkJCWJyZWFrOwo+ID4gKwkJfQo+ID4gKwkJZXggPSBwYXRoW2RlcHRoXS5wX2V4dDsKPiA+ ICsJCS8qCj4gPiArCQkgKiBObyBhbGxvY2F0ZWQgYmxvY2tzPyBUaGlzIGNvdWxkIG9ubHkgaGFw cGVuIGR1cmluZwo+ID4gKwkJICogMXN0IGl0ZXJhdGlvbi4gT3RoZXJ3aXNlIGl0IGlzIGVuIGVy cm9yLgo+ID4gKwkJICovCj4gPiArCQlpZiAoIWV4KSB7Cj4gPiArCQkJaWYgKGJsb2Nrc19tb3Zl ZCkKPiA+ICsJCQkJZXJyb3IgPSAtRUlPOwo+ID4gKwkJCWVsc2UKPiA+ICsJCQkJYmxvY2tzX21v dmVkID0gbGVuX2xibGs7Cj4gPiArCQkJZ290byBvdXQ7Cj4gPiArCQl9Cj4gPiArCQlleF9zdGFy dCA9IGxlMzJfdG9fY3B1KGV4LT5lZV9ibG9jayk7Cj4gPiArCQlleF9sZW4gPSBleHQ0X2V4dF9n ZXRfYWN0dWFsX2xlbihleCk7Cj4gPiArCj4gPiArCQlpZiAoZG9mZnNldF9sYmxrICE9IGV4X3N0 YXJ0KSB7Cj4gPiArCQkJLyogSG9sZSB3aXRoaW4gcmFuZ2UsIG1vdmUgdG8gdGhlIG5leHQgZXh0 ZW50ICovCj4gPiArCQkJaWYgKGV4X3N0YXJ0IDwgZG9mZnNldF9sYmxrKQo+ID4gKwkJCQllcnJv ciA9IG1leHRfbmV4dF9leHRlbnQoZG9ub3IsIHBhdGgsICZleCk7Cj4gPiArCQkJLyogQmVsb3cg aWYgd2lsbCBhbHNvIGhhbmRsZSBleF9zdGFydCA+IGRvZmZzZXRfbGJsayAqLwo+ID4gKwkJCWlm IChlcnJvciA9PSAwKSB7Cj4gPiArCQkJCWV4X3N0YXJ0ID0gbGUzMl90b19jcHUoZXgtPmVlX2Js b2NrKTsKPiA+ICsJCQkJYmxvY2tzX21vdmVkICs9IGV4X3N0YXJ0IC0gZG9mZnNldF9sYmxrOwo+ ID4gKwkJCQlyb2Zmc2V0X2xibGsgKz0gZXhfc3RhcnQgLSBkb2Zmc2V0X2xibGs7Cj4gPiArCQkJ CWRvZmZzZXRfbGJsayA9IGV4X3N0YXJ0Owo+ID4gKwkJCX0KPiA+ICsJCQlpZiAoZXJyb3IgPT0g MSkgewo+ID4gKwkJCQkvKiBkb2Zmc2V0X2xibGsgdGlsbCBFT0YgaXMgaG9sZS4gU3VjY2VzcyEh ICovCj4gPiArCQkJCWJsb2Nrc19tb3ZlZCA9IGxlbl9sYmxrOwo+ID4gKwkJCQllcnJvciA9IDA7 Cj4gPiArCQkJfQo+ID4gKwkJCWdvdG8gb3V0Owo+ID4gKwkJfQo+ID4gKwo+ID4gKwkJLyogQWRk IHRoaXMgZXh0ZW50IHRvIHJlY2VpdmVyICovCj4gPiArCQloYW5kbGUgPSBleHQ0X2pvdXJuYWxf c3RhcnQoZG9ub3IsIEVYVDRfSFRfVFJVTkNBVEUsIGNyZWRpdHMpOwo+ID4gKwkJaWYgKElTX0VS UihoYW5kbGUpKSB7Cj4gPiArCQkJZXJyb3IgPSBQVFJfRVJSKGhhbmRsZSk7Cj4gPiArCQkJZ290 byBvdXQ7Cj4gPiArCQl9Cj4gPiArCj4gPiArCQlycGF0aCA9IGV4dDRfZXh0X2ZpbmRfZXh0ZW50 KHJlY2VpdmVyLCByb2Zmc2V0X2xibGssIE5VTEwsIDApOwo+ID4gKwkJaWYgKElTX0VSUihycGF0 aCkpIHsKPiA+ICsJCQllcnJvciA9IFBUUl9FUlIocnBhdGgpOwo+ID4gKwkJCWV4dDRfam91cm5h bF9zdG9wKGhhbmRsZSk7Cj4gPiArCQkJZ290byBvdXQ7Cj4gPiArCQl9Cj4gPiArCQlleC0+ZWVf YmxvY2sgPSBjcHVfdG9fbGUzMihyb2Zmc2V0X2xibGspOwo+ID4gKwkJZXJyb3IgPSBleHQ0X2V4 dF9pbnNlcnRfZXh0ZW50KGhhbmRsZSwgcmVjZWl2ZXIsIHJwYXRoLCBleCwgMCk7Cj4gPiArCQlp ZiAoZXJyb3IpCj4gPiArCQkJZ290byBob3V0Owo+ID4gKwo+ID4gKwkJLyogUmVtb3ZlIHRoaXMg ZXh0ZW50IGZyb20gZG9ub3IgKi8KPiA+ICsJCWVycm9yID0gZXh0NF9leHRfcm1fZXh0ZW50KGhh bmRsZSwgZG9ub3IsIHBhdGgsIGV4KTsKPiA+ICsJCWlmIChlcnJvcikKPiA+ICsJCQlnb3RvIGhv dXQ7Cj4gPiArCj4gPiArCQkvKiBFeHRlbnQgbW92ZWQgc3VjY2Vzc2Z1bGx5ICovCj4gPiArCQly b2Zmc2V0X2xibGsgKz0gZXhfbGVuOwo+ID4gKwkJZG9mZnNldF9sYmxrICs9IGV4X2xlbjsKPiA+ ICsJCWJsb2Nrc19tb3ZlZCArPSBleF9sZW47Cj4gPiArCj4gPiArCQlkb25vci0+aV9ibG9ja3Mg LT0gKGV4X2xlbiA8PCAoZG9ub3ItPmlfYmxrYml0cyAtIDkpKTsKPiA+ICsJCXJlY2VpdmVyLT5p X2Jsb2NrcyArPSAoZXhfbGVuIDw8IChyZWNlaXZlci0+aV9ibGtiaXRzIC0gOSkpOwo+ID4gKwkJ ZG9ub3ItPmlfbXRpbWUgPSBkb25vci0+aV9jdGltZSA9IGV4dDRfY3VycmVudF90aW1lKGRvbm9y KTsKPiA+ICsJCXJlY2VpdmVyLT5pX210aW1lID0gcmVjZWl2ZXItPmlfY3RpbWUgPQo+ID4gKwkJ CQkJCWV4dDRfY3VycmVudF90aW1lKHJlY2VpdmVyKTsKPiA+ICsJCWV4dDRfbWFya19pbm9kZV9k aXJ0eShoYW5kbGUsIGRvbm9yKTsKPiA+ICsJCWV4dDRfbWFya19pbm9kZV9kaXJ0eShoYW5kbGUs IHJlY2VpdmVyKTsKPiA+ICtob3V0Ogo+ID4gKwkJZXh0NF9qb3VybmFsX3N0b3AoaGFuZGxlKTsK PiA+ICsJCWV4dDRfZXh0X2Ryb3BfcmVmcyhycGF0aCk7Cj4gPiArCQlrZnJlZShycGF0aCk7Cj4g PiArb3V0Ogo+ID4gKwkJZXh0NF9leHRfZHJvcF9yZWZzKHBhdGgpOwo+ID4gKwkJa2ZyZWUocGF0 aCk7Cj4gPiArCX0KPiA+ICsKPiA+ICsJLyogVGhpcyBjYW4gaGFwcGVuIHdoZW4gKGRvZmZzZXRf bGJsayArIGxlbl9sYmxrKSBpcyBpbiBhIGhvbGUgKi8KPiA+ICsJaWYgKGJsb2Nrc19tb3ZlZCA+ IGxlbl9sYmxrKQo+ID4gKwkJYmxvY2tzX21vdmVkID0gbGVuX2xibGs7Cj4gPiArCj4gPiArCSpi eXRlc19tb3ZlZCA9IGJsb2Nrc19tb3ZlZCA8PCBFWFQ0X0JMT0NLX1NJWkVfQklUUyhkb25vci0+ aV9zYik7Cj4gPiArCXJldHVybiBlcnJvcjsKPiA+ICt9Cj4gPiArCj4gPiAraW50IGV4dDRfbW92 X2RhdGEoc3RydWN0IGlub2RlICpkb25vciwgc3RydWN0IGlub2RlICpyZWNlaXZlciwgbG9mZl90 IGRvZmZzZXQsCj4gPiArCQkgIGxvZmZfdCByb2Zmc2V0LCBsb2ZmX3QgbGVuLCBsb2ZmX3QgKm1v dmVkX2xlbikKPiA+ICt7Cj4gPiArCXN0cnVjdCBzdXBlcl9ibG9jayAqc2IgPSBkb25vci0+aV9z YjsKPiA+ICsJbG9mZl90IGRfcGdfb2ZmLCByX3BnX29mZiwgcGdfbGVuOwo+ID4gKwlleHQ0X2xi bGtfdCBkb2Zmc2V0X2xibGssIHJvZmZzZXRfbGJsaywgbGVuX2xibGssIGhvbGVfc2l6ZTsKPiA+ ICsJaW50IGVycm9yOwo+ID4gKwo+ID4gKwlpZiAoZG9mZnNldCAmIChFWFQ0X0JMT0NLX1NJWkUo c2IpIC0gMSkgfHwKPiA+ICsJICAgIHJvZmZzZXQgJiAoRVhUNF9CTE9DS19TSVpFKHNiKSAtIDEp IHx8Cj4gPiArCSAgICBsZW4gJiAoRVhUNF9CTE9DS19TSVpFKHNiKSAtIDEpKQo+ID4gKwkJcmV0 dXJuIC1FSU5WQUw7Cj4gPiArCj4gPiArCWlmIChFWFQ0X1NCKHNiKS0+c19jbHVzdGVyX3JhdGlv ID4gMSkKPiA+ICsJCXJldHVybiAtRU9QTk9UU1VQUDsKPiA+ICsKPiA+ICsJaWYgKCFleHQ0X3Rl c3RfaW5vZGVfZmxhZyhkb25vciwgRVhUNF9JTk9ERV9FWFRFTlRTKSB8fAo+ID4gKwkgICAgIWV4 dDRfdGVzdF9pbm9kZV9mbGFnKHJlY2VpdmVyLCBFWFQ0X0lOT0RFX0VYVEVOVFMpKQo+ID4gKwkJ cmV0dXJuIC1FT1BOT1RTVVBQOwo+ID4gKwo+ID4gKwlkb2Zmc2V0X2xibGsgPSBkb2Zmc2V0ID4+ IEVYVDRfQkxPQ0tfU0laRV9CSVRTKHNiKTsKPiA+ICsJcm9mZnNldF9sYmxrID0gcm9mZnNldCA+ PiBFWFQ0X0JMT0NLX1NJWkVfQklUUyhzYik7Cj4gPiArCWxlbl9sYmxrID0gbGVuID4+IEVYVDRf QkxPQ0tfU0laRV9CSVRTKHNiKTsKPiA+ICsKPiA+ICsJZF9wZ19vZmYgPSByb3VuZF9kb3duKGRv ZmZzZXQsIFBBR0VfU0laRSk7Cj4gPiArCXJfcGdfb2ZmID0gcm91bmRfZG93bihyb2Zmc2V0LCBQ QUdFX1NJWkUpOwo+ID4gKwlwZ19sZW4gPSByb3VuZF91cChsZW4sIFBBR0VfU0laRSk7Cj4gPiAr Cj4gPiArCWlmIChleHQ0X3Nob3VsZF9qb3VybmFsX2RhdGEoZG9ub3IpKSB7Cj4gPiArCQllcnJv ciA9IGV4dDRfZm9yY2VfY29tbWl0KGRvbm9yLT5pX3NiKTsKPiA+ICsJCWlmIChlcnJvcikKPiA+ ICsJCQlyZXR1cm4gZXJyb3I7Cj4gPiArCQllcnJvciA9IGV4dDRfZm9yY2VfY29tbWl0KHJlY2Vp dmVyLT5pX3NiKTsKPiA+ICsJCWlmIChlcnJvcikKPiA+ICsJCQlyZXR1cm4gZXJyb3I7Cj4gPiAr CX0KPiA+ICsKPiA+ICsJZXJyb3IgPSBmaWxlbWFwX3dyaXRlX2FuZF93YWl0X3JhbmdlKGRvbm9y LT5pX21hcHBpbmcsCj4gPiArCQkJCQkgICAgIGRfcGdfb2ZmLCBkX3BnX29mZiArIHBnX2xlbik7 Cj4gPiArCWlmIChlcnJvcikKPiA+ICsJCXJldHVybiBlcnJvcjsKPiA+ICsJZXJyb3IgPSBmaWxl bWFwX3dyaXRlX2FuZF93YWl0X3JhbmdlKHJlY2VpdmVyLT5pX21hcHBpbmcsCj4gPiArCQkJCQkg ICAgIHJfcGdfb2ZmLCByX3BnX29mZiArIHBnX2xlbik7Cj4gPiArCWlmIChlcnJvcikKPiA+ICsJ CXJldHVybiBlcnJvcjsKPiA+ICsKPiA+ICsJbG9ja190d29fbm9uZGlyZWN0b3JpZXMoZG9ub3Is IHJlY2VpdmVyKTsKPiA+ICsKPiA+ICsJLyogQ2hlY2sgZm9yIGlzaXplIGxpbWl0cyBmb3IgYm90 aCBmaWxlcyAqLwo+ID4gKwlpZiAoZG9mZnNldCArIGxlbiA+IGRvbm9yLT5pX3NpemUgfHwKPiA+ ICsJICAgIHJvZmZzZXQgKyBsZW4gPiByZWNlaXZlci0+aV9zaXplKSB7Cj4gPiArCQllcnJvciA9 IC1FSU5WQUw7Cj4gPiArCQlnb3RvIG91dF9tdXRleDsKPiA+ICsJfQo+ID4gKwo+ID4gKwl0cnVu Y2F0ZV9wYWdlY2FjaGVfcmFuZ2UoZG9ub3IsIGRfcGdfb2ZmLCBkX3BnX29mZiArIHBnX2xlbiAt IDEpOwo+ID4gKwl0cnVuY2F0ZV9wYWdlY2FjaGVfcmFuZ2UocmVjZWl2ZXIsIHJfcGdfb2ZmLCBy X3BnX29mZiArIHBnX2xlbiAtIDEpOwo+ID4gKwo+ID4gKwlleHQ0X2lub2RlX2Jsb2NrX3VubG9j a2VkX2Rpbyhkb25vcik7Cj4gPiArCWlub2RlX2Rpb193YWl0KGRvbm9yKTsKPiA+ICsJZXh0NF9p bm9kZV9ibG9ja191bmxvY2tlZF9kaW8ocmVjZWl2ZXIpOwo+ID4gKwlpbm9kZV9kaW9fd2FpdChy ZWNlaXZlcik7Cj4gPiArCj4gPiArCWV4dDRfZGlzY2FyZF9wcmVhbGxvY2F0aW9ucyhkb25vcik7 Cj4gPiArCWV4dDRfZGlzY2FyZF9wcmVhbGxvY2F0aW9ucyhyZWNlaXZlcik7Cj4gPiArCj4gPiAr CWVycm9yID0gZXh0NF9lc19yZW1vdmVfZXh0ZW50KGRvbm9yLCBkb2Zmc2V0X2xibGssIGxlbl9s YmxrKTsKPiA+ICsJaWYgKGVycm9yKQo+ID4gKwkJZ290byBvdXRfc2VtOwo+ID4gKwllcnJvciA9 IGV4dDRfZXNfcmVtb3ZlX2V4dGVudChyZWNlaXZlciwgcm9mZnNldF9sYmxrLCBsZW5fbGJsayk7 Cj4gPiArCWlmIChlcnJvcikKPiA+ICsJCWdvdG8gb3V0X3NlbTsKPiA+ICsKPiA+ICsJZXJyb3Ig PSBleHQ0X2NvbXB1dGVfaG9sZV9zaXplKHJlY2VpdmVyLCByb2Zmc2V0X2xibGssICZob2xlX3Np emUpOwo+ID4gKwlpZiAoZXJyb3IpCj4gPiArCQlnb3RvIG91dF9zZW07Cj4gPiArCWlmIChsZW5f bGJsayA+IGhvbGVfc2l6ZSkgewo+ID4gKwkJZXJyb3IgPSAtRUlOVkFMOwo+ID4gKwkJZ290byBv dXRfc2VtOwo+ID4gKwl9Cj4gPiArCj4gPiArCWVycm9yID0gZXh0NF9maW5kX2FuZF9zcGxpdF9l eHRlbnRfYXQoZG9ub3IsIGRvZmZzZXRfbGJsayk7Cj4gPiArCWlmIChlcnJvcikKPiA+ICsJCWdv dG8gb3V0X3NlbTsKPiA+ICsKPiA+ICsJZXJyb3IgPSBleHQ0X2ZpbmRfYW5kX3NwbGl0X2V4dGVu dF9hdChkb25vciwgZG9mZnNldF9sYmxrICsgbGVuX2xibGspOwo+ID4gKwlpZiAoZXJyb3IpCj4g PiArCQlnb3RvIG91dF9zZW07Cj4gPiArCj4gPiArCWVycm9yID0gZXh0NF9leHRfbW92X2RhdGEo ZG9ub3IsIHJlY2VpdmVyLCBkb2Zmc2V0X2xibGssCj4gPiArCQkJCSAgcm9mZnNldF9sYmxrLCBs ZW5fbGJsaywgbW92ZWRfbGVuKTsKPiA+ICsKPiA+ICsJZXh0NF9kaXNjYXJkX3ByZWFsbG9jYXRp b25zKGRvbm9yKTsKPiA+ICsJZXh0NF9kaXNjYXJkX3ByZWFsbG9jYXRpb25zKHJlY2VpdmVyKTsK PiA+ICtvdXRfc2VtOgo+ID4gKwlleHQ0X2lub2RlX3Jlc3VtZV91bmxvY2tlZF9kaW8oZG9ub3Ip Owo+ID4gKwlleHQ0X2lub2RlX3Jlc3VtZV91bmxvY2tlZF9kaW8ocmVjZWl2ZXIpOwo+ID4gKwo+ ID4gK291dF9tdXRleDoKPiA+ICsJdW5sb2NrX3R3b19ub25kaXJlY3Rvcmllcyhkb25vciwgcmVj ZWl2ZXIpOwo+ID4gKwlyZXR1cm4gZXJyb3I7Cj4gPiArfQo+ID4gZGlmZiAtLWdpdCBhL2ZzL2V4 dDQvZmlsZS5jIGIvZnMvZXh0NC9maWxlLmMKPiA+IGluZGV4IDg2OTVmNzAuLmQyZmVhYmEgMTAw NjQ0Cj4gPiAtLS0gYS9mcy9leHQ0L2ZpbGUuYwo+ID4gKysrIGIvZnMvZXh0NC9maWxlLmMKPiA+ IEBAIC02MTQsNSArNjE0LDYgQEAgY29uc3Qgc3RydWN0IGlub2RlX29wZXJhdGlvbnMgZXh0NF9m aWxlX2lub2RlX29wZXJhdGlvbnMgPSB7Cj4gPiAgCS5nZXRfYWNsCT0gZXh0NF9nZXRfYWNsLAo+ ID4gIAkuc2V0X2FjbAk9IGV4dDRfc2V0X2FjbCwKPiA+ICAJLmZpZW1hcAkJPSBleHQ0X2ZpZW1h cCwKPiA+ICsJLm1vdl9kYXRhCT0gZXh0NF9tb3ZfZGF0YSwKPiA+ICB9Owo+ID4gIAo+ID4gCgpf X19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fXwp4ZnMgbWFpbGlu ZyBsaXN0Cnhmc0Bvc3Muc2dpLmNvbQpodHRwOi8vb3NzLnNnaS5jb20vbWFpbG1hbi9saXN0aW5m by94ZnMK From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1753596AbaGNJNI (ORCPT ); Mon, 14 Jul 2014 05:13:08 -0400 Received: from mail-la0-f46.google.com ([209.85.215.46]:60338 "EHLO mail-la0-f46.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1751204AbaGNJM6 convert rfc822-to-8bit (ORCPT ); Mon, 14 Jul 2014 05:12:58 -0400 From: Dmitry Monakhov To: =?utf-8?B?THVrw6HFoQ==?= Czerner , Namjae Jeon Cc: Dave Chinner , "Theodore Ts'o" , linux-ext4 , linux-fsdevel@vger.kernel.org, linux-kernel@vger.kernel.org, Brian Foster , Christoph Hellwig , Ashish Sangwan , xfs@oss.sgi.com Subject: Re: [PATCH 3/3] ext4: Add support IOC_MOV_DATA ioctl In-Reply-To: References: <004001cf9aa4$2670e280$7352a780$@samsung.com> User-Agent: Notmuch/0.6.1 (http://notmuchmail.org) Emacs/23.3.1 (x86_64-redhat-linux-gnu) Date: Mon, 14 Jul 2014 13:12:53 +0400 Message-ID: <87tx6ktiay.fsf@openvz.org> MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 8BIT Sender: linux-kernel-owner@vger.kernel.org List-ID: X-Mailing-List: linux-kernel@vger.kernel.org On Tue, 8 Jul 2014 16:02:28 +0200 (CEST), Lukáš Czerner wrote: Non-text part: MULTIPART/MIXED > On Tue, 8 Jul 2014, Namjae Jeon wrote: > > > Date: Tue, 08 Jul 2014 21:00:02 +0900 > > From: Namjae Jeon > > To: Dave Chinner , Theodore Ts'o > > Cc: linux-ext4 , linux-fsdevel@vger.kernel.org, > > linux-kernel@vger.kernel.org, Lukáš Czerner , > > Brian Foster , Christoph Hellwig , > > Ashish Sangwan , xfs@oss.sgi.com > > Subject: [PATCH 3/3] ext4: Add support IOC_MOV_DATA ioctl > > > > This patch implements fs ioctl's IOC_MOV_DATA for Ext4. > > Hmm isn't this basically what ext4_move_extents() does ? eg. > EXT4_IOC_MOVE_EXT ? > > I guess that the intention here is to do the move, without actually > moving the data right ? But nevertheless maybe some code can be > shared with ext4_move_extents() ? It definitely can be shared, because it has specific case for unwritten data see move_extent_per_page(). But I think we can observe another way to unify this two things. An idea inspired by the fact that ioc_move_data works only for regular inodes, where orig_offset == donor_offset. This is showstopper for my utility e4defrag2 ( new version of e4defrag which is able defragment pack small files as described here : http://lists.openwall.net/linux-ext4/2014/04/28/3) Proposed API is very similar to ext4_ext_migrate: Args: orig_file: inode which we want to defragment donor_file: a file which will be used as a donor of blocks 1) fallocate big donor_file 2) a) Create tmp inode wich nlink = 0 b) move extents required extents from donor to tmp_donor_inode c) return file descriptor (tmp_fd) to that tmp_donor_inode 4) Mark orig_file's inode with EXT4_STATE_EXT_MIGRATE state 5) Copy data from orig_file to tmp_fd 6) IOC_SWAP_EX: atomically swap orig_file->i_data and tmp_fd->i_data if EXT4_STATE_EXT_MIGRATE was not cleared. This approach can works not only for regular file w/o journaling enabled, but also for journaled ones, and directories. > > -Lukas > > > > > The semantics of this ioctl are: > > 1) Like collapse range, offsets and length should be file system block size > > aligned. > > 2) In the receiver file, atleast length size hole should be present at > > receiver_offset > > 3) It does not change file size of any of donor or receiver file. > > 4) It leaves a hole at the place from where blocks are moved out in donor file. > > 5) Both (donor_offset + length) and (receiver_offset + length) should be within > > size of donor file and receiver file respectively. > > Only unwritten extents resides beyond file size and it does not make sense > > to transfer unwritten extents, leave apart the security issues it may raise. > > 6) If the range to be transfered from donor file contain any holes, they are > > replicated as it is in receiver file. It mean holes are preserved and > > the length of hole will be added to moved_len signifying that the hole range > > is succesfully transfered. > > > > Signed-off-by: Namjae Jeon > > Signed-off-by: Ashish Sangwan > > --- > > fs/ext4/ext4.h | 2 + > > fs/ext4/extents.c | 375 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ > > fs/ext4/file.c | 1 + > > 3 files changed, 378 insertions(+) > > > > diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h > > index 6386c5f..26478eb 100644 > > --- a/fs/ext4/ext4.h > > +++ b/fs/ext4/ext4.h > > @@ -2725,6 +2725,8 @@ extern int ext4_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo, > > extern int ext4_ext_precache(struct inode *inode); > > extern int ext4_collapse_range(struct inode *inode, loff_t offset, loff_t len); > > extern int ext4_insert_range(struct file *file, loff_t offset, loff_t len); > > +extern int ext4_mov_data(struct inode *, struct inode *, loff_t, loff_t, loff_t, > > + loff_t *); > > > > /* move_extent.c */ > > extern void ext4_double_down_write_data_sem(struct inode *first, > > diff --git a/fs/ext4/extents.c b/fs/ext4/extents.c > > index 0c2432e..511db03 100644 > > --- a/fs/ext4/extents.c > > +++ b/fs/ext4/extents.c > > @@ -5811,3 +5811,378 @@ out_mutex: > > mutex_unlock(&inode->i_mutex); > > return ret; > > } > > + > > +/* > > + * If offset_lblk does not lie on the extent start boundary, split extent > > + */ > > +int ext4_find_and_split_extent_at(struct inode *inode, ext4_lblk_t offset_lblk) > > +{ > > + struct ext4_ext_path *path; > > + handle_t *handle; > > + int credits, err = 0, split_flag, ex_len; > > + struct ext4_extent *ex; > > + int depth = ext_depth(inode); > > + ext4_lblk_t ex_start; > > + > > + path = ext4_ext_find_extent(inode, offset_lblk, NULL, 0); > > + if (IS_ERR(path)) > > + return PTR_ERR(path); > > + > > + ex = path[depth].p_ext; > > + if (!ex) > > + goto free_path; > > + ex_start = le32_to_cpu(ex->ee_block); > > + ex_len = ext4_ext_get_actual_len(ex); > > + > > + if (offset_lblk > ex_start && offset_lblk < (ex_start + ex_len)) { > > + credits = ext4_writepage_trans_blocks(inode); > > + handle = ext4_journal_start(inode, EXT4_HT_TRUNCATE, credits); > > + if (IS_ERR(handle)) { > > + err = PTR_ERR(handle); > > + goto free_path; > > + } > > + if (ext4_ext_is_unwritten(ex)) > > + split_flag = EXT4_EXT_MARK_UNWRIT1 | > > + EXT4_EXT_MARK_UNWRIT2; > > + else > > + split_flag = 0; > > + > > + err = ext4_split_extent_at(handle, inode, path, offset_lblk, > > + split_flag, EXT4_EX_NOCACHE | > > + EXT4_GET_BLOCKS_PRE_IO); > > + ext4_journal_stop(handle); > > + } > > + > > +free_path: > > + ext4_ext_drop_refs(path); > > + kfree(path); > > + return err; > > +} > > + > > +/* > > + * Compute the size of hole in terms of filesystem blocks present at offset_lblk > > + * until the next extent is found OR till we reach the last block within isize. > > + * Store the computed value in hole_blkcnt. > > + * offset_lblk should be within isize of inode. > > + */ > > +int ext4_compute_hole_size(struct inode *inode, ext4_lblk_t offset_lblk, > > + ext4_lblk_t *hole_blkcnt) > > +{ > > + struct ext4_ext_path *path; > > + struct ext4_extent *ex; > > + ext4_lblk_t ex_start, isize_lblk; > > + int ret = 0, depth, ex_len; > > + > > + isize_lblk = (inode->i_size + EXT4_BLOCK_SIZE(inode->i_sb) - 1) >> > > + EXT4_BLOCK_SIZE_BITS(inode->i_sb); > > + > > + if (offset_lblk > isize_lblk) > > + return -EINVAL; > > + > > + *hole_blkcnt = 0; > > + path = ext4_ext_find_extent(inode, offset_lblk, NULL, 0); > > + if (IS_ERR(path)) > > + return PTR_ERR(path); > > + > > + depth = ext_depth(inode); > > + ex = path[depth].p_ext; > > + if (!ex) { > > + /* No blocks allocated in this file */ > > + *hole_blkcnt = isize_lblk - offset_lblk; > > + goto out; > > + } > > + ex_start = le32_to_cpu(ex->ee_block); > > + ex_len = ext4_ext_get_actual_len(ex); > > + > > + /* if offset_lblk lies within extent? */ > > + if (offset_lblk >= ex_start && offset_lblk < (ex_start + ex_len)) > > + goto out; > > + > > + if (ex_start < offset_lblk) { > > + ret = mext_next_extent(inode, path, &ex); > > + if (!ret) { > > + ex_start = le32_to_cpu(ex->ee_block); > > + } else { > > + if (ret == 1) { > > + *hole_blkcnt = isize_lblk - offset_lblk; > > + ret = 0; > > + } > > + goto out; > > + } > > + } > > + *hole_blkcnt = (ex_start < isize_lblk) ? (ex_start - offset_lblk) : > > + (isize_lblk - offset_lblk); > > +out: > > + ext4_ext_drop_refs(path); > > + kfree(path); > > + > > + return ret; > > +} > > + > > +/* > > + * Remove a complete extent from in memory and on-disk extent tree > > + * without freeing any data blocks covered by the extent. Caller must call > > + * ext4_mark_inode_dirty() to sync the changes to disk. > > + */ > > +int ext4_ext_rm_extent(handle_t *handle, struct inode *inode, > > + struct ext4_ext_path *path, struct ext4_extent *ex) > > +{ > > + struct ext4_extent_header *eh; > > + int depth = ext_depth(inode); > > + int credits, err, correct_index = 0; > > + int ex_ee_len = ext4_ext_get_actual_len(ex); > > + > > + if (!path[depth].p_hdr) > > + path[depth].p_hdr = ext_block_hdr(path[depth].p_bh); > > + eh = path[depth].p_hdr; > > + > > + credits = 7 + 2*(ex_ee_len/EXT4_BLOCKS_PER_GROUP(inode->i_sb)); > > + if (ex == EXT_FIRST_EXTENT(eh)) { > > + correct_index = 1; > > + credits += (ext_depth(inode)) + 1; > > + } > > + credits += EXT4_MAXQUOTAS_TRANS_BLOCKS(inode->i_sb); > > + err = ext4_ext_truncate_extend_restart(handle, inode, credits); > > + if (err) > > + return err; > > + > > + err = ext4_ext_get_access(handle, inode, path + depth); > > + if (err) > > + return err; > > + > > + ext4_ext_store_pblock(ex, 0); > > + memmove(ex, ex+1, > > + (EXT_LAST_EXTENT(eh) - ex) * sizeof(struct ext4_extent)); > > + memset(EXT_LAST_EXTENT(eh), 0, sizeof(struct ext4_extent)); > > + le16_add_cpu(&eh->eh_entries, -1); > > + > > + err = ext4_ext_dirty(handle, inode, path + depth); > > + if (err) > > + return err; > > + > > + if (correct_index && eh->eh_entries) > > + err = ext4_ext_correct_indexes(handle, inode, path); > > + > > + if (err == 0 && eh->eh_entries == 0 && path[depth].p_bh != NULL) > > + err = ext4_ext_rm_idx(handle, inode, path, depth); > > + > > + return err; > > +} > > + > > +/* > > + * Move len_lblk amount of blocks from donor inode to receiver inode. > > + * Blocks are to be moved from doffset_lblk and moved to roffset_lblk. > > + * Caller of this function must make sure there is atleast len_lblk size > > + * hole at roffset_lblk. Also doffset_lblk and doffset_lblk + len_lblk > > + * should fall on extent boundary. > > + */ > > +int ext4_ext_mov_data(struct inode *donor, struct inode *receiver, > > + ext4_lblk_t doffset_lblk, ext4_lblk_t roffset_lblk, > > + ext4_lblk_t len_lblk, loff_t *bytes_moved) > > +{ > > + int error = 0, depth = ext_depth(donor); > > + struct ext4_ext_path *path; > > + struct ext4_extent *ex; > > + loff_t blocks_moved = 0; > > + handle_t *handle; > > + int credits = ext4_writepage_trans_blocks(donor) + > > + ext4_writepage_trans_blocks(receiver); > > + > > + while (blocks_moved < len_lblk && !error) { > > + struct ext4_ext_path *rpath = NULL; > > + ext4_lblk_t ex_start; > > + int ex_len; > > + > > + path = ext4_ext_find_extent(donor, doffset_lblk, NULL, 0); > > + if (IS_ERR(path)) { > > + error = PTR_ERR(path); > > + break; > > + } > > + ex = path[depth].p_ext; > > + /* > > + * No allocated blocks? This could only happen during > > + * 1st iteration. Otherwise it is en error. > > + */ > > + if (!ex) { > > + if (blocks_moved) > > + error = -EIO; > > + else > > + blocks_moved = len_lblk; > > + goto out; > > + } > > + ex_start = le32_to_cpu(ex->ee_block); > > + ex_len = ext4_ext_get_actual_len(ex); > > + > > + if (doffset_lblk != ex_start) { > > + /* Hole within range, move to the next extent */ > > + if (ex_start < doffset_lblk) > > + error = mext_next_extent(donor, path, &ex); > > + /* Below if will also handle ex_start > doffset_lblk */ > > + if (error == 0) { > > + ex_start = le32_to_cpu(ex->ee_block); > > + blocks_moved += ex_start - doffset_lblk; > > + roffset_lblk += ex_start - doffset_lblk; > > + doffset_lblk = ex_start; > > + } > > + if (error == 1) { > > + /* doffset_lblk till EOF is hole. Success!! */ > > + blocks_moved = len_lblk; > > + error = 0; > > + } > > + goto out; > > + } > > + > > + /* Add this extent to receiver */ > > + handle = ext4_journal_start(donor, EXT4_HT_TRUNCATE, credits); > > + if (IS_ERR(handle)) { > > + error = PTR_ERR(handle); > > + goto out; > > + } > > + > > + rpath = ext4_ext_find_extent(receiver, roffset_lblk, NULL, 0); > > + if (IS_ERR(rpath)) { > > + error = PTR_ERR(rpath); > > + ext4_journal_stop(handle); > > + goto out; > > + } > > + ex->ee_block = cpu_to_le32(roffset_lblk); > > + error = ext4_ext_insert_extent(handle, receiver, rpath, ex, 0); > > + if (error) > > + goto hout; > > + > > + /* Remove this extent from donor */ > > + error = ext4_ext_rm_extent(handle, donor, path, ex); > > + if (error) > > + goto hout; > > + > > + /* Extent moved successfully */ > > + roffset_lblk += ex_len; > > + doffset_lblk += ex_len; > > + blocks_moved += ex_len; > > + > > + donor->i_blocks -= (ex_len << (donor->i_blkbits - 9)); > > + receiver->i_blocks += (ex_len << (receiver->i_blkbits - 9)); > > + donor->i_mtime = donor->i_ctime = ext4_current_time(donor); > > + receiver->i_mtime = receiver->i_ctime = > > + ext4_current_time(receiver); > > + ext4_mark_inode_dirty(handle, donor); > > + ext4_mark_inode_dirty(handle, receiver); > > +hout: > > + ext4_journal_stop(handle); > > + ext4_ext_drop_refs(rpath); > > + kfree(rpath); > > +out: > > + ext4_ext_drop_refs(path); > > + kfree(path); > > + } > > + > > + /* This can happen when (doffset_lblk + len_lblk) is in a hole */ > > + if (blocks_moved > len_lblk) > > + blocks_moved = len_lblk; > > + > > + *bytes_moved = blocks_moved << EXT4_BLOCK_SIZE_BITS(donor->i_sb); > > + return error; > > +} > > + > > +int ext4_mov_data(struct inode *donor, struct inode *receiver, loff_t doffset, > > + loff_t roffset, loff_t len, loff_t *moved_len) > > +{ > > + struct super_block *sb = donor->i_sb; > > + loff_t d_pg_off, r_pg_off, pg_len; > > + ext4_lblk_t doffset_lblk, roffset_lblk, len_lblk, hole_size; > > + int error; > > + > > + if (doffset & (EXT4_BLOCK_SIZE(sb) - 1) || > > + roffset & (EXT4_BLOCK_SIZE(sb) - 1) || > > + len & (EXT4_BLOCK_SIZE(sb) - 1)) > > + return -EINVAL; > > + > > + if (EXT4_SB(sb)->s_cluster_ratio > 1) > > + return -EOPNOTSUPP; > > + > > + if (!ext4_test_inode_flag(donor, EXT4_INODE_EXTENTS) || > > + !ext4_test_inode_flag(receiver, EXT4_INODE_EXTENTS)) > > + return -EOPNOTSUPP; > > + > > + doffset_lblk = doffset >> EXT4_BLOCK_SIZE_BITS(sb); > > + roffset_lblk = roffset >> EXT4_BLOCK_SIZE_BITS(sb); > > + len_lblk = len >> EXT4_BLOCK_SIZE_BITS(sb); > > + > > + d_pg_off = round_down(doffset, PAGE_SIZE); > > + r_pg_off = round_down(roffset, PAGE_SIZE); > > + pg_len = round_up(len, PAGE_SIZE); > > + > > + if (ext4_should_journal_data(donor)) { > > + error = ext4_force_commit(donor->i_sb); > > + if (error) > > + return error; > > + error = ext4_force_commit(receiver->i_sb); > > + if (error) > > + return error; > > + } > > + > > + error = filemap_write_and_wait_range(donor->i_mapping, > > + d_pg_off, d_pg_off + pg_len); > > + if (error) > > + return error; > > + error = filemap_write_and_wait_range(receiver->i_mapping, > > + r_pg_off, r_pg_off + pg_len); > > + if (error) > > + return error; > > + > > + lock_two_nondirectories(donor, receiver); > > + > > + /* Check for isize limits for both files */ > > + if (doffset + len > donor->i_size || > > + roffset + len > receiver->i_size) { > > + error = -EINVAL; > > + goto out_mutex; > > + } > > + > > + truncate_pagecache_range(donor, d_pg_off, d_pg_off + pg_len - 1); > > + truncate_pagecache_range(receiver, r_pg_off, r_pg_off + pg_len - 1); > > + > > + ext4_inode_block_unlocked_dio(donor); > > + inode_dio_wait(donor); > > + ext4_inode_block_unlocked_dio(receiver); > > + inode_dio_wait(receiver); > > + > > + ext4_discard_preallocations(donor); > > + ext4_discard_preallocations(receiver); > > + > > + error = ext4_es_remove_extent(donor, doffset_lblk, len_lblk); > > + if (error) > > + goto out_sem; > > + error = ext4_es_remove_extent(receiver, roffset_lblk, len_lblk); > > + if (error) > > + goto out_sem; > > + > > + error = ext4_compute_hole_size(receiver, roffset_lblk, &hole_size); > > + if (error) > > + goto out_sem; > > + if (len_lblk > hole_size) { > > + error = -EINVAL; > > + goto out_sem; > > + } > > + > > + error = ext4_find_and_split_extent_at(donor, doffset_lblk); > > + if (error) > > + goto out_sem; > > + > > + error = ext4_find_and_split_extent_at(donor, doffset_lblk + len_lblk); > > + if (error) > > + goto out_sem; > > + > > + error = ext4_ext_mov_data(donor, receiver, doffset_lblk, > > + roffset_lblk, len_lblk, moved_len); > > + > > + ext4_discard_preallocations(donor); > > + ext4_discard_preallocations(receiver); > > +out_sem: > > + ext4_inode_resume_unlocked_dio(donor); > > + ext4_inode_resume_unlocked_dio(receiver); > > + > > +out_mutex: > > + unlock_two_nondirectories(donor, receiver); > > + return error; > > +} > > diff --git a/fs/ext4/file.c b/fs/ext4/file.c > > index 8695f70..d2feaba 100644 > > --- a/fs/ext4/file.c > > +++ b/fs/ext4/file.c > > @@ -614,5 +614,6 @@ const struct inode_operations ext4_file_inode_operations = { > > .get_acl = ext4_get_acl, > > .set_acl = ext4_set_acl, > > .fiemap = ext4_fiemap, > > + .mov_data = ext4_mov_data, > > }; > > > >