18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci#include <linux/fs.h> 38c2ecf20Sopenharmony_ci#include <linux/random.h> 48c2ecf20Sopenharmony_ci#include <linux/buffer_head.h> 58c2ecf20Sopenharmony_ci#include <linux/utsname.h> 68c2ecf20Sopenharmony_ci#include <linux/kthread.h> 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include "ext4.h" 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci/* Checksumming functions */ 118c2ecf20Sopenharmony_cistatic __le32 ext4_mmp_csum(struct super_block *sb, struct mmp_struct *mmp) 128c2ecf20Sopenharmony_ci{ 138c2ecf20Sopenharmony_ci struct ext4_sb_info *sbi = EXT4_SB(sb); 148c2ecf20Sopenharmony_ci int offset = offsetof(struct mmp_struct, mmp_checksum); 158c2ecf20Sopenharmony_ci __u32 csum; 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_ci csum = ext4_chksum(sbi, sbi->s_csum_seed, (char *)mmp, offset); 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ci return cpu_to_le32(csum); 208c2ecf20Sopenharmony_ci} 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_cistatic int ext4_mmp_csum_verify(struct super_block *sb, struct mmp_struct *mmp) 238c2ecf20Sopenharmony_ci{ 248c2ecf20Sopenharmony_ci if (!ext4_has_metadata_csum(sb)) 258c2ecf20Sopenharmony_ci return 1; 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci return mmp->mmp_checksum == ext4_mmp_csum(sb, mmp); 288c2ecf20Sopenharmony_ci} 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_cistatic void ext4_mmp_csum_set(struct super_block *sb, struct mmp_struct *mmp) 318c2ecf20Sopenharmony_ci{ 328c2ecf20Sopenharmony_ci if (!ext4_has_metadata_csum(sb)) 338c2ecf20Sopenharmony_ci return; 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci mmp->mmp_checksum = ext4_mmp_csum(sb, mmp); 368c2ecf20Sopenharmony_ci} 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci/* 398c2ecf20Sopenharmony_ci * Write the MMP block using REQ_SYNC to try to get the block on-disk 408c2ecf20Sopenharmony_ci * faster. 418c2ecf20Sopenharmony_ci */ 428c2ecf20Sopenharmony_cistatic int write_mmp_block_thawed(struct super_block *sb, 438c2ecf20Sopenharmony_ci struct buffer_head *bh) 448c2ecf20Sopenharmony_ci{ 458c2ecf20Sopenharmony_ci struct mmp_struct *mmp = (struct mmp_struct *)(bh->b_data); 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_ci ext4_mmp_csum_set(sb, mmp); 488c2ecf20Sopenharmony_ci lock_buffer(bh); 498c2ecf20Sopenharmony_ci bh->b_end_io = end_buffer_write_sync; 508c2ecf20Sopenharmony_ci get_bh(bh); 518c2ecf20Sopenharmony_ci submit_bh(REQ_OP_WRITE, REQ_SYNC | REQ_META | REQ_PRIO, bh); 528c2ecf20Sopenharmony_ci wait_on_buffer(bh); 538c2ecf20Sopenharmony_ci if (unlikely(!buffer_uptodate(bh))) 548c2ecf20Sopenharmony_ci return -EIO; 558c2ecf20Sopenharmony_ci return 0; 568c2ecf20Sopenharmony_ci} 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_cistatic int write_mmp_block(struct super_block *sb, struct buffer_head *bh) 598c2ecf20Sopenharmony_ci{ 608c2ecf20Sopenharmony_ci int err; 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci /* 638c2ecf20Sopenharmony_ci * We protect against freezing so that we don't create dirty buffers 648c2ecf20Sopenharmony_ci * on frozen filesystem. 658c2ecf20Sopenharmony_ci */ 668c2ecf20Sopenharmony_ci sb_start_write(sb); 678c2ecf20Sopenharmony_ci err = write_mmp_block_thawed(sb, bh); 688c2ecf20Sopenharmony_ci sb_end_write(sb); 698c2ecf20Sopenharmony_ci return err; 708c2ecf20Sopenharmony_ci} 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci/* 738c2ecf20Sopenharmony_ci * Read the MMP block. It _must_ be read from disk and hence we clear the 748c2ecf20Sopenharmony_ci * uptodate flag on the buffer. 758c2ecf20Sopenharmony_ci */ 768c2ecf20Sopenharmony_cistatic int read_mmp_block(struct super_block *sb, struct buffer_head **bh, 778c2ecf20Sopenharmony_ci ext4_fsblk_t mmp_block) 788c2ecf20Sopenharmony_ci{ 798c2ecf20Sopenharmony_ci struct mmp_struct *mmp; 808c2ecf20Sopenharmony_ci int ret; 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci if (*bh) 838c2ecf20Sopenharmony_ci clear_buffer_uptodate(*bh); 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci /* This would be sb_bread(sb, mmp_block), except we need to be sure 868c2ecf20Sopenharmony_ci * that the MD RAID device cache has been bypassed, and that the read 878c2ecf20Sopenharmony_ci * is not blocked in the elevator. */ 888c2ecf20Sopenharmony_ci if (!*bh) { 898c2ecf20Sopenharmony_ci *bh = sb_getblk(sb, mmp_block); 908c2ecf20Sopenharmony_ci if (!*bh) { 918c2ecf20Sopenharmony_ci ret = -ENOMEM; 928c2ecf20Sopenharmony_ci goto warn_exit; 938c2ecf20Sopenharmony_ci } 948c2ecf20Sopenharmony_ci } 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci lock_buffer(*bh); 978c2ecf20Sopenharmony_ci ret = ext4_read_bh(*bh, REQ_META | REQ_PRIO, NULL); 988c2ecf20Sopenharmony_ci if (ret) 998c2ecf20Sopenharmony_ci goto warn_exit; 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci mmp = (struct mmp_struct *)((*bh)->b_data); 1028c2ecf20Sopenharmony_ci if (le32_to_cpu(mmp->mmp_magic) != EXT4_MMP_MAGIC) { 1038c2ecf20Sopenharmony_ci ret = -EFSCORRUPTED; 1048c2ecf20Sopenharmony_ci goto warn_exit; 1058c2ecf20Sopenharmony_ci } 1068c2ecf20Sopenharmony_ci if (!ext4_mmp_csum_verify(sb, mmp)) { 1078c2ecf20Sopenharmony_ci ret = -EFSBADCRC; 1088c2ecf20Sopenharmony_ci goto warn_exit; 1098c2ecf20Sopenharmony_ci } 1108c2ecf20Sopenharmony_ci return 0; 1118c2ecf20Sopenharmony_ciwarn_exit: 1128c2ecf20Sopenharmony_ci brelse(*bh); 1138c2ecf20Sopenharmony_ci *bh = NULL; 1148c2ecf20Sopenharmony_ci ext4_warning(sb, "Error %d while reading MMP block %llu", 1158c2ecf20Sopenharmony_ci ret, mmp_block); 1168c2ecf20Sopenharmony_ci return ret; 1178c2ecf20Sopenharmony_ci} 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci/* 1208c2ecf20Sopenharmony_ci * Dump as much information as possible to help the admin. 1218c2ecf20Sopenharmony_ci */ 1228c2ecf20Sopenharmony_civoid __dump_mmp_msg(struct super_block *sb, struct mmp_struct *mmp, 1238c2ecf20Sopenharmony_ci const char *function, unsigned int line, const char *msg) 1248c2ecf20Sopenharmony_ci{ 1258c2ecf20Sopenharmony_ci __ext4_warning(sb, function, line, "%s", msg); 1268c2ecf20Sopenharmony_ci __ext4_warning(sb, function, line, 1278c2ecf20Sopenharmony_ci "MMP failure info: last update time: %llu, last update node: %.*s, last update device: %.*s", 1288c2ecf20Sopenharmony_ci (unsigned long long)le64_to_cpu(mmp->mmp_time), 1298c2ecf20Sopenharmony_ci (int)sizeof(mmp->mmp_nodename), mmp->mmp_nodename, 1308c2ecf20Sopenharmony_ci (int)sizeof(mmp->mmp_bdevname), mmp->mmp_bdevname); 1318c2ecf20Sopenharmony_ci} 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci/* 1348c2ecf20Sopenharmony_ci * kmmpd will update the MMP sequence every s_mmp_update_interval seconds 1358c2ecf20Sopenharmony_ci */ 1368c2ecf20Sopenharmony_cistatic int kmmpd(void *data) 1378c2ecf20Sopenharmony_ci{ 1388c2ecf20Sopenharmony_ci struct super_block *sb = (struct super_block *) data; 1398c2ecf20Sopenharmony_ci struct ext4_super_block *es = EXT4_SB(sb)->s_es; 1408c2ecf20Sopenharmony_ci struct buffer_head *bh = EXT4_SB(sb)->s_mmp_bh; 1418c2ecf20Sopenharmony_ci struct mmp_struct *mmp; 1428c2ecf20Sopenharmony_ci ext4_fsblk_t mmp_block; 1438c2ecf20Sopenharmony_ci u32 seq = 0; 1448c2ecf20Sopenharmony_ci unsigned long failed_writes = 0; 1458c2ecf20Sopenharmony_ci int mmp_update_interval = le16_to_cpu(es->s_mmp_update_interval); 1468c2ecf20Sopenharmony_ci unsigned mmp_check_interval; 1478c2ecf20Sopenharmony_ci unsigned long last_update_time; 1488c2ecf20Sopenharmony_ci unsigned long diff; 1498c2ecf20Sopenharmony_ci int retval = 0; 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci mmp_block = le64_to_cpu(es->s_mmp_block); 1528c2ecf20Sopenharmony_ci mmp = (struct mmp_struct *)(bh->b_data); 1538c2ecf20Sopenharmony_ci mmp->mmp_time = cpu_to_le64(ktime_get_real_seconds()); 1548c2ecf20Sopenharmony_ci /* 1558c2ecf20Sopenharmony_ci * Start with the higher mmp_check_interval and reduce it if 1568c2ecf20Sopenharmony_ci * the MMP block is being updated on time. 1578c2ecf20Sopenharmony_ci */ 1588c2ecf20Sopenharmony_ci mmp_check_interval = max(EXT4_MMP_CHECK_MULT * mmp_update_interval, 1598c2ecf20Sopenharmony_ci EXT4_MMP_MIN_CHECK_INTERVAL); 1608c2ecf20Sopenharmony_ci mmp->mmp_check_interval = cpu_to_le16(mmp_check_interval); 1618c2ecf20Sopenharmony_ci BUILD_BUG_ON(sizeof(mmp->mmp_bdevname) < BDEVNAME_SIZE); 1628c2ecf20Sopenharmony_ci bdevname(bh->b_bdev, mmp->mmp_bdevname); 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci memcpy(mmp->mmp_nodename, init_utsname()->nodename, 1658c2ecf20Sopenharmony_ci sizeof(mmp->mmp_nodename)); 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci while (!kthread_should_stop() && !sb_rdonly(sb)) { 1688c2ecf20Sopenharmony_ci if (!ext4_has_feature_mmp(sb)) { 1698c2ecf20Sopenharmony_ci ext4_warning(sb, "kmmpd being stopped since MMP feature" 1708c2ecf20Sopenharmony_ci " has been disabled."); 1718c2ecf20Sopenharmony_ci goto wait_to_exit; 1728c2ecf20Sopenharmony_ci } 1738c2ecf20Sopenharmony_ci if (++seq > EXT4_MMP_SEQ_MAX) 1748c2ecf20Sopenharmony_ci seq = 1; 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ci mmp->mmp_seq = cpu_to_le32(seq); 1778c2ecf20Sopenharmony_ci mmp->mmp_time = cpu_to_le64(ktime_get_real_seconds()); 1788c2ecf20Sopenharmony_ci last_update_time = jiffies; 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci retval = write_mmp_block(sb, bh); 1818c2ecf20Sopenharmony_ci /* 1828c2ecf20Sopenharmony_ci * Don't spew too many error messages. Print one every 1838c2ecf20Sopenharmony_ci * (s_mmp_update_interval * 60) seconds. 1848c2ecf20Sopenharmony_ci */ 1858c2ecf20Sopenharmony_ci if (retval) { 1868c2ecf20Sopenharmony_ci if ((failed_writes % 60) == 0) { 1878c2ecf20Sopenharmony_ci ext4_error_err(sb, -retval, 1888c2ecf20Sopenharmony_ci "Error writing to MMP block"); 1898c2ecf20Sopenharmony_ci } 1908c2ecf20Sopenharmony_ci failed_writes++; 1918c2ecf20Sopenharmony_ci } 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ci diff = jiffies - last_update_time; 1948c2ecf20Sopenharmony_ci if (diff < mmp_update_interval * HZ) 1958c2ecf20Sopenharmony_ci schedule_timeout_interruptible(mmp_update_interval * 1968c2ecf20Sopenharmony_ci HZ - diff); 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ci /* 1998c2ecf20Sopenharmony_ci * We need to make sure that more than mmp_check_interval 2008c2ecf20Sopenharmony_ci * seconds have not passed since writing. If that has happened 2018c2ecf20Sopenharmony_ci * we need to check if the MMP block is as we left it. 2028c2ecf20Sopenharmony_ci */ 2038c2ecf20Sopenharmony_ci diff = jiffies - last_update_time; 2048c2ecf20Sopenharmony_ci if (diff > mmp_check_interval * HZ) { 2058c2ecf20Sopenharmony_ci struct buffer_head *bh_check = NULL; 2068c2ecf20Sopenharmony_ci struct mmp_struct *mmp_check; 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_ci retval = read_mmp_block(sb, &bh_check, mmp_block); 2098c2ecf20Sopenharmony_ci if (retval) { 2108c2ecf20Sopenharmony_ci ext4_error_err(sb, -retval, 2118c2ecf20Sopenharmony_ci "error reading MMP data: %d", 2128c2ecf20Sopenharmony_ci retval); 2138c2ecf20Sopenharmony_ci goto wait_to_exit; 2148c2ecf20Sopenharmony_ci } 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci mmp_check = (struct mmp_struct *)(bh_check->b_data); 2178c2ecf20Sopenharmony_ci if (mmp->mmp_seq != mmp_check->mmp_seq || 2188c2ecf20Sopenharmony_ci memcmp(mmp->mmp_nodename, mmp_check->mmp_nodename, 2198c2ecf20Sopenharmony_ci sizeof(mmp->mmp_nodename))) { 2208c2ecf20Sopenharmony_ci dump_mmp_msg(sb, mmp_check, 2218c2ecf20Sopenharmony_ci "Error while updating MMP info. " 2228c2ecf20Sopenharmony_ci "The filesystem seems to have been" 2238c2ecf20Sopenharmony_ci " multiply mounted."); 2248c2ecf20Sopenharmony_ci ext4_error_err(sb, EBUSY, "abort"); 2258c2ecf20Sopenharmony_ci put_bh(bh_check); 2268c2ecf20Sopenharmony_ci retval = -EBUSY; 2278c2ecf20Sopenharmony_ci goto wait_to_exit; 2288c2ecf20Sopenharmony_ci } 2298c2ecf20Sopenharmony_ci put_bh(bh_check); 2308c2ecf20Sopenharmony_ci } 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_ci /* 2338c2ecf20Sopenharmony_ci * Adjust the mmp_check_interval depending on how much time 2348c2ecf20Sopenharmony_ci * it took for the MMP block to be written. 2358c2ecf20Sopenharmony_ci */ 2368c2ecf20Sopenharmony_ci mmp_check_interval = max(min(EXT4_MMP_CHECK_MULT * diff / HZ, 2378c2ecf20Sopenharmony_ci EXT4_MMP_MAX_CHECK_INTERVAL), 2388c2ecf20Sopenharmony_ci EXT4_MMP_MIN_CHECK_INTERVAL); 2398c2ecf20Sopenharmony_ci mmp->mmp_check_interval = cpu_to_le16(mmp_check_interval); 2408c2ecf20Sopenharmony_ci } 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci /* 2438c2ecf20Sopenharmony_ci * Unmount seems to be clean. 2448c2ecf20Sopenharmony_ci */ 2458c2ecf20Sopenharmony_ci mmp->mmp_seq = cpu_to_le32(EXT4_MMP_SEQ_CLEAN); 2468c2ecf20Sopenharmony_ci mmp->mmp_time = cpu_to_le64(ktime_get_real_seconds()); 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_ci retval = write_mmp_block(sb, bh); 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_ciwait_to_exit: 2518c2ecf20Sopenharmony_ci while (!kthread_should_stop()) { 2528c2ecf20Sopenharmony_ci set_current_state(TASK_INTERRUPTIBLE); 2538c2ecf20Sopenharmony_ci if (!kthread_should_stop()) 2548c2ecf20Sopenharmony_ci schedule(); 2558c2ecf20Sopenharmony_ci } 2568c2ecf20Sopenharmony_ci set_current_state(TASK_RUNNING); 2578c2ecf20Sopenharmony_ci return retval; 2588c2ecf20Sopenharmony_ci} 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_civoid ext4_stop_mmpd(struct ext4_sb_info *sbi) 2618c2ecf20Sopenharmony_ci{ 2628c2ecf20Sopenharmony_ci if (sbi->s_mmp_tsk) { 2638c2ecf20Sopenharmony_ci kthread_stop(sbi->s_mmp_tsk); 2648c2ecf20Sopenharmony_ci brelse(sbi->s_mmp_bh); 2658c2ecf20Sopenharmony_ci sbi->s_mmp_tsk = NULL; 2668c2ecf20Sopenharmony_ci } 2678c2ecf20Sopenharmony_ci} 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_ci/* 2708c2ecf20Sopenharmony_ci * Get a random new sequence number but make sure it is not greater than 2718c2ecf20Sopenharmony_ci * EXT4_MMP_SEQ_MAX. 2728c2ecf20Sopenharmony_ci */ 2738c2ecf20Sopenharmony_cistatic unsigned int mmp_new_seq(void) 2748c2ecf20Sopenharmony_ci{ 2758c2ecf20Sopenharmony_ci u32 new_seq; 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_ci do { 2788c2ecf20Sopenharmony_ci new_seq = prandom_u32(); 2798c2ecf20Sopenharmony_ci } while (new_seq > EXT4_MMP_SEQ_MAX); 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_ci return new_seq; 2828c2ecf20Sopenharmony_ci} 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_ci/* 2858c2ecf20Sopenharmony_ci * Protect the filesystem from being mounted more than once. 2868c2ecf20Sopenharmony_ci */ 2878c2ecf20Sopenharmony_ciint ext4_multi_mount_protect(struct super_block *sb, 2888c2ecf20Sopenharmony_ci ext4_fsblk_t mmp_block) 2898c2ecf20Sopenharmony_ci{ 2908c2ecf20Sopenharmony_ci struct ext4_super_block *es = EXT4_SB(sb)->s_es; 2918c2ecf20Sopenharmony_ci struct buffer_head *bh = NULL; 2928c2ecf20Sopenharmony_ci struct mmp_struct *mmp = NULL; 2938c2ecf20Sopenharmony_ci u32 seq; 2948c2ecf20Sopenharmony_ci unsigned int mmp_check_interval = le16_to_cpu(es->s_mmp_update_interval); 2958c2ecf20Sopenharmony_ci unsigned int wait_time = 0; 2968c2ecf20Sopenharmony_ci int retval; 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_ci if (mmp_block < le32_to_cpu(es->s_first_data_block) || 2998c2ecf20Sopenharmony_ci mmp_block >= ext4_blocks_count(es)) { 3008c2ecf20Sopenharmony_ci ext4_warning(sb, "Invalid MMP block in superblock"); 3018c2ecf20Sopenharmony_ci retval = -EINVAL; 3028c2ecf20Sopenharmony_ci goto failed; 3038c2ecf20Sopenharmony_ci } 3048c2ecf20Sopenharmony_ci 3058c2ecf20Sopenharmony_ci retval = read_mmp_block(sb, &bh, mmp_block); 3068c2ecf20Sopenharmony_ci if (retval) 3078c2ecf20Sopenharmony_ci goto failed; 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_ci mmp = (struct mmp_struct *)(bh->b_data); 3108c2ecf20Sopenharmony_ci 3118c2ecf20Sopenharmony_ci if (mmp_check_interval < EXT4_MMP_MIN_CHECK_INTERVAL) 3128c2ecf20Sopenharmony_ci mmp_check_interval = EXT4_MMP_MIN_CHECK_INTERVAL; 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_ci /* 3158c2ecf20Sopenharmony_ci * If check_interval in MMP block is larger, use that instead of 3168c2ecf20Sopenharmony_ci * update_interval from the superblock. 3178c2ecf20Sopenharmony_ci */ 3188c2ecf20Sopenharmony_ci if (le16_to_cpu(mmp->mmp_check_interval) > mmp_check_interval) 3198c2ecf20Sopenharmony_ci mmp_check_interval = le16_to_cpu(mmp->mmp_check_interval); 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_ci seq = le32_to_cpu(mmp->mmp_seq); 3228c2ecf20Sopenharmony_ci if (seq == EXT4_MMP_SEQ_CLEAN) 3238c2ecf20Sopenharmony_ci goto skip; 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_ci if (seq == EXT4_MMP_SEQ_FSCK) { 3268c2ecf20Sopenharmony_ci dump_mmp_msg(sb, mmp, "fsck is running on the filesystem"); 3278c2ecf20Sopenharmony_ci retval = -EBUSY; 3288c2ecf20Sopenharmony_ci goto failed; 3298c2ecf20Sopenharmony_ci } 3308c2ecf20Sopenharmony_ci 3318c2ecf20Sopenharmony_ci wait_time = min(mmp_check_interval * 2 + 1, 3328c2ecf20Sopenharmony_ci mmp_check_interval + 60); 3338c2ecf20Sopenharmony_ci 3348c2ecf20Sopenharmony_ci /* Print MMP interval if more than 20 secs. */ 3358c2ecf20Sopenharmony_ci if (wait_time > EXT4_MMP_MIN_CHECK_INTERVAL * 4) 3368c2ecf20Sopenharmony_ci ext4_warning(sb, "MMP interval %u higher than expected, please" 3378c2ecf20Sopenharmony_ci " wait.\n", wait_time * 2); 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_ci if (schedule_timeout_interruptible(HZ * wait_time) != 0) { 3408c2ecf20Sopenharmony_ci ext4_warning(sb, "MMP startup interrupted, failing mount\n"); 3418c2ecf20Sopenharmony_ci retval = -ETIMEDOUT; 3428c2ecf20Sopenharmony_ci goto failed; 3438c2ecf20Sopenharmony_ci } 3448c2ecf20Sopenharmony_ci 3458c2ecf20Sopenharmony_ci retval = read_mmp_block(sb, &bh, mmp_block); 3468c2ecf20Sopenharmony_ci if (retval) 3478c2ecf20Sopenharmony_ci goto failed; 3488c2ecf20Sopenharmony_ci mmp = (struct mmp_struct *)(bh->b_data); 3498c2ecf20Sopenharmony_ci if (seq != le32_to_cpu(mmp->mmp_seq)) { 3508c2ecf20Sopenharmony_ci dump_mmp_msg(sb, mmp, 3518c2ecf20Sopenharmony_ci "Device is already active on another node."); 3528c2ecf20Sopenharmony_ci retval = -EBUSY; 3538c2ecf20Sopenharmony_ci goto failed; 3548c2ecf20Sopenharmony_ci } 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_ciskip: 3578c2ecf20Sopenharmony_ci /* 3588c2ecf20Sopenharmony_ci * write a new random sequence number. 3598c2ecf20Sopenharmony_ci */ 3608c2ecf20Sopenharmony_ci seq = mmp_new_seq(); 3618c2ecf20Sopenharmony_ci mmp->mmp_seq = cpu_to_le32(seq); 3628c2ecf20Sopenharmony_ci 3638c2ecf20Sopenharmony_ci /* 3648c2ecf20Sopenharmony_ci * On mount / remount we are protected against fs freezing (by s_umount 3658c2ecf20Sopenharmony_ci * semaphore) and grabbing freeze protection upsets lockdep 3668c2ecf20Sopenharmony_ci */ 3678c2ecf20Sopenharmony_ci retval = write_mmp_block_thawed(sb, bh); 3688c2ecf20Sopenharmony_ci if (retval) 3698c2ecf20Sopenharmony_ci goto failed; 3708c2ecf20Sopenharmony_ci 3718c2ecf20Sopenharmony_ci /* 3728c2ecf20Sopenharmony_ci * wait for MMP interval and check mmp_seq. 3738c2ecf20Sopenharmony_ci */ 3748c2ecf20Sopenharmony_ci if (schedule_timeout_interruptible(HZ * wait_time) != 0) { 3758c2ecf20Sopenharmony_ci ext4_warning(sb, "MMP startup interrupted, failing mount"); 3768c2ecf20Sopenharmony_ci retval = -ETIMEDOUT; 3778c2ecf20Sopenharmony_ci goto failed; 3788c2ecf20Sopenharmony_ci } 3798c2ecf20Sopenharmony_ci 3808c2ecf20Sopenharmony_ci retval = read_mmp_block(sb, &bh, mmp_block); 3818c2ecf20Sopenharmony_ci if (retval) 3828c2ecf20Sopenharmony_ci goto failed; 3838c2ecf20Sopenharmony_ci mmp = (struct mmp_struct *)(bh->b_data); 3848c2ecf20Sopenharmony_ci if (seq != le32_to_cpu(mmp->mmp_seq)) { 3858c2ecf20Sopenharmony_ci dump_mmp_msg(sb, mmp, 3868c2ecf20Sopenharmony_ci "Device is already active on another node."); 3878c2ecf20Sopenharmony_ci retval = -EBUSY; 3888c2ecf20Sopenharmony_ci goto failed; 3898c2ecf20Sopenharmony_ci } 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_ci EXT4_SB(sb)->s_mmp_bh = bh; 3928c2ecf20Sopenharmony_ci 3938c2ecf20Sopenharmony_ci /* 3948c2ecf20Sopenharmony_ci * Start a kernel thread to update the MMP block periodically. 3958c2ecf20Sopenharmony_ci */ 3968c2ecf20Sopenharmony_ci EXT4_SB(sb)->s_mmp_tsk = kthread_run(kmmpd, sb, "kmmpd-%.*s", 3978c2ecf20Sopenharmony_ci (int)sizeof(mmp->mmp_bdevname), 3988c2ecf20Sopenharmony_ci bdevname(bh->b_bdev, 3998c2ecf20Sopenharmony_ci mmp->mmp_bdevname)); 4008c2ecf20Sopenharmony_ci if (IS_ERR(EXT4_SB(sb)->s_mmp_tsk)) { 4018c2ecf20Sopenharmony_ci EXT4_SB(sb)->s_mmp_tsk = NULL; 4028c2ecf20Sopenharmony_ci ext4_warning(sb, "Unable to create kmmpd thread for %s.", 4038c2ecf20Sopenharmony_ci sb->s_id); 4048c2ecf20Sopenharmony_ci retval = -ENOMEM; 4058c2ecf20Sopenharmony_ci goto failed; 4068c2ecf20Sopenharmony_ci } 4078c2ecf20Sopenharmony_ci 4088c2ecf20Sopenharmony_ci return 0; 4098c2ecf20Sopenharmony_ci 4108c2ecf20Sopenharmony_cifailed: 4118c2ecf20Sopenharmony_ci brelse(bh); 4128c2ecf20Sopenharmony_ci return retval; 4138c2ecf20Sopenharmony_ci} 414