3852 lines · 90.7 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0-only/* * Copyright (C) 2001, 2002 Sistina Software (UK) Limited. * Copyright (C) 2004-2008 Red Hat, Inc. All rights reserved. * * This file is released under the GPL. */ #include "dm-core.h"#include "dm-rq.h"#include "dm-uevent.h"#include "dm-ima.h" #include <linux/bio-integrity.h>#include <linux/init.h>#include <linux/module.h>#include <linux/mutex.h>#include <linux/sched/mm.h>#include <linux/sched/signal.h>#include <linux/blkpg.h>#include <linux/bio.h>#include <linux/mempool.h>#include <linux/dax.h>#include <linux/slab.h>#include <linux/idr.h>#include <linux/uio.h>#include <linux/hdreg.h>#include <linux/delay.h>#include <linux/wait.h>#include <linux/pr.h>#include <linux/refcount.h>#include <linux/part_stat.h>#include <linux/blk-crypto.h>#include <linux/blk-crypto-profile.h> #define DM_MSG_PREFIX "core" /* * Cookies are numeric values sent with CHANGE and REMOVE * uevents while resuming, removing or renaming the device. */#define DM_COOKIE_ENV_VAR_NAME "DM_COOKIE"#define DM_COOKIE_LENGTH 24 /* * For REQ_POLLED fs bio, this flag is set if we link mapped underlying * dm_io into one list, and reuse bio->bi_private as the list head. Before * ending this fs bio, we will recover its ->bi_private. */#define REQ_DM_POLL_LIST	REQ_DRV static const char *_name = DM_NAME; static unsigned int major;static unsigned int _major; static DEFINE_IDR(_minor_idr); static DEFINE_SPINLOCK(_minor_lock); static void do_deferred_remove(struct work_struct *w); static DECLARE_WORK(deferred_remove_work, do_deferred_remove); static struct workqueue_struct *deferred_remove_workqueue; atomic_t dm_global_event_nr = ATOMIC_INIT(0);DECLARE_WAIT_QUEUE_HEAD(dm_global_eventq); void dm_issue_global_event(void){	atomic_inc(&dm_global_event_nr);	wake_up(&dm_global_eventq);} DEFINE_STATIC_KEY_FALSE(stats_enabled);DEFINE_STATIC_KEY_FALSE(swap_bios_enabled);DEFINE_STATIC_KEY_FALSE(zoned_enabled); /* * One of these is allocated (on-stack) per original bio. */struct clone_info {	struct dm_table *map;	struct bio *bio;	struct dm_io *io;	sector_t sector;	unsigned int sector_count;	bool is_abnormal_io:1;	bool submit_as_polled:1;}; static inline struct dm_target_io *clone_to_tio(struct bio *clone){	return container_of(clone, struct dm_target_io, clone);} void *dm_per_bio_data(struct bio *bio, size_t data_size){	if (!dm_tio_flagged(clone_to_tio(bio), DM_TIO_INSIDE_DM_IO))		return (char *)bio - DM_TARGET_IO_BIO_OFFSET - data_size;	return (char *)bio - DM_IO_BIO_OFFSET - data_size;}EXPORT_SYMBOL_GPL(dm_per_bio_data); struct bio *dm_bio_from_per_bio_data(void *data, size_t data_size){	struct dm_io *io = (struct dm_io *)((char *)data + data_size); 	if (io->magic == DM_IO_MAGIC)		return (struct bio *)((char *)io + DM_IO_BIO_OFFSET);	BUG_ON(io->magic != DM_TIO_MAGIC);	return (struct bio *)((char *)io + DM_TARGET_IO_BIO_OFFSET);}EXPORT_SYMBOL_GPL(dm_bio_from_per_bio_data); unsigned int dm_bio_get_target_bio_nr(const struct bio *bio){	return container_of(bio, struct dm_target_io, clone)->target_bio_nr;}EXPORT_SYMBOL_GPL(dm_bio_get_target_bio_nr); #define MINOR_ALLOCED ((void *)-1) #define DM_NUMA_NODE NUMA_NO_NODEstatic int dm_numa_node = DM_NUMA_NODE; #define DEFAULT_SWAP_BIOS	(8 * 1048576 / PAGE_SIZE)static int swap_bios = DEFAULT_SWAP_BIOS;static int get_swap_bios(void){	int latch = READ_ONCE(swap_bios); 	if (unlikely(latch <= 0))		latch = DEFAULT_SWAP_BIOS;	return latch;} struct table_device {	struct list_head list;	refcount_t count;	struct dm_dev dm_dev;}; /* * Bio-based DM's mempools' reserved IOs set by the user. */#define RESERVED_BIO_BASED_IOS		16static unsigned int reserved_bio_based_ios = RESERVED_BIO_BASED_IOS; static int __dm_get_module_param_int(int *module_param, int min, int max){	int param = READ_ONCE(*module_param);	int modified_param = 0;	bool modified = true; 	if (param < min)		modified_param = min;	else if (param > max)		modified_param = max;	else		modified = false; 	if (modified) {		(void)cmpxchg(module_param, param, modified_param);		param = modified_param;	} 	return param;} unsigned int __dm_get_module_param(unsigned int *module_param, unsigned int def, unsigned int max){	unsigned int param = READ_ONCE(*module_param);	unsigned int modified_param = 0; 	if (!param)		modified_param = def;	else if (param > max)		modified_param = max; 	if (modified_param) {		(void)cmpxchg(module_param, param, modified_param);		param = modified_param;	} 	return param;} unsigned int dm_get_reserved_bio_based_ios(void){	return __dm_get_module_param(&reserved_bio_based_ios,				     RESERVED_BIO_BASED_IOS, DM_RESERVED_MAX_IOS);}EXPORT_SYMBOL_GPL(dm_get_reserved_bio_based_ios); static unsigned int dm_get_numa_node(void){	return __dm_get_module_param_int(&dm_numa_node,					 DM_NUMA_NODE, num_online_nodes() - 1);} static int __init local_init(void){	int r; 	r = dm_uevent_init();	if (r)		return r; 	deferred_remove_workqueue = alloc_ordered_workqueue("kdmremove", 0);	if (!deferred_remove_workqueue) {		r = -ENOMEM;		goto out_uevent_exit;	} 	_major = major;	r = register_blkdev(_major, _name);	if (r < 0)		goto out_free_workqueue; 	if (!_major)		_major = r; 	return 0; out_free_workqueue:	destroy_workqueue(deferred_remove_workqueue);out_uevent_exit:	dm_uevent_exit(); 	return r;} static void local_exit(void){	destroy_workqueue(deferred_remove_workqueue); 	unregister_blkdev(_major, _name);	dm_uevent_exit(); 	_major = 0; 	DMINFO("cleaned up");} static int (*_inits[])(void) __initdata = {	local_init,	dm_target_init,	dm_linear_init,	dm_stripe_init,	dm_io_init,	dm_kcopyd_init,	dm_interface_init,	dm_statistics_init,}; static void (*_exits[])(void) = {	local_exit,	dm_target_exit,	dm_linear_exit,	dm_stripe_exit,	dm_io_exit,	dm_kcopyd_exit,	dm_interface_exit,	dm_statistics_exit,}; static int __init dm_init(void){	const int count = ARRAY_SIZE(_inits);	int r, i; #if (IS_ENABLED(CONFIG_IMA) && !IS_ENABLED(CONFIG_IMA_DISABLE_HTABLE))	DMINFO("CONFIG_IMA_DISABLE_HTABLE is disabled."	       " Duplicate IMA measurements will not be recorded in the IMA log.");#endif 	for (i = 0; i < count; i++) {		r = _inits[i]();		if (r)			goto bad;	} 	return 0;bad:	while (i--)		_exits[i](); 	return r;} static void __exit dm_exit(void){	int i = ARRAY_SIZE(_exits); 	while (i--)		_exits[i](); 	/*	 * Should be empty by this point.	 */	idr_destroy(&_minor_idr);} /* * Block device functions */int dm_deleting_md(struct mapped_device *md){	return test_bit(DMF_DELETING, &md->flags);} static int dm_blk_open(struct gendisk *disk, blk_mode_t mode){	struct mapped_device *md; 	spin_lock(&_minor_lock); 	md = disk->private_data;	if (!md)		goto out; 	if (test_bit(DMF_FREEING, &md->flags) ||	    dm_deleting_md(md)) {		md = NULL;		goto out;	} 	dm_get(md);	atomic_inc(&md->open_count);out:	spin_unlock(&_minor_lock); 	return md ? 0 : -ENXIO;} static void dm_blk_close(struct gendisk *disk){	struct mapped_device *md; 	spin_lock(&_minor_lock); 	md = disk->private_data;	if (WARN_ON(!md))		goto out; 	if (atomic_dec_and_test(&md->open_count) &&	    (test_bit(DMF_DEFERRED_REMOVE, &md->flags)))		queue_work(deferred_remove_workqueue, &deferred_remove_work); 	dm_put(md);out:	spin_unlock(&_minor_lock);} int dm_open_count(struct mapped_device *md){	return atomic_read(&md->open_count);} /* * Guarantees nothing is using the device before it's deleted. */int dm_lock_for_deletion(struct mapped_device *md, bool mark_deferred, bool only_deferred){	int r = 0; 	spin_lock(&_minor_lock); 	if (dm_open_count(md)) {		r = -EBUSY;		if (mark_deferred)			set_bit(DMF_DEFERRED_REMOVE, &md->flags);	} else if (only_deferred && !test_bit(DMF_DEFERRED_REMOVE, &md->flags))		r = -EEXIST;	else		set_bit(DMF_DELETING, &md->flags); 	spin_unlock(&_minor_lock); 	return r;} int dm_cancel_deferred_remove(struct mapped_device *md){	int r = 0; 	spin_lock(&_minor_lock); 	if (test_bit(DMF_DELETING, &md->flags))		r = -EBUSY;	else		clear_bit(DMF_DEFERRED_REMOVE, &md->flags); 	spin_unlock(&_minor_lock); 	return r;} static void do_deferred_remove(struct work_struct *w){	dm_deferred_remove();} static int dm_blk_getgeo(struct gendisk *disk, struct hd_geometry *geo){	struct mapped_device *md = disk->private_data; 	return dm_get_geometry(md, geo);} static int dm_prepare_ioctl(struct mapped_device *md, int *srcu_idx,			    struct block_device **bdev, unsigned int cmd,			    unsigned long arg, bool *forward){	struct dm_target *ti;	struct dm_table *map;	int r; retry:	r = -ENOTTY;	map = dm_get_live_table(md, srcu_idx);	if (!map || !dm_table_get_size(map))		return r; 	/* We only support devices that have a single target */	if (map->num_targets != 1)		return r; 	ti = dm_table_get_target(map, 0);	if (!ti->type->prepare_ioctl)		return r; 	if (dm_suspended_md(md))		return -EAGAIN; 	r = ti->type->prepare_ioctl(ti, bdev, cmd, arg, forward);	if (r == -ENOTCONN && *forward && !fatal_signal_pending(current)) {		dm_put_live_table(md, *srcu_idx);		fsleep(10000);		goto retry;	} 	return r;} static void dm_unprepare_ioctl(struct mapped_device *md, int srcu_idx){	dm_put_live_table(md, srcu_idx);} static int dm_blk_ioctl(struct block_device *bdev, blk_mode_t mode,			unsigned int cmd, unsigned long arg){	struct mapped_device *md = bdev->bd_disk->private_data;	int r, srcu_idx;	bool forward = true; 	r = dm_prepare_ioctl(md, &srcu_idx, &bdev, cmd, arg, &forward);	if (!forward || r < 0)		goto out; 	if (r > 0) {		/*		 * Target determined this ioctl is being issued against a		 * subset of the parent bdev; require extra privileges.		 */		if (!capable(CAP_SYS_RAWIO)) {			DMDEBUG_LIMIT(	"%s: sending ioctl %x to DM device without required privilege.",				current->comm, cmd);			r = -ENOIOCTLCMD;			goto out;		}	} 	if (!bdev->bd_disk->fops->ioctl)		r = -ENOTTY;	else		r = bdev->bd_disk->fops->ioctl(bdev, mode, cmd, arg);out:	dm_unprepare_ioctl(md, srcu_idx);	return r;} u64 dm_start_time_ns_from_clone(struct bio *bio){	return jiffies_to_nsecs(clone_to_tio(bio)->io->start_time);}EXPORT_SYMBOL_GPL(dm_start_time_ns_from_clone); static inline unsigned int dm_io_sectors(struct dm_io *io, struct bio *bio){	/*	 * If REQ_PREFLUSH set, don't account payload, it will be	 * submitted (and accounted) after this flush completes.	 */	if (io->requeue_flush_with_data)		return 0;

Lines 1–500 of 3852.