brintos

brintos / linux-shallow public Read only

0
0
Text · 10.4 KiB · bd946d0 Raw
409 lines · c
1// SPDX-License-Identifier: GPL-2.02#include <linux/fs.h>3#include <linux/random.h>4#include <linux/buffer_head.h>5#include <linux/utsname.h>6#include <linux/kthread.h>7 8#include "ext4.h"9 10/* Checksumming functions */11static __le32 ext4_mmp_csum(struct super_block *sb, struct mmp_struct *mmp)12{13	struct ext4_sb_info *sbi = EXT4_SB(sb);14	int offset = offsetof(struct mmp_struct, mmp_checksum);15	__u32 csum;16 17	csum = ext4_chksum(sbi, sbi->s_csum_seed, (char *)mmp, offset);18 19	return cpu_to_le32(csum);20}21 22static int ext4_mmp_csum_verify(struct super_block *sb, struct mmp_struct *mmp)23{24	if (!ext4_has_metadata_csum(sb))25		return 1;26 27	return mmp->mmp_checksum == ext4_mmp_csum(sb, mmp);28}29 30static void ext4_mmp_csum_set(struct super_block *sb, struct mmp_struct *mmp)31{32	if (!ext4_has_metadata_csum(sb))33		return;34 35	mmp->mmp_checksum = ext4_mmp_csum(sb, mmp);36}37 38/*39 * Write the MMP block using REQ_SYNC to try to get the block on-disk40 * faster.41 */42static int write_mmp_block_thawed(struct super_block *sb,43				  struct buffer_head *bh)44{45	struct mmp_struct *mmp = (struct mmp_struct *)(bh->b_data);46 47	ext4_mmp_csum_set(sb, mmp);48	lock_buffer(bh);49	bh->b_end_io = end_buffer_write_sync;50	get_bh(bh);51	submit_bh(REQ_OP_WRITE | REQ_SYNC | REQ_META | REQ_PRIO, bh);52	wait_on_buffer(bh);53	if (unlikely(!buffer_uptodate(bh)))54		return -EIO;55	return 0;56}57 58static int write_mmp_block(struct super_block *sb, struct buffer_head *bh)59{60	int err;61 62	/*63	 * We protect against freezing so that we don't create dirty buffers64	 * on frozen filesystem.65	 */66	sb_start_write(sb);67	err = write_mmp_block_thawed(sb, bh);68	sb_end_write(sb);69	return err;70}71 72/*73 * Read the MMP block. It _must_ be read from disk and hence we clear the74 * uptodate flag on the buffer.75 */76static int read_mmp_block(struct super_block *sb, struct buffer_head **bh,77			  ext4_fsblk_t mmp_block)78{79	struct mmp_struct *mmp;80	int ret;81 82	if (*bh)83		clear_buffer_uptodate(*bh);84 85	/* This would be sb_bread(sb, mmp_block), except we need to be sure86	 * that the MD RAID device cache has been bypassed, and that the read87	 * is not blocked in the elevator. */88	if (!*bh) {89		*bh = sb_getblk(sb, mmp_block);90		if (!*bh) {91			ret = -ENOMEM;92			goto warn_exit;93		}94	}95 96	lock_buffer(*bh);97	ret = ext4_read_bh(*bh, REQ_META | REQ_PRIO, NULL);98	if (ret)99		goto warn_exit;100 101	mmp = (struct mmp_struct *)((*bh)->b_data);102	if (le32_to_cpu(mmp->mmp_magic) != EXT4_MMP_MAGIC) {103		ret = -EFSCORRUPTED;104		goto warn_exit;105	}106	if (!ext4_mmp_csum_verify(sb, mmp)) {107		ret = -EFSBADCRC;108		goto warn_exit;109	}110	return 0;111warn_exit:112	brelse(*bh);113	*bh = NULL;114	ext4_warning(sb, "Error %d while reading MMP block %llu",115		     ret, mmp_block);116	return ret;117}118 119/*120 * Dump as much information as possible to help the admin.121 */122void __dump_mmp_msg(struct super_block *sb, struct mmp_struct *mmp,123		    const char *function, unsigned int line, const char *msg)124{125	__ext4_warning(sb, function, line, "%s", msg);126	__ext4_warning(sb, function, line,127		       "MMP failure info: last update time: %llu, last update node: %.*s, last update device: %.*s",128		       (unsigned long long)le64_to_cpu(mmp->mmp_time),129		       (int)sizeof(mmp->mmp_nodename), mmp->mmp_nodename,130		       (int)sizeof(mmp->mmp_bdevname), mmp->mmp_bdevname);131}132 133/*134 * kmmpd will update the MMP sequence every s_mmp_update_interval seconds135 */136static int kmmpd(void *data)137{138	struct super_block *sb = data;139	struct ext4_super_block *es = EXT4_SB(sb)->s_es;140	struct buffer_head *bh = EXT4_SB(sb)->s_mmp_bh;141	struct mmp_struct *mmp;142	ext4_fsblk_t mmp_block;143	u32 seq = 0;144	unsigned long failed_writes = 0;145	int mmp_update_interval = le16_to_cpu(es->s_mmp_update_interval);146	unsigned mmp_check_interval;147	unsigned long last_update_time;148	unsigned long diff;149	int retval = 0;150 151	mmp_block = le64_to_cpu(es->s_mmp_block);152	mmp = (struct mmp_struct *)(bh->b_data);153	mmp->mmp_time = cpu_to_le64(ktime_get_real_seconds());154	/*155	 * Start with the higher mmp_check_interval and reduce it if156	 * the MMP block is being updated on time.157	 */158	mmp_check_interval = max(EXT4_MMP_CHECK_MULT * mmp_update_interval,159				 EXT4_MMP_MIN_CHECK_INTERVAL);160	mmp->mmp_check_interval = cpu_to_le16(mmp_check_interval);161 162	memcpy(mmp->mmp_nodename, init_utsname()->nodename,163	       sizeof(mmp->mmp_nodename));164 165	while (!kthread_should_stop() && !ext4_forced_shutdown(sb)) {166		if (!ext4_has_feature_mmp(sb)) {167			ext4_warning(sb, "kmmpd being stopped since MMP feature"168				     " has been disabled.");169			goto wait_to_exit;170		}171		if (++seq > EXT4_MMP_SEQ_MAX)172			seq = 1;173 174		mmp->mmp_seq = cpu_to_le32(seq);175		mmp->mmp_time = cpu_to_le64(ktime_get_real_seconds());176		last_update_time = jiffies;177 178		retval = write_mmp_block(sb, bh);179		/*180		 * Don't spew too many error messages. Print one every181		 * (s_mmp_update_interval * 60) seconds.182		 */183		if (retval) {184			if ((failed_writes % 60) == 0) {185				ext4_error_err(sb, -retval,186					       "Error writing to MMP block");187			}188			failed_writes++;189		}190 191		diff = jiffies - last_update_time;192		if (diff < mmp_update_interval * HZ)193			schedule_timeout_interruptible(mmp_update_interval *194						       HZ - diff);195 196		/*197		 * We need to make sure that more than mmp_check_interval198		 * seconds have not passed since writing. If that has happened199		 * we need to check if the MMP block is as we left it.200		 */201		diff = jiffies - last_update_time;202		if (diff > mmp_check_interval * HZ) {203			struct buffer_head *bh_check = NULL;204			struct mmp_struct *mmp_check;205 206			retval = read_mmp_block(sb, &bh_check, mmp_block);207			if (retval) {208				ext4_error_err(sb, -retval,209					       "error reading MMP data: %d",210					       retval);211				goto wait_to_exit;212			}213 214			mmp_check = (struct mmp_struct *)(bh_check->b_data);215			if (mmp->mmp_seq != mmp_check->mmp_seq ||216			    memcmp(mmp->mmp_nodename, mmp_check->mmp_nodename,217				   sizeof(mmp->mmp_nodename))) {218				dump_mmp_msg(sb, mmp_check,219					     "Error while updating MMP info. "220					     "The filesystem seems to have been"221					     " multiply mounted.");222				ext4_error_err(sb, EBUSY, "abort");223				put_bh(bh_check);224				retval = -EBUSY;225				goto wait_to_exit;226			}227			put_bh(bh_check);228		}229 230		 /*231		 * Adjust the mmp_check_interval depending on how much time232		 * it took for the MMP block to be written.233		 */234		mmp_check_interval = max(min(EXT4_MMP_CHECK_MULT * diff / HZ,235					     EXT4_MMP_MAX_CHECK_INTERVAL),236					 EXT4_MMP_MIN_CHECK_INTERVAL);237		mmp->mmp_check_interval = cpu_to_le16(mmp_check_interval);238	}239 240	/*241	 * Unmount seems to be clean.242	 */243	mmp->mmp_seq = cpu_to_le32(EXT4_MMP_SEQ_CLEAN);244	mmp->mmp_time = cpu_to_le64(ktime_get_real_seconds());245 246	retval = write_mmp_block(sb, bh);247 248wait_to_exit:249	while (!kthread_should_stop()) {250		set_current_state(TASK_INTERRUPTIBLE);251		if (!kthread_should_stop())252			schedule();253	}254	set_current_state(TASK_RUNNING);255	return retval;256}257 258void ext4_stop_mmpd(struct ext4_sb_info *sbi)259{260	if (sbi->s_mmp_tsk) {261		kthread_stop(sbi->s_mmp_tsk);262		brelse(sbi->s_mmp_bh);263		sbi->s_mmp_tsk = NULL;264	}265}266 267/*268 * Get a random new sequence number but make sure it is not greater than269 * EXT4_MMP_SEQ_MAX.270 */271static unsigned int mmp_new_seq(void)272{273	return get_random_u32_below(EXT4_MMP_SEQ_MAX + 1);274}275 276/*277 * Protect the filesystem from being mounted more than once.278 */279int ext4_multi_mount_protect(struct super_block *sb,280				    ext4_fsblk_t mmp_block)281{282	struct ext4_super_block *es = EXT4_SB(sb)->s_es;283	struct buffer_head *bh = NULL;284	struct mmp_struct *mmp = NULL;285	u32 seq;286	unsigned int mmp_check_interval = le16_to_cpu(es->s_mmp_update_interval);287	unsigned int wait_time = 0;288	int retval;289 290	if (mmp_block < le32_to_cpu(es->s_first_data_block) ||291	    mmp_block >= ext4_blocks_count(es)) {292		ext4_warning(sb, "Invalid MMP block in superblock");293		retval = -EINVAL;294		goto failed;295	}296 297	retval = read_mmp_block(sb, &bh, mmp_block);298	if (retval)299		goto failed;300 301	mmp = (struct mmp_struct *)(bh->b_data);302 303	if (mmp_check_interval < EXT4_MMP_MIN_CHECK_INTERVAL)304		mmp_check_interval = EXT4_MMP_MIN_CHECK_INTERVAL;305 306	/*307	 * If check_interval in MMP block is larger, use that instead of308	 * update_interval from the superblock.309	 */310	if (le16_to_cpu(mmp->mmp_check_interval) > mmp_check_interval)311		mmp_check_interval = le16_to_cpu(mmp->mmp_check_interval);312 313	seq = le32_to_cpu(mmp->mmp_seq);314	if (seq == EXT4_MMP_SEQ_CLEAN)315		goto skip;316 317	if (seq == EXT4_MMP_SEQ_FSCK) {318		dump_mmp_msg(sb, mmp, "fsck is running on the filesystem");319		retval = -EBUSY;320		goto failed;321	}322 323	wait_time = min(mmp_check_interval * 2 + 1,324			mmp_check_interval + 60);325 326	/* Print MMP interval if more than 20 secs. */327	if (wait_time > EXT4_MMP_MIN_CHECK_INTERVAL * 4)328		ext4_warning(sb, "MMP interval %u higher than expected, please"329			     " wait.\n", wait_time * 2);330 331	if (schedule_timeout_interruptible(HZ * wait_time) != 0) {332		ext4_warning(sb, "MMP startup interrupted, failing mount\n");333		retval = -ETIMEDOUT;334		goto failed;335	}336 337	retval = read_mmp_block(sb, &bh, mmp_block);338	if (retval)339		goto failed;340	mmp = (struct mmp_struct *)(bh->b_data);341	if (seq != le32_to_cpu(mmp->mmp_seq)) {342		dump_mmp_msg(sb, mmp,343			     "Device is already active on another node.");344		retval = -EBUSY;345		goto failed;346	}347 348skip:349	/*350	 * write a new random sequence number.351	 */352	seq = mmp_new_seq();353	mmp->mmp_seq = cpu_to_le32(seq);354 355	/*356	 * On mount / remount we are protected against fs freezing (by s_umount357	 * semaphore) and grabbing freeze protection upsets lockdep358	 */359	retval = write_mmp_block_thawed(sb, bh);360	if (retval)361		goto failed;362 363	/*364	 * wait for MMP interval and check mmp_seq.365	 */366	if (schedule_timeout_interruptible(HZ * wait_time) != 0) {367		ext4_warning(sb, "MMP startup interrupted, failing mount");368		retval = -ETIMEDOUT;369		goto failed;370	}371 372	retval = read_mmp_block(sb, &bh, mmp_block);373	if (retval)374		goto failed;375	mmp = (struct mmp_struct *)(bh->b_data);376	if (seq != le32_to_cpu(mmp->mmp_seq)) {377		dump_mmp_msg(sb, mmp,378			     "Device is already active on another node.");379		retval = -EBUSY;380		goto failed;381	}382 383	EXT4_SB(sb)->s_mmp_bh = bh;384 385	BUILD_BUG_ON(sizeof(mmp->mmp_bdevname) < BDEVNAME_SIZE);386	snprintf(mmp->mmp_bdevname, sizeof(mmp->mmp_bdevname),387		 "%pg", bh->b_bdev);388 389	/*390	 * Start a kernel thread to update the MMP block periodically.391	 */392	EXT4_SB(sb)->s_mmp_tsk = kthread_run(kmmpd, sb, "kmmpd-%.*s",393					     (int)sizeof(mmp->mmp_bdevname),394					     mmp->mmp_bdevname);395	if (IS_ERR(EXT4_SB(sb)->s_mmp_tsk)) {396		EXT4_SB(sb)->s_mmp_tsk = NULL;397		ext4_warning(sb, "Unable to create kmmpd thread for %s.",398			     sb->s_id);399		retval = -ENOMEM;400		goto failed;401	}402 403	return 0;404 405failed:406	brelse(bh);407	return retval;408}409