diff options
| -rw-r--r-- | fs/ext4/ext4.h | 9 | ||||
| -rw-r--r-- | fs/ext4/ioctl.c | 55 | 
2 files changed, 64 insertions, 0 deletions
| diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h index 2b51cbee3907..a646bfcbd0e8 100644 --- a/fs/ext4/ext4.h +++ b/fs/ext4/ext4.h @@ -720,6 +720,7 @@ enum {  #define EXT4_IOC_CLEAR_ES_CACHE		_IO('f', 40)  #define EXT4_IOC_GETSTATE		_IOW('f', 41, __u32)  #define EXT4_IOC_GET_ES_CACHE		_IOWR('f', 42, struct fiemap) +#define EXT4_IOC_CHECKPOINT		_IOW('f', 43, __u32)  #define EXT4_IOC_SHUTDOWN _IOR ('X', 125, __u32) @@ -741,6 +742,14 @@ enum {  #define EXT4_STATE_FLAG_NEWENTRY	0x00000004  #define EXT4_STATE_FLAG_DA_ALLOC_CLOSE	0x00000008 +/* flags for ioctl EXT4_IOC_CHECKPOINT */ +#define EXT4_IOC_CHECKPOINT_FLAG_DISCARD	0x1 +#define EXT4_IOC_CHECKPOINT_FLAG_ZEROOUT	0x2 +#define EXT4_IOC_CHECKPOINT_FLAG_DRY_RUN	0x4 +#define EXT4_IOC_CHECKPOINT_FLAG_VALID		(EXT4_IOC_CHECKPOINT_FLAG_DISCARD | \ +						EXT4_IOC_CHECKPOINT_FLAG_ZEROOUT | \ +						EXT4_IOC_CHECKPOINT_FLAG_DRY_RUN) +  #if defined(__KERNEL__) && defined(CONFIG_COMPAT)  /*   * ioctl commands in 32 bit emulation diff --git a/fs/ext4/ioctl.c b/fs/ext4/ioctl.c index 93e9419825b8..5730aeca563c 100644 --- a/fs/ext4/ioctl.c +++ b/fs/ext4/ioctl.c @@ -799,6 +799,57 @@ static int ext4_ioctl_get_es_cache(struct file *filp, unsigned long arg)  	return error;  } +static int ext4_ioctl_checkpoint(struct file *filp, unsigned long arg) +{ +	int err = 0; +	__u32 flags = 0; +	unsigned int flush_flags = 0; +	struct super_block *sb = file_inode(filp)->i_sb; +	struct request_queue *q; + +	if (copy_from_user(&flags, (__u32 __user *)arg, +				sizeof(__u32))) +		return -EFAULT; + +	if (!capable(CAP_SYS_ADMIN)) +		return -EPERM; + +	/* check for invalid bits set */ +	if ((flags & ~EXT4_IOC_CHECKPOINT_FLAG_VALID) || +				((flags & JBD2_JOURNAL_FLUSH_DISCARD) && +				(flags & JBD2_JOURNAL_FLUSH_ZEROOUT))) +		return -EINVAL; + +	if (!EXT4_SB(sb)->s_journal) +		return -ENODEV; + +	if (flags & ~JBD2_JOURNAL_FLUSH_VALID) +		return -EINVAL; + +	q = bdev_get_queue(EXT4_SB(sb)->s_journal->j_dev); +	if (!q) +		return -ENXIO; +	if ((flags & JBD2_JOURNAL_FLUSH_DISCARD) && !blk_queue_discard(q)) +		return -EOPNOTSUPP; + +	if (flags & EXT4_IOC_CHECKPOINT_FLAG_DRY_RUN) +		return 0; + +	if (flags & EXT4_IOC_CHECKPOINT_FLAG_DISCARD) +		flush_flags |= JBD2_JOURNAL_FLUSH_DISCARD; + +	if (flags & EXT4_IOC_CHECKPOINT_FLAG_ZEROOUT) { +		flush_flags |= JBD2_JOURNAL_FLUSH_ZEROOUT; +		pr_info_ratelimited("warning: checkpointing journal with EXT4_IOC_CHECKPOINT_FLAG_ZEROOUT can be slow"); +	} + +	jbd2_journal_lock_updates(EXT4_SB(sb)->s_journal); +	err = jbd2_journal_flush(EXT4_SB(sb)->s_journal, flush_flags); +	jbd2_journal_unlock_updates(EXT4_SB(sb)->s_journal); + +	return err; +} +  static long __ext4_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)  {  	struct inode *inode = file_inode(filp); @@ -1210,6 +1261,9 @@ resizefs_out:  		return fsverity_ioctl_read_metadata(filp,  						    (const void __user *)arg); +	case EXT4_IOC_CHECKPOINT: +		return ext4_ioctl_checkpoint(filp, arg); +  	default:  		return -ENOTTY;  	} @@ -1290,6 +1344,7 @@ long ext4_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg)  	case EXT4_IOC_CLEAR_ES_CACHE:  	case EXT4_IOC_GETSTATE:  	case EXT4_IOC_GET_ES_CACHE: +	case EXT4_IOC_CHECKPOINT:  		break;  	default:  		return -ENOIOCTLCMD; | 
