···11+/*22+ * Copyright IBM Corporation, 200733+ * Author Aneesh Kumar K.V <aneesh.kumar@linux.vnet.ibm.com>44+ *55+ * This program is free software; you can redistribute it and/or modify it66+ * under the terms of version 2.1 of the GNU Lesser General Public License77+ * as published by the Free Software Foundation.88+ *99+ * This program is distributed in the hope that it would be useful, but1010+ * WITHOUT ANY WARRANTY; without even the implied warranty of1111+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.1212+ *1313+ */1414+1515+#include <linux/module.h>1616+#include <linux/ext4_jbd2.h>1717+#include <linux/ext4_fs_extents.h>1818+1919+/*2020+ * The contiguous blocks details which can be2121+ * represented by a single extent2222+ */2323+struct list_blocks_struct {2424+ ext4_lblk_t first_block, last_block;2525+ ext4_fsblk_t first_pblock, last_pblock;2626+};2727+2828+static int finish_range(handle_t *handle, struct inode *inode,2929+ struct list_blocks_struct *lb)3030+3131+{3232+ int retval = 0, needed;3333+ struct ext4_extent newext;3434+ struct ext4_ext_path *path;3535+ if (lb->first_pblock == 0)3636+ return 0;3737+3838+ /* Add the extent to temp inode*/3939+ newext.ee_block = cpu_to_le32(lb->first_block);4040+ newext.ee_len = cpu_to_le16(lb->last_block - lb->first_block + 1);4141+ ext4_ext_store_pblock(&newext, lb->first_pblock);4242+ path = ext4_ext_find_extent(inode, lb->first_block, NULL);4343+4444+ if (IS_ERR(path)) {4545+ retval = PTR_ERR(path);4646+ goto err_out;4747+ }4848+4949+ /*5050+ * Calculate the credit needed to inserting this extent5151+ * Since we are doing this in loop we may accumalate extra5252+ * credit. But below we try to not accumalate too much5353+ * of them by restarting the journal.5454+ */5555+ needed = ext4_ext_calc_credits_for_insert(inode, path);5656+5757+ /*5858+ * Make sure the credit we accumalated is not really high5959+ */6060+ if (needed && handle->h_buffer_credits >= EXT4_RESERVE_TRANS_BLOCKS) {6161+ retval = ext4_journal_restart(handle, needed);6262+ if (retval)6363+ goto err_out;6464+ }6565+ if (needed) {6666+ retval = ext4_journal_extend(handle, needed);6767+ if (retval != 0) {6868+ /*6969+ * IF not able to extend the journal restart the journal7070+ */7171+ retval = ext4_journal_restart(handle, needed);7272+ if (retval)7373+ goto err_out;7474+ }7575+ }7676+ retval = ext4_ext_insert_extent(handle, inode, path, &newext);7777+err_out:7878+ lb->first_pblock = 0;7979+ return retval;8080+}8181+8282+static int update_extent_range(handle_t *handle, struct inode *inode,8383+ ext4_fsblk_t pblock, ext4_lblk_t blk_num,8484+ struct list_blocks_struct *lb)8585+{8686+ int retval;8787+ /*8888+ * See if we can add on to the existing range (if it exists)8989+ */9090+ if (lb->first_pblock &&9191+ (lb->last_pblock+1 == pblock) &&9292+ (lb->last_block+1 == blk_num)) {9393+ lb->last_pblock = pblock;9494+ lb->last_block = blk_num;9595+ return 0;9696+ }9797+ /*9898+ * Start a new range.9999+ */100100+ retval = finish_range(handle, inode, lb);101101+ lb->first_pblock = lb->last_pblock = pblock;102102+ lb->first_block = lb->last_block = blk_num;103103+104104+ return retval;105105+}106106+107107+static int update_ind_extent_range(handle_t *handle, struct inode *inode,108108+ ext4_fsblk_t pblock, ext4_lblk_t *blk_nump,109109+ struct list_blocks_struct *lb)110110+{111111+ struct buffer_head *bh;112112+ __le32 *i_data;113113+ int i, retval = 0;114114+ ext4_lblk_t blk_count = *blk_nump;115115+ unsigned long max_entries = inode->i_sb->s_blocksize >> 2;116116+117117+ if (!pblock) {118118+ /* Only update the file block number */119119+ *blk_nump += max_entries;120120+ return 0;121121+ }122122+123123+ bh = sb_bread(inode->i_sb, pblock);124124+ if (!bh)125125+ return -EIO;126126+127127+ i_data = (__le32 *)bh->b_data;128128+ for (i = 0; i < max_entries; i++, blk_count++) {129129+ if (i_data[i]) {130130+ retval = update_extent_range(handle, inode,131131+ le32_to_cpu(i_data[i]),132132+ blk_count, lb);133133+ if (retval)134134+ break;135135+ }136136+ }137137+138138+ /* Update the file block number */139139+ *blk_nump = blk_count;140140+ put_bh(bh);141141+ return retval;142142+143143+}144144+145145+static int update_dind_extent_range(handle_t *handle, struct inode *inode,146146+ ext4_fsblk_t pblock, ext4_lblk_t *blk_nump,147147+ struct list_blocks_struct *lb)148148+{149149+ struct buffer_head *bh;150150+ __le32 *i_data;151151+ int i, retval = 0;152152+ ext4_lblk_t blk_count = *blk_nump;153153+ unsigned long max_entries = inode->i_sb->s_blocksize >> 2;154154+155155+ if (!pblock) {156156+ /* Only update the file block number */157157+ *blk_nump += max_entries * max_entries;158158+ return 0;159159+ }160160+ bh = sb_bread(inode->i_sb, pblock);161161+ if (!bh)162162+ return -EIO;163163+164164+ i_data = (__le32 *)bh->b_data;165165+ for (i = 0; i < max_entries; i++) {166166+ if (i_data[i]) {167167+ retval = update_ind_extent_range(handle, inode,168168+ le32_to_cpu(i_data[i]),169169+ &blk_count, lb);170170+ if (retval)171171+ break;172172+ } else {173173+ /* Only update the file block number */174174+ blk_count += max_entries;175175+ }176176+ }177177+178178+ /* Update the file block number */179179+ *blk_nump = blk_count;180180+ put_bh(bh);181181+ return retval;182182+183183+}184184+185185+static int update_tind_extent_range(handle_t *handle, struct inode *inode,186186+ ext4_fsblk_t pblock, ext4_lblk_t *blk_nump,187187+ struct list_blocks_struct *lb)188188+{189189+ struct buffer_head *bh;190190+ __le32 *i_data;191191+ int i, retval = 0;192192+ ext4_lblk_t blk_count = *blk_nump;193193+ unsigned long max_entries = inode->i_sb->s_blocksize >> 2;194194+195195+ if (!pblock) {196196+ /* Only update the file block number */197197+ *blk_nump += max_entries * max_entries * max_entries;198198+ return 0;199199+ }200200+ bh = sb_bread(inode->i_sb, pblock);201201+ if (!bh)202202+ return -EIO;203203+204204+ i_data = (__le32 *)bh->b_data;205205+ for (i = 0; i < max_entries; i++) {206206+ if (i_data[i]) {207207+ retval = update_dind_extent_range(handle, inode,208208+ le32_to_cpu(i_data[i]),209209+ &blk_count, lb);210210+ if (retval)211211+ break;212212+ } else213213+ /* Only update the file block number */214214+ blk_count += max_entries * max_entries;215215+ }216216+ /* Update the file block number */217217+ *blk_nump = blk_count;218218+ put_bh(bh);219219+ return retval;220220+221221+}222222+223223+static int free_dind_blocks(handle_t *handle,224224+ struct inode *inode, __le32 i_data)225225+{226226+ int i;227227+ __le32 *tmp_idata;228228+ struct buffer_head *bh;229229+ unsigned long max_entries = inode->i_sb->s_blocksize >> 2;230230+231231+ bh = sb_bread(inode->i_sb, le32_to_cpu(i_data));232232+ if (!bh)233233+ return -EIO;234234+235235+ tmp_idata = (__le32 *)bh->b_data;236236+ for (i = 0; i < max_entries; i++) {237237+ if (tmp_idata[i])238238+ ext4_free_blocks(handle, inode,239239+ le32_to_cpu(tmp_idata[i]), 1);240240+ }241241+ put_bh(bh);242242+ ext4_free_blocks(handle, inode, le32_to_cpu(i_data), 1);243243+ return 0;244244+}245245+246246+static int free_tind_blocks(handle_t *handle,247247+ struct inode *inode, __le32 i_data)248248+{249249+ int i, retval = 0;250250+ __le32 *tmp_idata;251251+ struct buffer_head *bh;252252+ unsigned long max_entries = inode->i_sb->s_blocksize >> 2;253253+254254+ bh = sb_bread(inode->i_sb, le32_to_cpu(i_data));255255+ if (!bh)256256+ return -EIO;257257+258258+ tmp_idata = (__le32 *)bh->b_data;259259+ for (i = 0; i < max_entries; i++) {260260+ if (tmp_idata[i]) {261261+ retval = free_dind_blocks(handle,262262+ inode, tmp_idata[i]);263263+ if (retval) {264264+ put_bh(bh);265265+ return retval;266266+ }267267+ }268268+ }269269+ put_bh(bh);270270+ ext4_free_blocks(handle, inode, le32_to_cpu(i_data), 1);271271+ return 0;272272+}273273+274274+static int free_ind_block(handle_t *handle, struct inode *inode)275275+{276276+ int retval;277277+ struct ext4_inode_info *ei = EXT4_I(inode);278278+279279+ if (ei->i_data[EXT4_IND_BLOCK])280280+ ext4_free_blocks(handle, inode,281281+ le32_to_cpu(ei->i_data[EXT4_IND_BLOCK]), 1);282282+283283+ if (ei->i_data[EXT4_DIND_BLOCK]) {284284+ retval = free_dind_blocks(handle, inode,285285+ ei->i_data[EXT4_DIND_BLOCK]);286286+ if (retval)287287+ return retval;288288+ }289289+290290+ if (ei->i_data[EXT4_TIND_BLOCK]) {291291+ retval = free_tind_blocks(handle, inode,292292+ ei->i_data[EXT4_TIND_BLOCK]);293293+ if (retval)294294+ return retval;295295+ }296296+ return 0;297297+}298298+299299+static int ext4_ext_swap_inode_data(handle_t *handle, struct inode *inode,300300+ struct inode *tmp_inode, int retval)301301+{302302+ struct ext4_inode_info *ei = EXT4_I(inode);303303+ struct ext4_inode_info *tmp_ei = EXT4_I(tmp_inode);304304+305305+ retval = free_ind_block(handle, inode);306306+ if (retval)307307+ goto err_out;308308+309309+ /*310310+ * One credit accounted for writing the311311+ * i_data field of the original inode312312+ */313313+ retval = ext4_journal_extend(handle, 1);314314+ if (retval != 0) {315315+ retval = ext4_journal_restart(handle, 1);316316+ if (retval)317317+ goto err_out;318318+ }319319+320320+ /*321321+ * We have the extent map build with the tmp inode.322322+ * Now copy the i_data across323323+ */324324+ ei->i_flags |= EXT4_EXTENTS_FL;325325+ memcpy(ei->i_data, tmp_ei->i_data, sizeof(ei->i_data));326326+327327+ /*328328+ * Update i_blocks with the new blocks that got329329+ * allocated while adding extents for extent index330330+ * blocks.331331+ *332332+ * While converting to extents we need not333333+ * update the orignal inode i_blocks for extent blocks334334+ * via quota APIs. The quota update happened via tmp_inode already.335335+ */336336+ spin_lock(&inode->i_lock);337337+ inode->i_blocks += tmp_inode->i_blocks;338338+ spin_unlock(&inode->i_lock);339339+340340+ ext4_mark_inode_dirty(handle, inode);341341+err_out:342342+ return retval;343343+}344344+345345+static int free_ext_idx(handle_t *handle, struct inode *inode,346346+ struct ext4_extent_idx *ix)347347+{348348+ int i, retval = 0;349349+ ext4_fsblk_t block;350350+ struct buffer_head *bh;351351+ struct ext4_extent_header *eh;352352+353353+ block = idx_pblock(ix);354354+ bh = sb_bread(inode->i_sb, block);355355+ if (!bh)356356+ return -EIO;357357+358358+ eh = (struct ext4_extent_header *)bh->b_data;359359+ if (eh->eh_depth != 0) {360360+ ix = EXT_FIRST_INDEX(eh);361361+ for (i = 0; i < le16_to_cpu(eh->eh_entries); i++, ix++) {362362+ retval = free_ext_idx(handle, inode, ix);363363+ if (retval)364364+ break;365365+ }366366+ }367367+ put_bh(bh);368368+ ext4_free_blocks(handle, inode, block, 1);369369+ return retval;370370+}371371+372372+/*373373+ * Free the extent meta data blocks only374374+ */375375+static int free_ext_block(handle_t *handle, struct inode *inode)376376+{377377+ int i, retval = 0;378378+ struct ext4_inode_info *ei = EXT4_I(inode);379379+ struct ext4_extent_header *eh = (struct ext4_extent_header *)ei->i_data;380380+ struct ext4_extent_idx *ix;381381+ if (eh->eh_depth == 0)382382+ /*383383+ * No extra blocks allocated for extent meta data384384+ */385385+ return 0;386386+ ix = EXT_FIRST_INDEX(eh);387387+ for (i = 0; i < le16_to_cpu(eh->eh_entries); i++, ix++) {388388+ retval = free_ext_idx(handle, inode, ix);389389+ if (retval)390390+ return retval;391391+ }392392+ return retval;393393+394394+}395395+396396+int ext4_ext_migrate(struct inode *inode, struct file *filp,397397+ unsigned int cmd, unsigned long arg)398398+{399399+ handle_t *handle;400400+ int retval = 0, i;401401+ __le32 *i_data;402402+ ext4_lblk_t blk_count = 0;403403+ struct ext4_inode_info *ei;404404+ struct inode *tmp_inode = NULL;405405+ struct list_blocks_struct lb;406406+ unsigned long max_entries;407407+408408+ if (!test_opt(inode->i_sb, EXTENTS))409409+ /*410410+ * if mounted with noextents we don't allow the migrate411411+ */412412+ return -EINVAL;413413+414414+ if ((EXT4_I(inode)->i_flags & EXT4_EXTENTS_FL))415415+ return -EINVAL;416416+417417+ down_write(&EXT4_I(inode)->i_data_sem);418418+ handle = ext4_journal_start(inode,419419+ EXT4_DATA_TRANS_BLOCKS(inode->i_sb) +420420+ EXT4_INDEX_EXTRA_TRANS_BLOCKS + 3 +421421+ 2 * EXT4_QUOTA_INIT_BLOCKS(inode->i_sb)422422+ + 1);423423+ if (IS_ERR(handle)) {424424+ retval = PTR_ERR(handle);425425+ goto err_out;426426+ }427427+ tmp_inode = ext4_new_inode(handle,428428+ inode->i_sb->s_root->d_inode,429429+ S_IFREG);430430+ if (IS_ERR(tmp_inode)) {431431+ retval = -ENOMEM;432432+ ext4_journal_stop(handle);433433+ tmp_inode = NULL;434434+ goto err_out;435435+ }436436+ i_size_write(tmp_inode, i_size_read(inode));437437+ /*438438+ * We don't want the inode to be reclaimed439439+ * if we got interrupted in between. We have440440+ * this tmp inode carrying reference to the441441+ * data blocks of the original file. We set442442+ * the i_nlink to zero at the last stage after443443+ * switching the original file to extent format444444+ */445445+ tmp_inode->i_nlink = 1;446446+447447+ ext4_ext_tree_init(handle, tmp_inode);448448+ ext4_orphan_add(handle, tmp_inode);449449+ ext4_journal_stop(handle);450450+451451+ ei = EXT4_I(inode);452452+ i_data = ei->i_data;453453+ memset(&lb, 0, sizeof(lb));454454+455455+ /* 32 bit block address 4 bytes */456456+ max_entries = inode->i_sb->s_blocksize >> 2;457457+458458+ /*459459+ * start with one credit accounted for460460+ * superblock modification.461461+ *462462+ * For the tmp_inode we already have commited the463463+ * trascation that created the inode. Later as and464464+ * when we add extents we extent the journal465465+ */466466+ handle = ext4_journal_start(inode, 1);467467+ for (i = 0; i < EXT4_NDIR_BLOCKS; i++, blk_count++) {468468+ if (i_data[i]) {469469+ retval = update_extent_range(handle, tmp_inode,470470+ le32_to_cpu(i_data[i]),471471+ blk_count, &lb);472472+ if (retval)473473+ goto err_out;474474+ }475475+ }476476+ if (i_data[EXT4_IND_BLOCK]) {477477+ retval = update_ind_extent_range(handle, tmp_inode,478478+ le32_to_cpu(i_data[EXT4_IND_BLOCK]),479479+ &blk_count, &lb);480480+ if (retval)481481+ goto err_out;482482+ } else483483+ blk_count += max_entries;484484+ if (i_data[EXT4_DIND_BLOCK]) {485485+ retval = update_dind_extent_range(handle, tmp_inode,486486+ le32_to_cpu(i_data[EXT4_DIND_BLOCK]),487487+ &blk_count, &lb);488488+ if (retval)489489+ goto err_out;490490+ } else491491+ blk_count += max_entries * max_entries;492492+ if (i_data[EXT4_TIND_BLOCK]) {493493+ retval = update_tind_extent_range(handle, tmp_inode,494494+ le32_to_cpu(i_data[EXT4_TIND_BLOCK]),495495+ &blk_count, &lb);496496+ if (retval)497497+ goto err_out;498498+ }499499+ /*500500+ * Build the last extent501501+ */502502+ retval = finish_range(handle, tmp_inode, &lb);503503+err_out:504504+ /*505505+ * We are either freeing extent information or indirect506506+ * blocks. During this we touch superblock, group descriptor507507+ * and block bitmap. Later we mark the tmp_inode dirty508508+ * via ext4_ext_tree_init. So allocate a credit of 4509509+ * We may update quota (user and group).510510+ *511511+ * FIXME!! we may be touching bitmaps in different block groups.512512+ */513513+ if (ext4_journal_extend(handle,514514+ 4 + 2*EXT4_QUOTA_TRANS_BLOCKS(inode->i_sb)) != 0)515515+ ext4_journal_restart(handle,516516+ 4 + 2*EXT4_QUOTA_TRANS_BLOCKS(inode->i_sb));517517+ if (retval)518518+ /*519519+ * Failure case delete the extent information with the520520+ * tmp_inode521521+ */522522+ free_ext_block(handle, tmp_inode);523523+ else524524+ retval = ext4_ext_swap_inode_data(handle, inode,525525+ tmp_inode, retval);526526+527527+ /*528528+ * Mark the tmp_inode as of size zero529529+ */530530+ i_size_write(tmp_inode, 0);531531+532532+ /*533533+ * set the i_blocks count to zero534534+ * so that the ext4_delete_inode does the535535+ * right job536536+ *537537+ * We don't need to take the i_lock because538538+ * the inode is not visible to user space.539539+ */540540+ tmp_inode->i_blocks = 0;541541+542542+ /* Reset the extent details */543543+ ext4_ext_tree_init(handle, tmp_inode);544544+545545+ /*546546+ * Set the i_nlink to zero so that547547+ * generic_drop_inode really deletes the548548+ * inode549549+ */550550+ tmp_inode->i_nlink = 0;551551+552552+ ext4_journal_stop(handle);553553+554554+ up_write(&EXT4_I(inode)->i_data_sem);555555+556556+ if (tmp_inode)557557+ iput(tmp_inode);558558+559559+ return retval;560560+}
+4
include/linux/ext4_fs.h
···243243#endif244244#define EXT4_IOC_GETRSVSZ _IOR('f', 5, long)245245#define EXT4_IOC_SETRSVSZ _IOW('f', 6, long)246246+#define EXT4_IOC_MIGRATE _IO('f', 7)246247247248/*248249 * ioctl commands in 32 bit emulation···984983 unsigned long);985984extern long ext4_compat_ioctl (struct file *, unsigned int, unsigned long);986985986986+/* migrate.c */987987+extern int ext4_ext_migrate(struct inode *, struct file *, unsigned int,988988+ unsigned long);987989/* namei.c */988990extern int ext4_orphan_add(handle_t *, struct inode *);989991extern int ext4_orphan_del(handle_t *, struct inode *);
+2
include/linux/ext4_fs_extents.h
···212212 (le16_to_cpu(ext->ee_len) - EXT_INIT_MAX_LEN));213213}214214215215+extern ext4_fsblk_t idx_pblock(struct ext4_extent_idx *);216216+extern void ext4_ext_store_pblock(struct ext4_extent *, ext4_fsblk_t);215217extern int ext4_extent_tree_init(handle_t *, struct inode *);216218extern int ext4_ext_calc_credits_for_insert(struct inode *, struct ext4_ext_path *);217219extern int ext4_ext_try_to_merge(struct inode *inode,