162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Add configfs and memory store: Kyungchan Koh <kkc6196@fb.com> and 462306a36Sopenharmony_ci * Shaohua Li <shli@fb.com> 562306a36Sopenharmony_ci */ 662306a36Sopenharmony_ci#include <linux/module.h> 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <linux/moduleparam.h> 962306a36Sopenharmony_ci#include <linux/sched.h> 1062306a36Sopenharmony_ci#include <linux/fs.h> 1162306a36Sopenharmony_ci#include <linux/init.h> 1262306a36Sopenharmony_ci#include "null_blk.h" 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ci#undef pr_fmt 1562306a36Sopenharmony_ci#define pr_fmt(fmt) "null_blk: " fmt 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci#define FREE_BATCH 16 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci#define TICKS_PER_SEC 50ULL 2062306a36Sopenharmony_ci#define TIMER_INTERVAL (NSEC_PER_SEC / TICKS_PER_SEC) 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci#ifdef CONFIG_BLK_DEV_NULL_BLK_FAULT_INJECTION 2362306a36Sopenharmony_cistatic DECLARE_FAULT_ATTR(null_timeout_attr); 2462306a36Sopenharmony_cistatic DECLARE_FAULT_ATTR(null_requeue_attr); 2562306a36Sopenharmony_cistatic DECLARE_FAULT_ATTR(null_init_hctx_attr); 2662306a36Sopenharmony_ci#endif 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_cistatic inline u64 mb_per_tick(int mbps) 2962306a36Sopenharmony_ci{ 3062306a36Sopenharmony_ci return (1 << 20) / TICKS_PER_SEC * ((u64) mbps); 3162306a36Sopenharmony_ci} 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci/* 3462306a36Sopenharmony_ci * Status flags for nullb_device. 3562306a36Sopenharmony_ci * 3662306a36Sopenharmony_ci * CONFIGURED: Device has been configured and turned on. Cannot reconfigure. 3762306a36Sopenharmony_ci * UP: Device is currently on and visible in userspace. 3862306a36Sopenharmony_ci * THROTTLED: Device is being throttled. 3962306a36Sopenharmony_ci * CACHE: Device is using a write-back cache. 4062306a36Sopenharmony_ci */ 4162306a36Sopenharmony_cienum nullb_device_flags { 4262306a36Sopenharmony_ci NULLB_DEV_FL_CONFIGURED = 0, 4362306a36Sopenharmony_ci NULLB_DEV_FL_UP = 1, 4462306a36Sopenharmony_ci NULLB_DEV_FL_THROTTLED = 2, 4562306a36Sopenharmony_ci NULLB_DEV_FL_CACHE = 3, 4662306a36Sopenharmony_ci}; 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci#define MAP_SZ ((PAGE_SIZE >> SECTOR_SHIFT) + 2) 4962306a36Sopenharmony_ci/* 5062306a36Sopenharmony_ci * nullb_page is a page in memory for nullb devices. 5162306a36Sopenharmony_ci * 5262306a36Sopenharmony_ci * @page: The page holding the data. 5362306a36Sopenharmony_ci * @bitmap: The bitmap represents which sector in the page has data. 5462306a36Sopenharmony_ci * Each bit represents one block size. For example, sector 8 5562306a36Sopenharmony_ci * will use the 7th bit 5662306a36Sopenharmony_ci * The highest 2 bits of bitmap are for special purpose. LOCK means the cache 5762306a36Sopenharmony_ci * page is being flushing to storage. FREE means the cache page is freed and 5862306a36Sopenharmony_ci * should be skipped from flushing to storage. Please see 5962306a36Sopenharmony_ci * null_make_cache_space 6062306a36Sopenharmony_ci */ 6162306a36Sopenharmony_cistruct nullb_page { 6262306a36Sopenharmony_ci struct page *page; 6362306a36Sopenharmony_ci DECLARE_BITMAP(bitmap, MAP_SZ); 6462306a36Sopenharmony_ci}; 6562306a36Sopenharmony_ci#define NULLB_PAGE_LOCK (MAP_SZ - 1) 6662306a36Sopenharmony_ci#define NULLB_PAGE_FREE (MAP_SZ - 2) 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_cistatic LIST_HEAD(nullb_list); 6962306a36Sopenharmony_cistatic struct mutex lock; 7062306a36Sopenharmony_cistatic int null_major; 7162306a36Sopenharmony_cistatic DEFINE_IDA(nullb_indexes); 7262306a36Sopenharmony_cistatic struct blk_mq_tag_set tag_set; 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_cienum { 7562306a36Sopenharmony_ci NULL_IRQ_NONE = 0, 7662306a36Sopenharmony_ci NULL_IRQ_SOFTIRQ = 1, 7762306a36Sopenharmony_ci NULL_IRQ_TIMER = 2, 7862306a36Sopenharmony_ci}; 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_cistatic bool g_virt_boundary = false; 8162306a36Sopenharmony_cimodule_param_named(virt_boundary, g_virt_boundary, bool, 0444); 8262306a36Sopenharmony_ciMODULE_PARM_DESC(virt_boundary, "Require a virtual boundary for the device. Default: False"); 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_cistatic int g_no_sched; 8562306a36Sopenharmony_cimodule_param_named(no_sched, g_no_sched, int, 0444); 8662306a36Sopenharmony_ciMODULE_PARM_DESC(no_sched, "No io scheduler"); 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_cistatic int g_submit_queues = 1; 8962306a36Sopenharmony_cimodule_param_named(submit_queues, g_submit_queues, int, 0444); 9062306a36Sopenharmony_ciMODULE_PARM_DESC(submit_queues, "Number of submission queues"); 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_cistatic int g_poll_queues = 1; 9362306a36Sopenharmony_cimodule_param_named(poll_queues, g_poll_queues, int, 0444); 9462306a36Sopenharmony_ciMODULE_PARM_DESC(poll_queues, "Number of IOPOLL submission queues"); 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_cistatic int g_home_node = NUMA_NO_NODE; 9762306a36Sopenharmony_cimodule_param_named(home_node, g_home_node, int, 0444); 9862306a36Sopenharmony_ciMODULE_PARM_DESC(home_node, "Home node for the device"); 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci#ifdef CONFIG_BLK_DEV_NULL_BLK_FAULT_INJECTION 10162306a36Sopenharmony_ci/* 10262306a36Sopenharmony_ci * For more details about fault injection, please refer to 10362306a36Sopenharmony_ci * Documentation/fault-injection/fault-injection.rst. 10462306a36Sopenharmony_ci */ 10562306a36Sopenharmony_cistatic char g_timeout_str[80]; 10662306a36Sopenharmony_cimodule_param_string(timeout, g_timeout_str, sizeof(g_timeout_str), 0444); 10762306a36Sopenharmony_ciMODULE_PARM_DESC(timeout, "Fault injection. timeout=<interval>,<probability>,<space>,<times>"); 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_cistatic char g_requeue_str[80]; 11062306a36Sopenharmony_cimodule_param_string(requeue, g_requeue_str, sizeof(g_requeue_str), 0444); 11162306a36Sopenharmony_ciMODULE_PARM_DESC(requeue, "Fault injection. requeue=<interval>,<probability>,<space>,<times>"); 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_cistatic char g_init_hctx_str[80]; 11462306a36Sopenharmony_cimodule_param_string(init_hctx, g_init_hctx_str, sizeof(g_init_hctx_str), 0444); 11562306a36Sopenharmony_ciMODULE_PARM_DESC(init_hctx, "Fault injection to fail hctx init. init_hctx=<interval>,<probability>,<space>,<times>"); 11662306a36Sopenharmony_ci#endif 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_cistatic int g_queue_mode = NULL_Q_MQ; 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_cistatic int null_param_store_val(const char *str, int *val, int min, int max) 12162306a36Sopenharmony_ci{ 12262306a36Sopenharmony_ci int ret, new_val; 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci ret = kstrtoint(str, 10, &new_val); 12562306a36Sopenharmony_ci if (ret) 12662306a36Sopenharmony_ci return -EINVAL; 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci if (new_val < min || new_val > max) 12962306a36Sopenharmony_ci return -EINVAL; 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci *val = new_val; 13262306a36Sopenharmony_ci return 0; 13362306a36Sopenharmony_ci} 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_cistatic int null_set_queue_mode(const char *str, const struct kernel_param *kp) 13662306a36Sopenharmony_ci{ 13762306a36Sopenharmony_ci return null_param_store_val(str, &g_queue_mode, NULL_Q_BIO, NULL_Q_MQ); 13862306a36Sopenharmony_ci} 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_cistatic const struct kernel_param_ops null_queue_mode_param_ops = { 14162306a36Sopenharmony_ci .set = null_set_queue_mode, 14262306a36Sopenharmony_ci .get = param_get_int, 14362306a36Sopenharmony_ci}; 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_cidevice_param_cb(queue_mode, &null_queue_mode_param_ops, &g_queue_mode, 0444); 14662306a36Sopenharmony_ciMODULE_PARM_DESC(queue_mode, "Block interface to use (0=bio,1=rq,2=multiqueue)"); 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_cistatic int g_gb = 250; 14962306a36Sopenharmony_cimodule_param_named(gb, g_gb, int, 0444); 15062306a36Sopenharmony_ciMODULE_PARM_DESC(gb, "Size in GB"); 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_cistatic int g_bs = 512; 15362306a36Sopenharmony_cimodule_param_named(bs, g_bs, int, 0444); 15462306a36Sopenharmony_ciMODULE_PARM_DESC(bs, "Block size (in bytes)"); 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_cistatic int g_max_sectors; 15762306a36Sopenharmony_cimodule_param_named(max_sectors, g_max_sectors, int, 0444); 15862306a36Sopenharmony_ciMODULE_PARM_DESC(max_sectors, "Maximum size of a command (in 512B sectors)"); 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_cistatic unsigned int nr_devices = 1; 16162306a36Sopenharmony_cimodule_param(nr_devices, uint, 0444); 16262306a36Sopenharmony_ciMODULE_PARM_DESC(nr_devices, "Number of devices to register"); 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_cistatic bool g_blocking; 16562306a36Sopenharmony_cimodule_param_named(blocking, g_blocking, bool, 0444); 16662306a36Sopenharmony_ciMODULE_PARM_DESC(blocking, "Register as a blocking blk-mq driver device"); 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_cistatic bool shared_tags; 16962306a36Sopenharmony_cimodule_param(shared_tags, bool, 0444); 17062306a36Sopenharmony_ciMODULE_PARM_DESC(shared_tags, "Share tag set between devices for blk-mq"); 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_cistatic bool g_shared_tag_bitmap; 17362306a36Sopenharmony_cimodule_param_named(shared_tag_bitmap, g_shared_tag_bitmap, bool, 0444); 17462306a36Sopenharmony_ciMODULE_PARM_DESC(shared_tag_bitmap, "Use shared tag bitmap for all submission queues for blk-mq"); 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_cistatic int g_irqmode = NULL_IRQ_SOFTIRQ; 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_cistatic int null_set_irqmode(const char *str, const struct kernel_param *kp) 17962306a36Sopenharmony_ci{ 18062306a36Sopenharmony_ci return null_param_store_val(str, &g_irqmode, NULL_IRQ_NONE, 18162306a36Sopenharmony_ci NULL_IRQ_TIMER); 18262306a36Sopenharmony_ci} 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_cistatic const struct kernel_param_ops null_irqmode_param_ops = { 18562306a36Sopenharmony_ci .set = null_set_irqmode, 18662306a36Sopenharmony_ci .get = param_get_int, 18762306a36Sopenharmony_ci}; 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_cidevice_param_cb(irqmode, &null_irqmode_param_ops, &g_irqmode, 0444); 19062306a36Sopenharmony_ciMODULE_PARM_DESC(irqmode, "IRQ completion handler. 0-none, 1-softirq, 2-timer"); 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_cistatic unsigned long g_completion_nsec = 10000; 19362306a36Sopenharmony_cimodule_param_named(completion_nsec, g_completion_nsec, ulong, 0444); 19462306a36Sopenharmony_ciMODULE_PARM_DESC(completion_nsec, "Time in ns to complete a request in hardware. Default: 10,000ns"); 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_cistatic int g_hw_queue_depth = 64; 19762306a36Sopenharmony_cimodule_param_named(hw_queue_depth, g_hw_queue_depth, int, 0444); 19862306a36Sopenharmony_ciMODULE_PARM_DESC(hw_queue_depth, "Queue depth for each hardware queue. Default: 64"); 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_cistatic bool g_use_per_node_hctx; 20162306a36Sopenharmony_cimodule_param_named(use_per_node_hctx, g_use_per_node_hctx, bool, 0444); 20262306a36Sopenharmony_ciMODULE_PARM_DESC(use_per_node_hctx, "Use per-node allocation for hardware context queues. Default: false"); 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_cistatic bool g_memory_backed; 20562306a36Sopenharmony_cimodule_param_named(memory_backed, g_memory_backed, bool, 0444); 20662306a36Sopenharmony_ciMODULE_PARM_DESC(memory_backed, "Create a memory-backed block device. Default: false"); 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_cistatic bool g_discard; 20962306a36Sopenharmony_cimodule_param_named(discard, g_discard, bool, 0444); 21062306a36Sopenharmony_ciMODULE_PARM_DESC(discard, "Support discard operations (requires memory-backed null_blk device). Default: false"); 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_cistatic unsigned long g_cache_size; 21362306a36Sopenharmony_cimodule_param_named(cache_size, g_cache_size, ulong, 0444); 21462306a36Sopenharmony_ciMODULE_PARM_DESC(mbps, "Cache size in MiB for memory-backed device. Default: 0 (none)"); 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_cistatic unsigned int g_mbps; 21762306a36Sopenharmony_cimodule_param_named(mbps, g_mbps, uint, 0444); 21862306a36Sopenharmony_ciMODULE_PARM_DESC(mbps, "Limit maximum bandwidth (in MiB/s). Default: 0 (no limit)"); 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_cistatic bool g_zoned; 22162306a36Sopenharmony_cimodule_param_named(zoned, g_zoned, bool, S_IRUGO); 22262306a36Sopenharmony_ciMODULE_PARM_DESC(zoned, "Make device as a host-managed zoned block device. Default: false"); 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_cistatic unsigned long g_zone_size = 256; 22562306a36Sopenharmony_cimodule_param_named(zone_size, g_zone_size, ulong, S_IRUGO); 22662306a36Sopenharmony_ciMODULE_PARM_DESC(zone_size, "Zone size in MB when block device is zoned. Must be power-of-two: Default: 256"); 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_cistatic unsigned long g_zone_capacity; 22962306a36Sopenharmony_cimodule_param_named(zone_capacity, g_zone_capacity, ulong, 0444); 23062306a36Sopenharmony_ciMODULE_PARM_DESC(zone_capacity, "Zone capacity in MB when block device is zoned. Can be less than or equal to zone size. Default: Zone size"); 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_cistatic unsigned int g_zone_nr_conv; 23362306a36Sopenharmony_cimodule_param_named(zone_nr_conv, g_zone_nr_conv, uint, 0444); 23462306a36Sopenharmony_ciMODULE_PARM_DESC(zone_nr_conv, "Number of conventional zones when block device is zoned. Default: 0"); 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_cistatic unsigned int g_zone_max_open; 23762306a36Sopenharmony_cimodule_param_named(zone_max_open, g_zone_max_open, uint, 0444); 23862306a36Sopenharmony_ciMODULE_PARM_DESC(zone_max_open, "Maximum number of open zones when block device is zoned. Default: 0 (no limit)"); 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_cistatic unsigned int g_zone_max_active; 24162306a36Sopenharmony_cimodule_param_named(zone_max_active, g_zone_max_active, uint, 0444); 24262306a36Sopenharmony_ciMODULE_PARM_DESC(zone_max_active, "Maximum number of active zones when block device is zoned. Default: 0 (no limit)"); 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_cistatic struct nullb_device *null_alloc_dev(void); 24562306a36Sopenharmony_cistatic void null_free_dev(struct nullb_device *dev); 24662306a36Sopenharmony_cistatic void null_del_dev(struct nullb *nullb); 24762306a36Sopenharmony_cistatic int null_add_dev(struct nullb_device *dev); 24862306a36Sopenharmony_cistatic struct nullb *null_find_dev_by_name(const char *name); 24962306a36Sopenharmony_cistatic void null_free_device_storage(struct nullb_device *dev, bool is_cache); 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_cistatic inline struct nullb_device *to_nullb_device(struct config_item *item) 25262306a36Sopenharmony_ci{ 25362306a36Sopenharmony_ci return item ? container_of(to_config_group(item), struct nullb_device, group) : NULL; 25462306a36Sopenharmony_ci} 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_cistatic inline ssize_t nullb_device_uint_attr_show(unsigned int val, char *page) 25762306a36Sopenharmony_ci{ 25862306a36Sopenharmony_ci return snprintf(page, PAGE_SIZE, "%u\n", val); 25962306a36Sopenharmony_ci} 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_cistatic inline ssize_t nullb_device_ulong_attr_show(unsigned long val, 26262306a36Sopenharmony_ci char *page) 26362306a36Sopenharmony_ci{ 26462306a36Sopenharmony_ci return snprintf(page, PAGE_SIZE, "%lu\n", val); 26562306a36Sopenharmony_ci} 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_cistatic inline ssize_t nullb_device_bool_attr_show(bool val, char *page) 26862306a36Sopenharmony_ci{ 26962306a36Sopenharmony_ci return snprintf(page, PAGE_SIZE, "%u\n", val); 27062306a36Sopenharmony_ci} 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_cistatic ssize_t nullb_device_uint_attr_store(unsigned int *val, 27362306a36Sopenharmony_ci const char *page, size_t count) 27462306a36Sopenharmony_ci{ 27562306a36Sopenharmony_ci unsigned int tmp; 27662306a36Sopenharmony_ci int result; 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ci result = kstrtouint(page, 0, &tmp); 27962306a36Sopenharmony_ci if (result < 0) 28062306a36Sopenharmony_ci return result; 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_ci *val = tmp; 28362306a36Sopenharmony_ci return count; 28462306a36Sopenharmony_ci} 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_cistatic ssize_t nullb_device_ulong_attr_store(unsigned long *val, 28762306a36Sopenharmony_ci const char *page, size_t count) 28862306a36Sopenharmony_ci{ 28962306a36Sopenharmony_ci int result; 29062306a36Sopenharmony_ci unsigned long tmp; 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_ci result = kstrtoul(page, 0, &tmp); 29362306a36Sopenharmony_ci if (result < 0) 29462306a36Sopenharmony_ci return result; 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_ci *val = tmp; 29762306a36Sopenharmony_ci return count; 29862306a36Sopenharmony_ci} 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_cistatic ssize_t nullb_device_bool_attr_store(bool *val, const char *page, 30162306a36Sopenharmony_ci size_t count) 30262306a36Sopenharmony_ci{ 30362306a36Sopenharmony_ci bool tmp; 30462306a36Sopenharmony_ci int result; 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ci result = kstrtobool(page, &tmp); 30762306a36Sopenharmony_ci if (result < 0) 30862306a36Sopenharmony_ci return result; 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_ci *val = tmp; 31162306a36Sopenharmony_ci return count; 31262306a36Sopenharmony_ci} 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_ci/* The following macro should only be used with TYPE = {uint, ulong, bool}. */ 31562306a36Sopenharmony_ci#define NULLB_DEVICE_ATTR(NAME, TYPE, APPLY) \ 31662306a36Sopenharmony_cistatic ssize_t \ 31762306a36Sopenharmony_cinullb_device_##NAME##_show(struct config_item *item, char *page) \ 31862306a36Sopenharmony_ci{ \ 31962306a36Sopenharmony_ci return nullb_device_##TYPE##_attr_show( \ 32062306a36Sopenharmony_ci to_nullb_device(item)->NAME, page); \ 32162306a36Sopenharmony_ci} \ 32262306a36Sopenharmony_cistatic ssize_t \ 32362306a36Sopenharmony_cinullb_device_##NAME##_store(struct config_item *item, const char *page, \ 32462306a36Sopenharmony_ci size_t count) \ 32562306a36Sopenharmony_ci{ \ 32662306a36Sopenharmony_ci int (*apply_fn)(struct nullb_device *dev, TYPE new_value) = APPLY;\ 32762306a36Sopenharmony_ci struct nullb_device *dev = to_nullb_device(item); \ 32862306a36Sopenharmony_ci TYPE new_value = 0; \ 32962306a36Sopenharmony_ci int ret; \ 33062306a36Sopenharmony_ci \ 33162306a36Sopenharmony_ci ret = nullb_device_##TYPE##_attr_store(&new_value, page, count);\ 33262306a36Sopenharmony_ci if (ret < 0) \ 33362306a36Sopenharmony_ci return ret; \ 33462306a36Sopenharmony_ci if (apply_fn) \ 33562306a36Sopenharmony_ci ret = apply_fn(dev, new_value); \ 33662306a36Sopenharmony_ci else if (test_bit(NULLB_DEV_FL_CONFIGURED, &dev->flags)) \ 33762306a36Sopenharmony_ci ret = -EBUSY; \ 33862306a36Sopenharmony_ci if (ret < 0) \ 33962306a36Sopenharmony_ci return ret; \ 34062306a36Sopenharmony_ci dev->NAME = new_value; \ 34162306a36Sopenharmony_ci return count; \ 34262306a36Sopenharmony_ci} \ 34362306a36Sopenharmony_ciCONFIGFS_ATTR(nullb_device_, NAME); 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_cistatic int nullb_update_nr_hw_queues(struct nullb_device *dev, 34662306a36Sopenharmony_ci unsigned int submit_queues, 34762306a36Sopenharmony_ci unsigned int poll_queues) 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_ci{ 35062306a36Sopenharmony_ci struct blk_mq_tag_set *set; 35162306a36Sopenharmony_ci int ret, nr_hw_queues; 35262306a36Sopenharmony_ci 35362306a36Sopenharmony_ci if (!dev->nullb) 35462306a36Sopenharmony_ci return 0; 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_ci /* 35762306a36Sopenharmony_ci * Make sure at least one submit queue exists. 35862306a36Sopenharmony_ci */ 35962306a36Sopenharmony_ci if (!submit_queues) 36062306a36Sopenharmony_ci return -EINVAL; 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_ci /* 36362306a36Sopenharmony_ci * Make sure that null_init_hctx() does not access nullb->queues[] past 36462306a36Sopenharmony_ci * the end of that array. 36562306a36Sopenharmony_ci */ 36662306a36Sopenharmony_ci if (submit_queues > nr_cpu_ids || poll_queues > g_poll_queues) 36762306a36Sopenharmony_ci return -EINVAL; 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_ci /* 37062306a36Sopenharmony_ci * Keep previous and new queue numbers in nullb_device for reference in 37162306a36Sopenharmony_ci * the call back function null_map_queues(). 37262306a36Sopenharmony_ci */ 37362306a36Sopenharmony_ci dev->prev_submit_queues = dev->submit_queues; 37462306a36Sopenharmony_ci dev->prev_poll_queues = dev->poll_queues; 37562306a36Sopenharmony_ci dev->submit_queues = submit_queues; 37662306a36Sopenharmony_ci dev->poll_queues = poll_queues; 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_ci set = dev->nullb->tag_set; 37962306a36Sopenharmony_ci nr_hw_queues = submit_queues + poll_queues; 38062306a36Sopenharmony_ci blk_mq_update_nr_hw_queues(set, nr_hw_queues); 38162306a36Sopenharmony_ci ret = set->nr_hw_queues == nr_hw_queues ? 0 : -ENOMEM; 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_ci if (ret) { 38462306a36Sopenharmony_ci /* on error, revert the queue numbers */ 38562306a36Sopenharmony_ci dev->submit_queues = dev->prev_submit_queues; 38662306a36Sopenharmony_ci dev->poll_queues = dev->prev_poll_queues; 38762306a36Sopenharmony_ci } 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_ci return ret; 39062306a36Sopenharmony_ci} 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_cistatic int nullb_apply_submit_queues(struct nullb_device *dev, 39362306a36Sopenharmony_ci unsigned int submit_queues) 39462306a36Sopenharmony_ci{ 39562306a36Sopenharmony_ci return nullb_update_nr_hw_queues(dev, submit_queues, dev->poll_queues); 39662306a36Sopenharmony_ci} 39762306a36Sopenharmony_ci 39862306a36Sopenharmony_cistatic int nullb_apply_poll_queues(struct nullb_device *dev, 39962306a36Sopenharmony_ci unsigned int poll_queues) 40062306a36Sopenharmony_ci{ 40162306a36Sopenharmony_ci return nullb_update_nr_hw_queues(dev, dev->submit_queues, poll_queues); 40262306a36Sopenharmony_ci} 40362306a36Sopenharmony_ci 40462306a36Sopenharmony_ciNULLB_DEVICE_ATTR(size, ulong, NULL); 40562306a36Sopenharmony_ciNULLB_DEVICE_ATTR(completion_nsec, ulong, NULL); 40662306a36Sopenharmony_ciNULLB_DEVICE_ATTR(submit_queues, uint, nullb_apply_submit_queues); 40762306a36Sopenharmony_ciNULLB_DEVICE_ATTR(poll_queues, uint, nullb_apply_poll_queues); 40862306a36Sopenharmony_ciNULLB_DEVICE_ATTR(home_node, uint, NULL); 40962306a36Sopenharmony_ciNULLB_DEVICE_ATTR(queue_mode, uint, NULL); 41062306a36Sopenharmony_ciNULLB_DEVICE_ATTR(blocksize, uint, NULL); 41162306a36Sopenharmony_ciNULLB_DEVICE_ATTR(max_sectors, uint, NULL); 41262306a36Sopenharmony_ciNULLB_DEVICE_ATTR(irqmode, uint, NULL); 41362306a36Sopenharmony_ciNULLB_DEVICE_ATTR(hw_queue_depth, uint, NULL); 41462306a36Sopenharmony_ciNULLB_DEVICE_ATTR(index, uint, NULL); 41562306a36Sopenharmony_ciNULLB_DEVICE_ATTR(blocking, bool, NULL); 41662306a36Sopenharmony_ciNULLB_DEVICE_ATTR(use_per_node_hctx, bool, NULL); 41762306a36Sopenharmony_ciNULLB_DEVICE_ATTR(memory_backed, bool, NULL); 41862306a36Sopenharmony_ciNULLB_DEVICE_ATTR(discard, bool, NULL); 41962306a36Sopenharmony_ciNULLB_DEVICE_ATTR(mbps, uint, NULL); 42062306a36Sopenharmony_ciNULLB_DEVICE_ATTR(cache_size, ulong, NULL); 42162306a36Sopenharmony_ciNULLB_DEVICE_ATTR(zoned, bool, NULL); 42262306a36Sopenharmony_ciNULLB_DEVICE_ATTR(zone_size, ulong, NULL); 42362306a36Sopenharmony_ciNULLB_DEVICE_ATTR(zone_capacity, ulong, NULL); 42462306a36Sopenharmony_ciNULLB_DEVICE_ATTR(zone_nr_conv, uint, NULL); 42562306a36Sopenharmony_ciNULLB_DEVICE_ATTR(zone_max_open, uint, NULL); 42662306a36Sopenharmony_ciNULLB_DEVICE_ATTR(zone_max_active, uint, NULL); 42762306a36Sopenharmony_ciNULLB_DEVICE_ATTR(virt_boundary, bool, NULL); 42862306a36Sopenharmony_ciNULLB_DEVICE_ATTR(no_sched, bool, NULL); 42962306a36Sopenharmony_ciNULLB_DEVICE_ATTR(shared_tag_bitmap, bool, NULL); 43062306a36Sopenharmony_ci 43162306a36Sopenharmony_cistatic ssize_t nullb_device_power_show(struct config_item *item, char *page) 43262306a36Sopenharmony_ci{ 43362306a36Sopenharmony_ci return nullb_device_bool_attr_show(to_nullb_device(item)->power, page); 43462306a36Sopenharmony_ci} 43562306a36Sopenharmony_ci 43662306a36Sopenharmony_cistatic ssize_t nullb_device_power_store(struct config_item *item, 43762306a36Sopenharmony_ci const char *page, size_t count) 43862306a36Sopenharmony_ci{ 43962306a36Sopenharmony_ci struct nullb_device *dev = to_nullb_device(item); 44062306a36Sopenharmony_ci bool newp = false; 44162306a36Sopenharmony_ci ssize_t ret; 44262306a36Sopenharmony_ci 44362306a36Sopenharmony_ci ret = nullb_device_bool_attr_store(&newp, page, count); 44462306a36Sopenharmony_ci if (ret < 0) 44562306a36Sopenharmony_ci return ret; 44662306a36Sopenharmony_ci 44762306a36Sopenharmony_ci if (!dev->power && newp) { 44862306a36Sopenharmony_ci if (test_and_set_bit(NULLB_DEV_FL_UP, &dev->flags)) 44962306a36Sopenharmony_ci return count; 45062306a36Sopenharmony_ci ret = null_add_dev(dev); 45162306a36Sopenharmony_ci if (ret) { 45262306a36Sopenharmony_ci clear_bit(NULLB_DEV_FL_UP, &dev->flags); 45362306a36Sopenharmony_ci return ret; 45462306a36Sopenharmony_ci } 45562306a36Sopenharmony_ci 45662306a36Sopenharmony_ci set_bit(NULLB_DEV_FL_CONFIGURED, &dev->flags); 45762306a36Sopenharmony_ci dev->power = newp; 45862306a36Sopenharmony_ci } else if (dev->power && !newp) { 45962306a36Sopenharmony_ci if (test_and_clear_bit(NULLB_DEV_FL_UP, &dev->flags)) { 46062306a36Sopenharmony_ci mutex_lock(&lock); 46162306a36Sopenharmony_ci dev->power = newp; 46262306a36Sopenharmony_ci null_del_dev(dev->nullb); 46362306a36Sopenharmony_ci mutex_unlock(&lock); 46462306a36Sopenharmony_ci } 46562306a36Sopenharmony_ci clear_bit(NULLB_DEV_FL_CONFIGURED, &dev->flags); 46662306a36Sopenharmony_ci } 46762306a36Sopenharmony_ci 46862306a36Sopenharmony_ci return count; 46962306a36Sopenharmony_ci} 47062306a36Sopenharmony_ci 47162306a36Sopenharmony_ciCONFIGFS_ATTR(nullb_device_, power); 47262306a36Sopenharmony_ci 47362306a36Sopenharmony_cistatic ssize_t nullb_device_badblocks_show(struct config_item *item, char *page) 47462306a36Sopenharmony_ci{ 47562306a36Sopenharmony_ci struct nullb_device *t_dev = to_nullb_device(item); 47662306a36Sopenharmony_ci 47762306a36Sopenharmony_ci return badblocks_show(&t_dev->badblocks, page, 0); 47862306a36Sopenharmony_ci} 47962306a36Sopenharmony_ci 48062306a36Sopenharmony_cistatic ssize_t nullb_device_badblocks_store(struct config_item *item, 48162306a36Sopenharmony_ci const char *page, size_t count) 48262306a36Sopenharmony_ci{ 48362306a36Sopenharmony_ci struct nullb_device *t_dev = to_nullb_device(item); 48462306a36Sopenharmony_ci char *orig, *buf, *tmp; 48562306a36Sopenharmony_ci u64 start, end; 48662306a36Sopenharmony_ci int ret; 48762306a36Sopenharmony_ci 48862306a36Sopenharmony_ci orig = kstrndup(page, count, GFP_KERNEL); 48962306a36Sopenharmony_ci if (!orig) 49062306a36Sopenharmony_ci return -ENOMEM; 49162306a36Sopenharmony_ci 49262306a36Sopenharmony_ci buf = strstrip(orig); 49362306a36Sopenharmony_ci 49462306a36Sopenharmony_ci ret = -EINVAL; 49562306a36Sopenharmony_ci if (buf[0] != '+' && buf[0] != '-') 49662306a36Sopenharmony_ci goto out; 49762306a36Sopenharmony_ci tmp = strchr(&buf[1], '-'); 49862306a36Sopenharmony_ci if (!tmp) 49962306a36Sopenharmony_ci goto out; 50062306a36Sopenharmony_ci *tmp = '\0'; 50162306a36Sopenharmony_ci ret = kstrtoull(buf + 1, 0, &start); 50262306a36Sopenharmony_ci if (ret) 50362306a36Sopenharmony_ci goto out; 50462306a36Sopenharmony_ci ret = kstrtoull(tmp + 1, 0, &end); 50562306a36Sopenharmony_ci if (ret) 50662306a36Sopenharmony_ci goto out; 50762306a36Sopenharmony_ci ret = -EINVAL; 50862306a36Sopenharmony_ci if (start > end) 50962306a36Sopenharmony_ci goto out; 51062306a36Sopenharmony_ci /* enable badblocks */ 51162306a36Sopenharmony_ci cmpxchg(&t_dev->badblocks.shift, -1, 0); 51262306a36Sopenharmony_ci if (buf[0] == '+') 51362306a36Sopenharmony_ci ret = badblocks_set(&t_dev->badblocks, start, 51462306a36Sopenharmony_ci end - start + 1, 1); 51562306a36Sopenharmony_ci else 51662306a36Sopenharmony_ci ret = badblocks_clear(&t_dev->badblocks, start, 51762306a36Sopenharmony_ci end - start + 1); 51862306a36Sopenharmony_ci if (ret == 0) 51962306a36Sopenharmony_ci ret = count; 52062306a36Sopenharmony_ciout: 52162306a36Sopenharmony_ci kfree(orig); 52262306a36Sopenharmony_ci return ret; 52362306a36Sopenharmony_ci} 52462306a36Sopenharmony_ciCONFIGFS_ATTR(nullb_device_, badblocks); 52562306a36Sopenharmony_ci 52662306a36Sopenharmony_cistatic ssize_t nullb_device_zone_readonly_store(struct config_item *item, 52762306a36Sopenharmony_ci const char *page, size_t count) 52862306a36Sopenharmony_ci{ 52962306a36Sopenharmony_ci struct nullb_device *dev = to_nullb_device(item); 53062306a36Sopenharmony_ci 53162306a36Sopenharmony_ci return zone_cond_store(dev, page, count, BLK_ZONE_COND_READONLY); 53262306a36Sopenharmony_ci} 53362306a36Sopenharmony_ciCONFIGFS_ATTR_WO(nullb_device_, zone_readonly); 53462306a36Sopenharmony_ci 53562306a36Sopenharmony_cistatic ssize_t nullb_device_zone_offline_store(struct config_item *item, 53662306a36Sopenharmony_ci const char *page, size_t count) 53762306a36Sopenharmony_ci{ 53862306a36Sopenharmony_ci struct nullb_device *dev = to_nullb_device(item); 53962306a36Sopenharmony_ci 54062306a36Sopenharmony_ci return zone_cond_store(dev, page, count, BLK_ZONE_COND_OFFLINE); 54162306a36Sopenharmony_ci} 54262306a36Sopenharmony_ciCONFIGFS_ATTR_WO(nullb_device_, zone_offline); 54362306a36Sopenharmony_ci 54462306a36Sopenharmony_cistatic struct configfs_attribute *nullb_device_attrs[] = { 54562306a36Sopenharmony_ci &nullb_device_attr_size, 54662306a36Sopenharmony_ci &nullb_device_attr_completion_nsec, 54762306a36Sopenharmony_ci &nullb_device_attr_submit_queues, 54862306a36Sopenharmony_ci &nullb_device_attr_poll_queues, 54962306a36Sopenharmony_ci &nullb_device_attr_home_node, 55062306a36Sopenharmony_ci &nullb_device_attr_queue_mode, 55162306a36Sopenharmony_ci &nullb_device_attr_blocksize, 55262306a36Sopenharmony_ci &nullb_device_attr_max_sectors, 55362306a36Sopenharmony_ci &nullb_device_attr_irqmode, 55462306a36Sopenharmony_ci &nullb_device_attr_hw_queue_depth, 55562306a36Sopenharmony_ci &nullb_device_attr_index, 55662306a36Sopenharmony_ci &nullb_device_attr_blocking, 55762306a36Sopenharmony_ci &nullb_device_attr_use_per_node_hctx, 55862306a36Sopenharmony_ci &nullb_device_attr_power, 55962306a36Sopenharmony_ci &nullb_device_attr_memory_backed, 56062306a36Sopenharmony_ci &nullb_device_attr_discard, 56162306a36Sopenharmony_ci &nullb_device_attr_mbps, 56262306a36Sopenharmony_ci &nullb_device_attr_cache_size, 56362306a36Sopenharmony_ci &nullb_device_attr_badblocks, 56462306a36Sopenharmony_ci &nullb_device_attr_zoned, 56562306a36Sopenharmony_ci &nullb_device_attr_zone_size, 56662306a36Sopenharmony_ci &nullb_device_attr_zone_capacity, 56762306a36Sopenharmony_ci &nullb_device_attr_zone_nr_conv, 56862306a36Sopenharmony_ci &nullb_device_attr_zone_max_open, 56962306a36Sopenharmony_ci &nullb_device_attr_zone_max_active, 57062306a36Sopenharmony_ci &nullb_device_attr_zone_readonly, 57162306a36Sopenharmony_ci &nullb_device_attr_zone_offline, 57262306a36Sopenharmony_ci &nullb_device_attr_virt_boundary, 57362306a36Sopenharmony_ci &nullb_device_attr_no_sched, 57462306a36Sopenharmony_ci &nullb_device_attr_shared_tag_bitmap, 57562306a36Sopenharmony_ci NULL, 57662306a36Sopenharmony_ci}; 57762306a36Sopenharmony_ci 57862306a36Sopenharmony_cistatic void nullb_device_release(struct config_item *item) 57962306a36Sopenharmony_ci{ 58062306a36Sopenharmony_ci struct nullb_device *dev = to_nullb_device(item); 58162306a36Sopenharmony_ci 58262306a36Sopenharmony_ci null_free_device_storage(dev, false); 58362306a36Sopenharmony_ci null_free_dev(dev); 58462306a36Sopenharmony_ci} 58562306a36Sopenharmony_ci 58662306a36Sopenharmony_cistatic struct configfs_item_operations nullb_device_ops = { 58762306a36Sopenharmony_ci .release = nullb_device_release, 58862306a36Sopenharmony_ci}; 58962306a36Sopenharmony_ci 59062306a36Sopenharmony_cistatic const struct config_item_type nullb_device_type = { 59162306a36Sopenharmony_ci .ct_item_ops = &nullb_device_ops, 59262306a36Sopenharmony_ci .ct_attrs = nullb_device_attrs, 59362306a36Sopenharmony_ci .ct_owner = THIS_MODULE, 59462306a36Sopenharmony_ci}; 59562306a36Sopenharmony_ci 59662306a36Sopenharmony_ci#ifdef CONFIG_BLK_DEV_NULL_BLK_FAULT_INJECTION 59762306a36Sopenharmony_ci 59862306a36Sopenharmony_cistatic void nullb_add_fault_config(struct nullb_device *dev) 59962306a36Sopenharmony_ci{ 60062306a36Sopenharmony_ci fault_config_init(&dev->timeout_config, "timeout_inject"); 60162306a36Sopenharmony_ci fault_config_init(&dev->requeue_config, "requeue_inject"); 60262306a36Sopenharmony_ci fault_config_init(&dev->init_hctx_fault_config, "init_hctx_fault_inject"); 60362306a36Sopenharmony_ci 60462306a36Sopenharmony_ci configfs_add_default_group(&dev->timeout_config.group, &dev->group); 60562306a36Sopenharmony_ci configfs_add_default_group(&dev->requeue_config.group, &dev->group); 60662306a36Sopenharmony_ci configfs_add_default_group(&dev->init_hctx_fault_config.group, &dev->group); 60762306a36Sopenharmony_ci} 60862306a36Sopenharmony_ci 60962306a36Sopenharmony_ci#else 61062306a36Sopenharmony_ci 61162306a36Sopenharmony_cistatic void nullb_add_fault_config(struct nullb_device *dev) 61262306a36Sopenharmony_ci{ 61362306a36Sopenharmony_ci} 61462306a36Sopenharmony_ci 61562306a36Sopenharmony_ci#endif 61662306a36Sopenharmony_ci 61762306a36Sopenharmony_cistatic struct 61862306a36Sopenharmony_ciconfig_group *nullb_group_make_group(struct config_group *group, const char *name) 61962306a36Sopenharmony_ci{ 62062306a36Sopenharmony_ci struct nullb_device *dev; 62162306a36Sopenharmony_ci 62262306a36Sopenharmony_ci if (null_find_dev_by_name(name)) 62362306a36Sopenharmony_ci return ERR_PTR(-EEXIST); 62462306a36Sopenharmony_ci 62562306a36Sopenharmony_ci dev = null_alloc_dev(); 62662306a36Sopenharmony_ci if (!dev) 62762306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 62862306a36Sopenharmony_ci 62962306a36Sopenharmony_ci config_group_init_type_name(&dev->group, name, &nullb_device_type); 63062306a36Sopenharmony_ci nullb_add_fault_config(dev); 63162306a36Sopenharmony_ci 63262306a36Sopenharmony_ci return &dev->group; 63362306a36Sopenharmony_ci} 63462306a36Sopenharmony_ci 63562306a36Sopenharmony_cistatic void 63662306a36Sopenharmony_cinullb_group_drop_item(struct config_group *group, struct config_item *item) 63762306a36Sopenharmony_ci{ 63862306a36Sopenharmony_ci struct nullb_device *dev = to_nullb_device(item); 63962306a36Sopenharmony_ci 64062306a36Sopenharmony_ci if (test_and_clear_bit(NULLB_DEV_FL_UP, &dev->flags)) { 64162306a36Sopenharmony_ci mutex_lock(&lock); 64262306a36Sopenharmony_ci dev->power = false; 64362306a36Sopenharmony_ci null_del_dev(dev->nullb); 64462306a36Sopenharmony_ci mutex_unlock(&lock); 64562306a36Sopenharmony_ci } 64662306a36Sopenharmony_ci 64762306a36Sopenharmony_ci config_item_put(item); 64862306a36Sopenharmony_ci} 64962306a36Sopenharmony_ci 65062306a36Sopenharmony_cistatic ssize_t memb_group_features_show(struct config_item *item, char *page) 65162306a36Sopenharmony_ci{ 65262306a36Sopenharmony_ci return snprintf(page, PAGE_SIZE, 65362306a36Sopenharmony_ci "badblocks,blocking,blocksize,cache_size," 65462306a36Sopenharmony_ci "completion_nsec,discard,home_node,hw_queue_depth," 65562306a36Sopenharmony_ci "irqmode,max_sectors,mbps,memory_backed,no_sched," 65662306a36Sopenharmony_ci "poll_queues,power,queue_mode,shared_tag_bitmap,size," 65762306a36Sopenharmony_ci "submit_queues,use_per_node_hctx,virt_boundary,zoned," 65862306a36Sopenharmony_ci "zone_capacity,zone_max_active,zone_max_open," 65962306a36Sopenharmony_ci "zone_nr_conv,zone_offline,zone_readonly,zone_size\n"); 66062306a36Sopenharmony_ci} 66162306a36Sopenharmony_ci 66262306a36Sopenharmony_ciCONFIGFS_ATTR_RO(memb_group_, features); 66362306a36Sopenharmony_ci 66462306a36Sopenharmony_cistatic struct configfs_attribute *nullb_group_attrs[] = { 66562306a36Sopenharmony_ci &memb_group_attr_features, 66662306a36Sopenharmony_ci NULL, 66762306a36Sopenharmony_ci}; 66862306a36Sopenharmony_ci 66962306a36Sopenharmony_cistatic struct configfs_group_operations nullb_group_ops = { 67062306a36Sopenharmony_ci .make_group = nullb_group_make_group, 67162306a36Sopenharmony_ci .drop_item = nullb_group_drop_item, 67262306a36Sopenharmony_ci}; 67362306a36Sopenharmony_ci 67462306a36Sopenharmony_cistatic const struct config_item_type nullb_group_type = { 67562306a36Sopenharmony_ci .ct_group_ops = &nullb_group_ops, 67662306a36Sopenharmony_ci .ct_attrs = nullb_group_attrs, 67762306a36Sopenharmony_ci .ct_owner = THIS_MODULE, 67862306a36Sopenharmony_ci}; 67962306a36Sopenharmony_ci 68062306a36Sopenharmony_cistatic struct configfs_subsystem nullb_subsys = { 68162306a36Sopenharmony_ci .su_group = { 68262306a36Sopenharmony_ci .cg_item = { 68362306a36Sopenharmony_ci .ci_namebuf = "nullb", 68462306a36Sopenharmony_ci .ci_type = &nullb_group_type, 68562306a36Sopenharmony_ci }, 68662306a36Sopenharmony_ci }, 68762306a36Sopenharmony_ci}; 68862306a36Sopenharmony_ci 68962306a36Sopenharmony_cistatic inline int null_cache_active(struct nullb *nullb) 69062306a36Sopenharmony_ci{ 69162306a36Sopenharmony_ci return test_bit(NULLB_DEV_FL_CACHE, &nullb->dev->flags); 69262306a36Sopenharmony_ci} 69362306a36Sopenharmony_ci 69462306a36Sopenharmony_cistatic struct nullb_device *null_alloc_dev(void) 69562306a36Sopenharmony_ci{ 69662306a36Sopenharmony_ci struct nullb_device *dev; 69762306a36Sopenharmony_ci 69862306a36Sopenharmony_ci dev = kzalloc(sizeof(*dev), GFP_KERNEL); 69962306a36Sopenharmony_ci if (!dev) 70062306a36Sopenharmony_ci return NULL; 70162306a36Sopenharmony_ci 70262306a36Sopenharmony_ci#ifdef CONFIG_BLK_DEV_NULL_BLK_FAULT_INJECTION 70362306a36Sopenharmony_ci dev->timeout_config.attr = null_timeout_attr; 70462306a36Sopenharmony_ci dev->requeue_config.attr = null_requeue_attr; 70562306a36Sopenharmony_ci dev->init_hctx_fault_config.attr = null_init_hctx_attr; 70662306a36Sopenharmony_ci#endif 70762306a36Sopenharmony_ci 70862306a36Sopenharmony_ci INIT_RADIX_TREE(&dev->data, GFP_ATOMIC); 70962306a36Sopenharmony_ci INIT_RADIX_TREE(&dev->cache, GFP_ATOMIC); 71062306a36Sopenharmony_ci if (badblocks_init(&dev->badblocks, 0)) { 71162306a36Sopenharmony_ci kfree(dev); 71262306a36Sopenharmony_ci return NULL; 71362306a36Sopenharmony_ci } 71462306a36Sopenharmony_ci 71562306a36Sopenharmony_ci dev->size = g_gb * 1024; 71662306a36Sopenharmony_ci dev->completion_nsec = g_completion_nsec; 71762306a36Sopenharmony_ci dev->submit_queues = g_submit_queues; 71862306a36Sopenharmony_ci dev->prev_submit_queues = g_submit_queues; 71962306a36Sopenharmony_ci dev->poll_queues = g_poll_queues; 72062306a36Sopenharmony_ci dev->prev_poll_queues = g_poll_queues; 72162306a36Sopenharmony_ci dev->home_node = g_home_node; 72262306a36Sopenharmony_ci dev->queue_mode = g_queue_mode; 72362306a36Sopenharmony_ci dev->blocksize = g_bs; 72462306a36Sopenharmony_ci dev->max_sectors = g_max_sectors; 72562306a36Sopenharmony_ci dev->irqmode = g_irqmode; 72662306a36Sopenharmony_ci dev->hw_queue_depth = g_hw_queue_depth; 72762306a36Sopenharmony_ci dev->blocking = g_blocking; 72862306a36Sopenharmony_ci dev->memory_backed = g_memory_backed; 72962306a36Sopenharmony_ci dev->discard = g_discard; 73062306a36Sopenharmony_ci dev->cache_size = g_cache_size; 73162306a36Sopenharmony_ci dev->mbps = g_mbps; 73262306a36Sopenharmony_ci dev->use_per_node_hctx = g_use_per_node_hctx; 73362306a36Sopenharmony_ci dev->zoned = g_zoned; 73462306a36Sopenharmony_ci dev->zone_size = g_zone_size; 73562306a36Sopenharmony_ci dev->zone_capacity = g_zone_capacity; 73662306a36Sopenharmony_ci dev->zone_nr_conv = g_zone_nr_conv; 73762306a36Sopenharmony_ci dev->zone_max_open = g_zone_max_open; 73862306a36Sopenharmony_ci dev->zone_max_active = g_zone_max_active; 73962306a36Sopenharmony_ci dev->virt_boundary = g_virt_boundary; 74062306a36Sopenharmony_ci dev->no_sched = g_no_sched; 74162306a36Sopenharmony_ci dev->shared_tag_bitmap = g_shared_tag_bitmap; 74262306a36Sopenharmony_ci return dev; 74362306a36Sopenharmony_ci} 74462306a36Sopenharmony_ci 74562306a36Sopenharmony_cistatic void null_free_dev(struct nullb_device *dev) 74662306a36Sopenharmony_ci{ 74762306a36Sopenharmony_ci if (!dev) 74862306a36Sopenharmony_ci return; 74962306a36Sopenharmony_ci 75062306a36Sopenharmony_ci null_free_zoned_dev(dev); 75162306a36Sopenharmony_ci badblocks_exit(&dev->badblocks); 75262306a36Sopenharmony_ci kfree(dev); 75362306a36Sopenharmony_ci} 75462306a36Sopenharmony_ci 75562306a36Sopenharmony_cistatic void put_tag(struct nullb_queue *nq, unsigned int tag) 75662306a36Sopenharmony_ci{ 75762306a36Sopenharmony_ci clear_bit_unlock(tag, nq->tag_map); 75862306a36Sopenharmony_ci 75962306a36Sopenharmony_ci if (waitqueue_active(&nq->wait)) 76062306a36Sopenharmony_ci wake_up(&nq->wait); 76162306a36Sopenharmony_ci} 76262306a36Sopenharmony_ci 76362306a36Sopenharmony_cistatic unsigned int get_tag(struct nullb_queue *nq) 76462306a36Sopenharmony_ci{ 76562306a36Sopenharmony_ci unsigned int tag; 76662306a36Sopenharmony_ci 76762306a36Sopenharmony_ci do { 76862306a36Sopenharmony_ci tag = find_first_zero_bit(nq->tag_map, nq->queue_depth); 76962306a36Sopenharmony_ci if (tag >= nq->queue_depth) 77062306a36Sopenharmony_ci return -1U; 77162306a36Sopenharmony_ci } while (test_and_set_bit_lock(tag, nq->tag_map)); 77262306a36Sopenharmony_ci 77362306a36Sopenharmony_ci return tag; 77462306a36Sopenharmony_ci} 77562306a36Sopenharmony_ci 77662306a36Sopenharmony_cistatic void free_cmd(struct nullb_cmd *cmd) 77762306a36Sopenharmony_ci{ 77862306a36Sopenharmony_ci put_tag(cmd->nq, cmd->tag); 77962306a36Sopenharmony_ci} 78062306a36Sopenharmony_ci 78162306a36Sopenharmony_cistatic enum hrtimer_restart null_cmd_timer_expired(struct hrtimer *timer); 78262306a36Sopenharmony_ci 78362306a36Sopenharmony_cistatic struct nullb_cmd *__alloc_cmd(struct nullb_queue *nq) 78462306a36Sopenharmony_ci{ 78562306a36Sopenharmony_ci struct nullb_cmd *cmd; 78662306a36Sopenharmony_ci unsigned int tag; 78762306a36Sopenharmony_ci 78862306a36Sopenharmony_ci tag = get_tag(nq); 78962306a36Sopenharmony_ci if (tag != -1U) { 79062306a36Sopenharmony_ci cmd = &nq->cmds[tag]; 79162306a36Sopenharmony_ci cmd->tag = tag; 79262306a36Sopenharmony_ci cmd->error = BLK_STS_OK; 79362306a36Sopenharmony_ci cmd->nq = nq; 79462306a36Sopenharmony_ci if (nq->dev->irqmode == NULL_IRQ_TIMER) { 79562306a36Sopenharmony_ci hrtimer_init(&cmd->timer, CLOCK_MONOTONIC, 79662306a36Sopenharmony_ci HRTIMER_MODE_REL); 79762306a36Sopenharmony_ci cmd->timer.function = null_cmd_timer_expired; 79862306a36Sopenharmony_ci } 79962306a36Sopenharmony_ci return cmd; 80062306a36Sopenharmony_ci } 80162306a36Sopenharmony_ci 80262306a36Sopenharmony_ci return NULL; 80362306a36Sopenharmony_ci} 80462306a36Sopenharmony_ci 80562306a36Sopenharmony_cistatic struct nullb_cmd *alloc_cmd(struct nullb_queue *nq, struct bio *bio) 80662306a36Sopenharmony_ci{ 80762306a36Sopenharmony_ci struct nullb_cmd *cmd; 80862306a36Sopenharmony_ci DEFINE_WAIT(wait); 80962306a36Sopenharmony_ci 81062306a36Sopenharmony_ci do { 81162306a36Sopenharmony_ci /* 81262306a36Sopenharmony_ci * This avoids multiple return statements, multiple calls to 81362306a36Sopenharmony_ci * __alloc_cmd() and a fast path call to prepare_to_wait(). 81462306a36Sopenharmony_ci */ 81562306a36Sopenharmony_ci cmd = __alloc_cmd(nq); 81662306a36Sopenharmony_ci if (cmd) { 81762306a36Sopenharmony_ci cmd->bio = bio; 81862306a36Sopenharmony_ci return cmd; 81962306a36Sopenharmony_ci } 82062306a36Sopenharmony_ci prepare_to_wait(&nq->wait, &wait, TASK_UNINTERRUPTIBLE); 82162306a36Sopenharmony_ci io_schedule(); 82262306a36Sopenharmony_ci finish_wait(&nq->wait, &wait); 82362306a36Sopenharmony_ci } while (1); 82462306a36Sopenharmony_ci} 82562306a36Sopenharmony_ci 82662306a36Sopenharmony_cistatic void end_cmd(struct nullb_cmd *cmd) 82762306a36Sopenharmony_ci{ 82862306a36Sopenharmony_ci int queue_mode = cmd->nq->dev->queue_mode; 82962306a36Sopenharmony_ci 83062306a36Sopenharmony_ci switch (queue_mode) { 83162306a36Sopenharmony_ci case NULL_Q_MQ: 83262306a36Sopenharmony_ci blk_mq_end_request(cmd->rq, cmd->error); 83362306a36Sopenharmony_ci return; 83462306a36Sopenharmony_ci case NULL_Q_BIO: 83562306a36Sopenharmony_ci cmd->bio->bi_status = cmd->error; 83662306a36Sopenharmony_ci bio_endio(cmd->bio); 83762306a36Sopenharmony_ci break; 83862306a36Sopenharmony_ci } 83962306a36Sopenharmony_ci 84062306a36Sopenharmony_ci free_cmd(cmd); 84162306a36Sopenharmony_ci} 84262306a36Sopenharmony_ci 84362306a36Sopenharmony_cistatic enum hrtimer_restart null_cmd_timer_expired(struct hrtimer *timer) 84462306a36Sopenharmony_ci{ 84562306a36Sopenharmony_ci end_cmd(container_of(timer, struct nullb_cmd, timer)); 84662306a36Sopenharmony_ci 84762306a36Sopenharmony_ci return HRTIMER_NORESTART; 84862306a36Sopenharmony_ci} 84962306a36Sopenharmony_ci 85062306a36Sopenharmony_cistatic void null_cmd_end_timer(struct nullb_cmd *cmd) 85162306a36Sopenharmony_ci{ 85262306a36Sopenharmony_ci ktime_t kt = cmd->nq->dev->completion_nsec; 85362306a36Sopenharmony_ci 85462306a36Sopenharmony_ci hrtimer_start(&cmd->timer, kt, HRTIMER_MODE_REL); 85562306a36Sopenharmony_ci} 85662306a36Sopenharmony_ci 85762306a36Sopenharmony_cistatic void null_complete_rq(struct request *rq) 85862306a36Sopenharmony_ci{ 85962306a36Sopenharmony_ci end_cmd(blk_mq_rq_to_pdu(rq)); 86062306a36Sopenharmony_ci} 86162306a36Sopenharmony_ci 86262306a36Sopenharmony_cistatic struct nullb_page *null_alloc_page(void) 86362306a36Sopenharmony_ci{ 86462306a36Sopenharmony_ci struct nullb_page *t_page; 86562306a36Sopenharmony_ci 86662306a36Sopenharmony_ci t_page = kmalloc(sizeof(struct nullb_page), GFP_NOIO); 86762306a36Sopenharmony_ci if (!t_page) 86862306a36Sopenharmony_ci return NULL; 86962306a36Sopenharmony_ci 87062306a36Sopenharmony_ci t_page->page = alloc_pages(GFP_NOIO, 0); 87162306a36Sopenharmony_ci if (!t_page->page) { 87262306a36Sopenharmony_ci kfree(t_page); 87362306a36Sopenharmony_ci return NULL; 87462306a36Sopenharmony_ci } 87562306a36Sopenharmony_ci 87662306a36Sopenharmony_ci memset(t_page->bitmap, 0, sizeof(t_page->bitmap)); 87762306a36Sopenharmony_ci return t_page; 87862306a36Sopenharmony_ci} 87962306a36Sopenharmony_ci 88062306a36Sopenharmony_cistatic void null_free_page(struct nullb_page *t_page) 88162306a36Sopenharmony_ci{ 88262306a36Sopenharmony_ci __set_bit(NULLB_PAGE_FREE, t_page->bitmap); 88362306a36Sopenharmony_ci if (test_bit(NULLB_PAGE_LOCK, t_page->bitmap)) 88462306a36Sopenharmony_ci return; 88562306a36Sopenharmony_ci __free_page(t_page->page); 88662306a36Sopenharmony_ci kfree(t_page); 88762306a36Sopenharmony_ci} 88862306a36Sopenharmony_ci 88962306a36Sopenharmony_cistatic bool null_page_empty(struct nullb_page *page) 89062306a36Sopenharmony_ci{ 89162306a36Sopenharmony_ci int size = MAP_SZ - 2; 89262306a36Sopenharmony_ci 89362306a36Sopenharmony_ci return find_first_bit(page->bitmap, size) == size; 89462306a36Sopenharmony_ci} 89562306a36Sopenharmony_ci 89662306a36Sopenharmony_cistatic void null_free_sector(struct nullb *nullb, sector_t sector, 89762306a36Sopenharmony_ci bool is_cache) 89862306a36Sopenharmony_ci{ 89962306a36Sopenharmony_ci unsigned int sector_bit; 90062306a36Sopenharmony_ci u64 idx; 90162306a36Sopenharmony_ci struct nullb_page *t_page, *ret; 90262306a36Sopenharmony_ci struct radix_tree_root *root; 90362306a36Sopenharmony_ci 90462306a36Sopenharmony_ci root = is_cache ? &nullb->dev->cache : &nullb->dev->data; 90562306a36Sopenharmony_ci idx = sector >> PAGE_SECTORS_SHIFT; 90662306a36Sopenharmony_ci sector_bit = (sector & SECTOR_MASK); 90762306a36Sopenharmony_ci 90862306a36Sopenharmony_ci t_page = radix_tree_lookup(root, idx); 90962306a36Sopenharmony_ci if (t_page) { 91062306a36Sopenharmony_ci __clear_bit(sector_bit, t_page->bitmap); 91162306a36Sopenharmony_ci 91262306a36Sopenharmony_ci if (null_page_empty(t_page)) { 91362306a36Sopenharmony_ci ret = radix_tree_delete_item(root, idx, t_page); 91462306a36Sopenharmony_ci WARN_ON(ret != t_page); 91562306a36Sopenharmony_ci null_free_page(ret); 91662306a36Sopenharmony_ci if (is_cache) 91762306a36Sopenharmony_ci nullb->dev->curr_cache -= PAGE_SIZE; 91862306a36Sopenharmony_ci } 91962306a36Sopenharmony_ci } 92062306a36Sopenharmony_ci} 92162306a36Sopenharmony_ci 92262306a36Sopenharmony_cistatic struct nullb_page *null_radix_tree_insert(struct nullb *nullb, u64 idx, 92362306a36Sopenharmony_ci struct nullb_page *t_page, bool is_cache) 92462306a36Sopenharmony_ci{ 92562306a36Sopenharmony_ci struct radix_tree_root *root; 92662306a36Sopenharmony_ci 92762306a36Sopenharmony_ci root = is_cache ? &nullb->dev->cache : &nullb->dev->data; 92862306a36Sopenharmony_ci 92962306a36Sopenharmony_ci if (radix_tree_insert(root, idx, t_page)) { 93062306a36Sopenharmony_ci null_free_page(t_page); 93162306a36Sopenharmony_ci t_page = radix_tree_lookup(root, idx); 93262306a36Sopenharmony_ci WARN_ON(!t_page || t_page->page->index != idx); 93362306a36Sopenharmony_ci } else if (is_cache) 93462306a36Sopenharmony_ci nullb->dev->curr_cache += PAGE_SIZE; 93562306a36Sopenharmony_ci 93662306a36Sopenharmony_ci return t_page; 93762306a36Sopenharmony_ci} 93862306a36Sopenharmony_ci 93962306a36Sopenharmony_cistatic void null_free_device_storage(struct nullb_device *dev, bool is_cache) 94062306a36Sopenharmony_ci{ 94162306a36Sopenharmony_ci unsigned long pos = 0; 94262306a36Sopenharmony_ci int nr_pages; 94362306a36Sopenharmony_ci struct nullb_page *ret, *t_pages[FREE_BATCH]; 94462306a36Sopenharmony_ci struct radix_tree_root *root; 94562306a36Sopenharmony_ci 94662306a36Sopenharmony_ci root = is_cache ? &dev->cache : &dev->data; 94762306a36Sopenharmony_ci 94862306a36Sopenharmony_ci do { 94962306a36Sopenharmony_ci int i; 95062306a36Sopenharmony_ci 95162306a36Sopenharmony_ci nr_pages = radix_tree_gang_lookup(root, 95262306a36Sopenharmony_ci (void **)t_pages, pos, FREE_BATCH); 95362306a36Sopenharmony_ci 95462306a36Sopenharmony_ci for (i = 0; i < nr_pages; i++) { 95562306a36Sopenharmony_ci pos = t_pages[i]->page->index; 95662306a36Sopenharmony_ci ret = radix_tree_delete_item(root, pos, t_pages[i]); 95762306a36Sopenharmony_ci WARN_ON(ret != t_pages[i]); 95862306a36Sopenharmony_ci null_free_page(ret); 95962306a36Sopenharmony_ci } 96062306a36Sopenharmony_ci 96162306a36Sopenharmony_ci pos++; 96262306a36Sopenharmony_ci } while (nr_pages == FREE_BATCH); 96362306a36Sopenharmony_ci 96462306a36Sopenharmony_ci if (is_cache) 96562306a36Sopenharmony_ci dev->curr_cache = 0; 96662306a36Sopenharmony_ci} 96762306a36Sopenharmony_ci 96862306a36Sopenharmony_cistatic struct nullb_page *__null_lookup_page(struct nullb *nullb, 96962306a36Sopenharmony_ci sector_t sector, bool for_write, bool is_cache) 97062306a36Sopenharmony_ci{ 97162306a36Sopenharmony_ci unsigned int sector_bit; 97262306a36Sopenharmony_ci u64 idx; 97362306a36Sopenharmony_ci struct nullb_page *t_page; 97462306a36Sopenharmony_ci struct radix_tree_root *root; 97562306a36Sopenharmony_ci 97662306a36Sopenharmony_ci idx = sector >> PAGE_SECTORS_SHIFT; 97762306a36Sopenharmony_ci sector_bit = (sector & SECTOR_MASK); 97862306a36Sopenharmony_ci 97962306a36Sopenharmony_ci root = is_cache ? &nullb->dev->cache : &nullb->dev->data; 98062306a36Sopenharmony_ci t_page = radix_tree_lookup(root, idx); 98162306a36Sopenharmony_ci WARN_ON(t_page && t_page->page->index != idx); 98262306a36Sopenharmony_ci 98362306a36Sopenharmony_ci if (t_page && (for_write || test_bit(sector_bit, t_page->bitmap))) 98462306a36Sopenharmony_ci return t_page; 98562306a36Sopenharmony_ci 98662306a36Sopenharmony_ci return NULL; 98762306a36Sopenharmony_ci} 98862306a36Sopenharmony_ci 98962306a36Sopenharmony_cistatic struct nullb_page *null_lookup_page(struct nullb *nullb, 99062306a36Sopenharmony_ci sector_t sector, bool for_write, bool ignore_cache) 99162306a36Sopenharmony_ci{ 99262306a36Sopenharmony_ci struct nullb_page *page = NULL; 99362306a36Sopenharmony_ci 99462306a36Sopenharmony_ci if (!ignore_cache) 99562306a36Sopenharmony_ci page = __null_lookup_page(nullb, sector, for_write, true); 99662306a36Sopenharmony_ci if (page) 99762306a36Sopenharmony_ci return page; 99862306a36Sopenharmony_ci return __null_lookup_page(nullb, sector, for_write, false); 99962306a36Sopenharmony_ci} 100062306a36Sopenharmony_ci 100162306a36Sopenharmony_cistatic struct nullb_page *null_insert_page(struct nullb *nullb, 100262306a36Sopenharmony_ci sector_t sector, bool ignore_cache) 100362306a36Sopenharmony_ci __releases(&nullb->lock) 100462306a36Sopenharmony_ci __acquires(&nullb->lock) 100562306a36Sopenharmony_ci{ 100662306a36Sopenharmony_ci u64 idx; 100762306a36Sopenharmony_ci struct nullb_page *t_page; 100862306a36Sopenharmony_ci 100962306a36Sopenharmony_ci t_page = null_lookup_page(nullb, sector, true, ignore_cache); 101062306a36Sopenharmony_ci if (t_page) 101162306a36Sopenharmony_ci return t_page; 101262306a36Sopenharmony_ci 101362306a36Sopenharmony_ci spin_unlock_irq(&nullb->lock); 101462306a36Sopenharmony_ci 101562306a36Sopenharmony_ci t_page = null_alloc_page(); 101662306a36Sopenharmony_ci if (!t_page) 101762306a36Sopenharmony_ci goto out_lock; 101862306a36Sopenharmony_ci 101962306a36Sopenharmony_ci if (radix_tree_preload(GFP_NOIO)) 102062306a36Sopenharmony_ci goto out_freepage; 102162306a36Sopenharmony_ci 102262306a36Sopenharmony_ci spin_lock_irq(&nullb->lock); 102362306a36Sopenharmony_ci idx = sector >> PAGE_SECTORS_SHIFT; 102462306a36Sopenharmony_ci t_page->page->index = idx; 102562306a36Sopenharmony_ci t_page = null_radix_tree_insert(nullb, idx, t_page, !ignore_cache); 102662306a36Sopenharmony_ci radix_tree_preload_end(); 102762306a36Sopenharmony_ci 102862306a36Sopenharmony_ci return t_page; 102962306a36Sopenharmony_ciout_freepage: 103062306a36Sopenharmony_ci null_free_page(t_page); 103162306a36Sopenharmony_ciout_lock: 103262306a36Sopenharmony_ci spin_lock_irq(&nullb->lock); 103362306a36Sopenharmony_ci return null_lookup_page(nullb, sector, true, ignore_cache); 103462306a36Sopenharmony_ci} 103562306a36Sopenharmony_ci 103662306a36Sopenharmony_cistatic int null_flush_cache_page(struct nullb *nullb, struct nullb_page *c_page) 103762306a36Sopenharmony_ci{ 103862306a36Sopenharmony_ci int i; 103962306a36Sopenharmony_ci unsigned int offset; 104062306a36Sopenharmony_ci u64 idx; 104162306a36Sopenharmony_ci struct nullb_page *t_page, *ret; 104262306a36Sopenharmony_ci void *dst, *src; 104362306a36Sopenharmony_ci 104462306a36Sopenharmony_ci idx = c_page->page->index; 104562306a36Sopenharmony_ci 104662306a36Sopenharmony_ci t_page = null_insert_page(nullb, idx << PAGE_SECTORS_SHIFT, true); 104762306a36Sopenharmony_ci 104862306a36Sopenharmony_ci __clear_bit(NULLB_PAGE_LOCK, c_page->bitmap); 104962306a36Sopenharmony_ci if (test_bit(NULLB_PAGE_FREE, c_page->bitmap)) { 105062306a36Sopenharmony_ci null_free_page(c_page); 105162306a36Sopenharmony_ci if (t_page && null_page_empty(t_page)) { 105262306a36Sopenharmony_ci ret = radix_tree_delete_item(&nullb->dev->data, 105362306a36Sopenharmony_ci idx, t_page); 105462306a36Sopenharmony_ci null_free_page(t_page); 105562306a36Sopenharmony_ci } 105662306a36Sopenharmony_ci return 0; 105762306a36Sopenharmony_ci } 105862306a36Sopenharmony_ci 105962306a36Sopenharmony_ci if (!t_page) 106062306a36Sopenharmony_ci return -ENOMEM; 106162306a36Sopenharmony_ci 106262306a36Sopenharmony_ci src = kmap_local_page(c_page->page); 106362306a36Sopenharmony_ci dst = kmap_local_page(t_page->page); 106462306a36Sopenharmony_ci 106562306a36Sopenharmony_ci for (i = 0; i < PAGE_SECTORS; 106662306a36Sopenharmony_ci i += (nullb->dev->blocksize >> SECTOR_SHIFT)) { 106762306a36Sopenharmony_ci if (test_bit(i, c_page->bitmap)) { 106862306a36Sopenharmony_ci offset = (i << SECTOR_SHIFT); 106962306a36Sopenharmony_ci memcpy(dst + offset, src + offset, 107062306a36Sopenharmony_ci nullb->dev->blocksize); 107162306a36Sopenharmony_ci __set_bit(i, t_page->bitmap); 107262306a36Sopenharmony_ci } 107362306a36Sopenharmony_ci } 107462306a36Sopenharmony_ci 107562306a36Sopenharmony_ci kunmap_local(dst); 107662306a36Sopenharmony_ci kunmap_local(src); 107762306a36Sopenharmony_ci 107862306a36Sopenharmony_ci ret = radix_tree_delete_item(&nullb->dev->cache, idx, c_page); 107962306a36Sopenharmony_ci null_free_page(ret); 108062306a36Sopenharmony_ci nullb->dev->curr_cache -= PAGE_SIZE; 108162306a36Sopenharmony_ci 108262306a36Sopenharmony_ci return 0; 108362306a36Sopenharmony_ci} 108462306a36Sopenharmony_ci 108562306a36Sopenharmony_cistatic int null_make_cache_space(struct nullb *nullb, unsigned long n) 108662306a36Sopenharmony_ci{ 108762306a36Sopenharmony_ci int i, err, nr_pages; 108862306a36Sopenharmony_ci struct nullb_page *c_pages[FREE_BATCH]; 108962306a36Sopenharmony_ci unsigned long flushed = 0, one_round; 109062306a36Sopenharmony_ci 109162306a36Sopenharmony_ciagain: 109262306a36Sopenharmony_ci if ((nullb->dev->cache_size * 1024 * 1024) > 109362306a36Sopenharmony_ci nullb->dev->curr_cache + n || nullb->dev->curr_cache == 0) 109462306a36Sopenharmony_ci return 0; 109562306a36Sopenharmony_ci 109662306a36Sopenharmony_ci nr_pages = radix_tree_gang_lookup(&nullb->dev->cache, 109762306a36Sopenharmony_ci (void **)c_pages, nullb->cache_flush_pos, FREE_BATCH); 109862306a36Sopenharmony_ci /* 109962306a36Sopenharmony_ci * nullb_flush_cache_page could unlock before using the c_pages. To 110062306a36Sopenharmony_ci * avoid race, we don't allow page free 110162306a36Sopenharmony_ci */ 110262306a36Sopenharmony_ci for (i = 0; i < nr_pages; i++) { 110362306a36Sopenharmony_ci nullb->cache_flush_pos = c_pages[i]->page->index; 110462306a36Sopenharmony_ci /* 110562306a36Sopenharmony_ci * We found the page which is being flushed to disk by other 110662306a36Sopenharmony_ci * threads 110762306a36Sopenharmony_ci */ 110862306a36Sopenharmony_ci if (test_bit(NULLB_PAGE_LOCK, c_pages[i]->bitmap)) 110962306a36Sopenharmony_ci c_pages[i] = NULL; 111062306a36Sopenharmony_ci else 111162306a36Sopenharmony_ci __set_bit(NULLB_PAGE_LOCK, c_pages[i]->bitmap); 111262306a36Sopenharmony_ci } 111362306a36Sopenharmony_ci 111462306a36Sopenharmony_ci one_round = 0; 111562306a36Sopenharmony_ci for (i = 0; i < nr_pages; i++) { 111662306a36Sopenharmony_ci if (c_pages[i] == NULL) 111762306a36Sopenharmony_ci continue; 111862306a36Sopenharmony_ci err = null_flush_cache_page(nullb, c_pages[i]); 111962306a36Sopenharmony_ci if (err) 112062306a36Sopenharmony_ci return err; 112162306a36Sopenharmony_ci one_round++; 112262306a36Sopenharmony_ci } 112362306a36Sopenharmony_ci flushed += one_round << PAGE_SHIFT; 112462306a36Sopenharmony_ci 112562306a36Sopenharmony_ci if (n > flushed) { 112662306a36Sopenharmony_ci if (nr_pages == 0) 112762306a36Sopenharmony_ci nullb->cache_flush_pos = 0; 112862306a36Sopenharmony_ci if (one_round == 0) { 112962306a36Sopenharmony_ci /* give other threads a chance */ 113062306a36Sopenharmony_ci spin_unlock_irq(&nullb->lock); 113162306a36Sopenharmony_ci spin_lock_irq(&nullb->lock); 113262306a36Sopenharmony_ci } 113362306a36Sopenharmony_ci goto again; 113462306a36Sopenharmony_ci } 113562306a36Sopenharmony_ci return 0; 113662306a36Sopenharmony_ci} 113762306a36Sopenharmony_ci 113862306a36Sopenharmony_cistatic int copy_to_nullb(struct nullb *nullb, struct page *source, 113962306a36Sopenharmony_ci unsigned int off, sector_t sector, size_t n, bool is_fua) 114062306a36Sopenharmony_ci{ 114162306a36Sopenharmony_ci size_t temp, count = 0; 114262306a36Sopenharmony_ci unsigned int offset; 114362306a36Sopenharmony_ci struct nullb_page *t_page; 114462306a36Sopenharmony_ci 114562306a36Sopenharmony_ci while (count < n) { 114662306a36Sopenharmony_ci temp = min_t(size_t, nullb->dev->blocksize, n - count); 114762306a36Sopenharmony_ci 114862306a36Sopenharmony_ci if (null_cache_active(nullb) && !is_fua) 114962306a36Sopenharmony_ci null_make_cache_space(nullb, PAGE_SIZE); 115062306a36Sopenharmony_ci 115162306a36Sopenharmony_ci offset = (sector & SECTOR_MASK) << SECTOR_SHIFT; 115262306a36Sopenharmony_ci t_page = null_insert_page(nullb, sector, 115362306a36Sopenharmony_ci !null_cache_active(nullb) || is_fua); 115462306a36Sopenharmony_ci if (!t_page) 115562306a36Sopenharmony_ci return -ENOSPC; 115662306a36Sopenharmony_ci 115762306a36Sopenharmony_ci memcpy_page(t_page->page, offset, source, off + count, temp); 115862306a36Sopenharmony_ci 115962306a36Sopenharmony_ci __set_bit(sector & SECTOR_MASK, t_page->bitmap); 116062306a36Sopenharmony_ci 116162306a36Sopenharmony_ci if (is_fua) 116262306a36Sopenharmony_ci null_free_sector(nullb, sector, true); 116362306a36Sopenharmony_ci 116462306a36Sopenharmony_ci count += temp; 116562306a36Sopenharmony_ci sector += temp >> SECTOR_SHIFT; 116662306a36Sopenharmony_ci } 116762306a36Sopenharmony_ci return 0; 116862306a36Sopenharmony_ci} 116962306a36Sopenharmony_ci 117062306a36Sopenharmony_cistatic int copy_from_nullb(struct nullb *nullb, struct page *dest, 117162306a36Sopenharmony_ci unsigned int off, sector_t sector, size_t n) 117262306a36Sopenharmony_ci{ 117362306a36Sopenharmony_ci size_t temp, count = 0; 117462306a36Sopenharmony_ci unsigned int offset; 117562306a36Sopenharmony_ci struct nullb_page *t_page; 117662306a36Sopenharmony_ci 117762306a36Sopenharmony_ci while (count < n) { 117862306a36Sopenharmony_ci temp = min_t(size_t, nullb->dev->blocksize, n - count); 117962306a36Sopenharmony_ci 118062306a36Sopenharmony_ci offset = (sector & SECTOR_MASK) << SECTOR_SHIFT; 118162306a36Sopenharmony_ci t_page = null_lookup_page(nullb, sector, false, 118262306a36Sopenharmony_ci !null_cache_active(nullb)); 118362306a36Sopenharmony_ci 118462306a36Sopenharmony_ci if (t_page) 118562306a36Sopenharmony_ci memcpy_page(dest, off + count, t_page->page, offset, 118662306a36Sopenharmony_ci temp); 118762306a36Sopenharmony_ci else 118862306a36Sopenharmony_ci zero_user(dest, off + count, temp); 118962306a36Sopenharmony_ci 119062306a36Sopenharmony_ci count += temp; 119162306a36Sopenharmony_ci sector += temp >> SECTOR_SHIFT; 119262306a36Sopenharmony_ci } 119362306a36Sopenharmony_ci return 0; 119462306a36Sopenharmony_ci} 119562306a36Sopenharmony_ci 119662306a36Sopenharmony_cistatic void nullb_fill_pattern(struct nullb *nullb, struct page *page, 119762306a36Sopenharmony_ci unsigned int len, unsigned int off) 119862306a36Sopenharmony_ci{ 119962306a36Sopenharmony_ci memset_page(page, off, 0xff, len); 120062306a36Sopenharmony_ci} 120162306a36Sopenharmony_ci 120262306a36Sopenharmony_ciblk_status_t null_handle_discard(struct nullb_device *dev, 120362306a36Sopenharmony_ci sector_t sector, sector_t nr_sectors) 120462306a36Sopenharmony_ci{ 120562306a36Sopenharmony_ci struct nullb *nullb = dev->nullb; 120662306a36Sopenharmony_ci size_t n = nr_sectors << SECTOR_SHIFT; 120762306a36Sopenharmony_ci size_t temp; 120862306a36Sopenharmony_ci 120962306a36Sopenharmony_ci spin_lock_irq(&nullb->lock); 121062306a36Sopenharmony_ci while (n > 0) { 121162306a36Sopenharmony_ci temp = min_t(size_t, n, dev->blocksize); 121262306a36Sopenharmony_ci null_free_sector(nullb, sector, false); 121362306a36Sopenharmony_ci if (null_cache_active(nullb)) 121462306a36Sopenharmony_ci null_free_sector(nullb, sector, true); 121562306a36Sopenharmony_ci sector += temp >> SECTOR_SHIFT; 121662306a36Sopenharmony_ci n -= temp; 121762306a36Sopenharmony_ci } 121862306a36Sopenharmony_ci spin_unlock_irq(&nullb->lock); 121962306a36Sopenharmony_ci 122062306a36Sopenharmony_ci return BLK_STS_OK; 122162306a36Sopenharmony_ci} 122262306a36Sopenharmony_ci 122362306a36Sopenharmony_cistatic int null_handle_flush(struct nullb *nullb) 122462306a36Sopenharmony_ci{ 122562306a36Sopenharmony_ci int err; 122662306a36Sopenharmony_ci 122762306a36Sopenharmony_ci if (!null_cache_active(nullb)) 122862306a36Sopenharmony_ci return 0; 122962306a36Sopenharmony_ci 123062306a36Sopenharmony_ci spin_lock_irq(&nullb->lock); 123162306a36Sopenharmony_ci while (true) { 123262306a36Sopenharmony_ci err = null_make_cache_space(nullb, 123362306a36Sopenharmony_ci nullb->dev->cache_size * 1024 * 1024); 123462306a36Sopenharmony_ci if (err || nullb->dev->curr_cache == 0) 123562306a36Sopenharmony_ci break; 123662306a36Sopenharmony_ci } 123762306a36Sopenharmony_ci 123862306a36Sopenharmony_ci WARN_ON(!radix_tree_empty(&nullb->dev->cache)); 123962306a36Sopenharmony_ci spin_unlock_irq(&nullb->lock); 124062306a36Sopenharmony_ci return err; 124162306a36Sopenharmony_ci} 124262306a36Sopenharmony_ci 124362306a36Sopenharmony_cistatic int null_transfer(struct nullb *nullb, struct page *page, 124462306a36Sopenharmony_ci unsigned int len, unsigned int off, bool is_write, sector_t sector, 124562306a36Sopenharmony_ci bool is_fua) 124662306a36Sopenharmony_ci{ 124762306a36Sopenharmony_ci struct nullb_device *dev = nullb->dev; 124862306a36Sopenharmony_ci unsigned int valid_len = len; 124962306a36Sopenharmony_ci int err = 0; 125062306a36Sopenharmony_ci 125162306a36Sopenharmony_ci if (!is_write) { 125262306a36Sopenharmony_ci if (dev->zoned) 125362306a36Sopenharmony_ci valid_len = null_zone_valid_read_len(nullb, 125462306a36Sopenharmony_ci sector, len); 125562306a36Sopenharmony_ci 125662306a36Sopenharmony_ci if (valid_len) { 125762306a36Sopenharmony_ci err = copy_from_nullb(nullb, page, off, 125862306a36Sopenharmony_ci sector, valid_len); 125962306a36Sopenharmony_ci off += valid_len; 126062306a36Sopenharmony_ci len -= valid_len; 126162306a36Sopenharmony_ci } 126262306a36Sopenharmony_ci 126362306a36Sopenharmony_ci if (len) 126462306a36Sopenharmony_ci nullb_fill_pattern(nullb, page, len, off); 126562306a36Sopenharmony_ci flush_dcache_page(page); 126662306a36Sopenharmony_ci } else { 126762306a36Sopenharmony_ci flush_dcache_page(page); 126862306a36Sopenharmony_ci err = copy_to_nullb(nullb, page, off, sector, len, is_fua); 126962306a36Sopenharmony_ci } 127062306a36Sopenharmony_ci 127162306a36Sopenharmony_ci return err; 127262306a36Sopenharmony_ci} 127362306a36Sopenharmony_ci 127462306a36Sopenharmony_cistatic int null_handle_rq(struct nullb_cmd *cmd) 127562306a36Sopenharmony_ci{ 127662306a36Sopenharmony_ci struct request *rq = cmd->rq; 127762306a36Sopenharmony_ci struct nullb *nullb = cmd->nq->dev->nullb; 127862306a36Sopenharmony_ci int err; 127962306a36Sopenharmony_ci unsigned int len; 128062306a36Sopenharmony_ci sector_t sector = blk_rq_pos(rq); 128162306a36Sopenharmony_ci struct req_iterator iter; 128262306a36Sopenharmony_ci struct bio_vec bvec; 128362306a36Sopenharmony_ci 128462306a36Sopenharmony_ci spin_lock_irq(&nullb->lock); 128562306a36Sopenharmony_ci rq_for_each_segment(bvec, rq, iter) { 128662306a36Sopenharmony_ci len = bvec.bv_len; 128762306a36Sopenharmony_ci err = null_transfer(nullb, bvec.bv_page, len, bvec.bv_offset, 128862306a36Sopenharmony_ci op_is_write(req_op(rq)), sector, 128962306a36Sopenharmony_ci rq->cmd_flags & REQ_FUA); 129062306a36Sopenharmony_ci if (err) { 129162306a36Sopenharmony_ci spin_unlock_irq(&nullb->lock); 129262306a36Sopenharmony_ci return err; 129362306a36Sopenharmony_ci } 129462306a36Sopenharmony_ci sector += len >> SECTOR_SHIFT; 129562306a36Sopenharmony_ci } 129662306a36Sopenharmony_ci spin_unlock_irq(&nullb->lock); 129762306a36Sopenharmony_ci 129862306a36Sopenharmony_ci return 0; 129962306a36Sopenharmony_ci} 130062306a36Sopenharmony_ci 130162306a36Sopenharmony_cistatic int null_handle_bio(struct nullb_cmd *cmd) 130262306a36Sopenharmony_ci{ 130362306a36Sopenharmony_ci struct bio *bio = cmd->bio; 130462306a36Sopenharmony_ci struct nullb *nullb = cmd->nq->dev->nullb; 130562306a36Sopenharmony_ci int err; 130662306a36Sopenharmony_ci unsigned int len; 130762306a36Sopenharmony_ci sector_t sector = bio->bi_iter.bi_sector; 130862306a36Sopenharmony_ci struct bio_vec bvec; 130962306a36Sopenharmony_ci struct bvec_iter iter; 131062306a36Sopenharmony_ci 131162306a36Sopenharmony_ci spin_lock_irq(&nullb->lock); 131262306a36Sopenharmony_ci bio_for_each_segment(bvec, bio, iter) { 131362306a36Sopenharmony_ci len = bvec.bv_len; 131462306a36Sopenharmony_ci err = null_transfer(nullb, bvec.bv_page, len, bvec.bv_offset, 131562306a36Sopenharmony_ci op_is_write(bio_op(bio)), sector, 131662306a36Sopenharmony_ci bio->bi_opf & REQ_FUA); 131762306a36Sopenharmony_ci if (err) { 131862306a36Sopenharmony_ci spin_unlock_irq(&nullb->lock); 131962306a36Sopenharmony_ci return err; 132062306a36Sopenharmony_ci } 132162306a36Sopenharmony_ci sector += len >> SECTOR_SHIFT; 132262306a36Sopenharmony_ci } 132362306a36Sopenharmony_ci spin_unlock_irq(&nullb->lock); 132462306a36Sopenharmony_ci return 0; 132562306a36Sopenharmony_ci} 132662306a36Sopenharmony_ci 132762306a36Sopenharmony_cistatic void null_stop_queue(struct nullb *nullb) 132862306a36Sopenharmony_ci{ 132962306a36Sopenharmony_ci struct request_queue *q = nullb->q; 133062306a36Sopenharmony_ci 133162306a36Sopenharmony_ci if (nullb->dev->queue_mode == NULL_Q_MQ) 133262306a36Sopenharmony_ci blk_mq_stop_hw_queues(q); 133362306a36Sopenharmony_ci} 133462306a36Sopenharmony_ci 133562306a36Sopenharmony_cistatic void null_restart_queue_async(struct nullb *nullb) 133662306a36Sopenharmony_ci{ 133762306a36Sopenharmony_ci struct request_queue *q = nullb->q; 133862306a36Sopenharmony_ci 133962306a36Sopenharmony_ci if (nullb->dev->queue_mode == NULL_Q_MQ) 134062306a36Sopenharmony_ci blk_mq_start_stopped_hw_queues(q, true); 134162306a36Sopenharmony_ci} 134262306a36Sopenharmony_ci 134362306a36Sopenharmony_cistatic inline blk_status_t null_handle_throttled(struct nullb_cmd *cmd) 134462306a36Sopenharmony_ci{ 134562306a36Sopenharmony_ci struct nullb_device *dev = cmd->nq->dev; 134662306a36Sopenharmony_ci struct nullb *nullb = dev->nullb; 134762306a36Sopenharmony_ci blk_status_t sts = BLK_STS_OK; 134862306a36Sopenharmony_ci struct request *rq = cmd->rq; 134962306a36Sopenharmony_ci 135062306a36Sopenharmony_ci if (!hrtimer_active(&nullb->bw_timer)) 135162306a36Sopenharmony_ci hrtimer_restart(&nullb->bw_timer); 135262306a36Sopenharmony_ci 135362306a36Sopenharmony_ci if (atomic_long_sub_return(blk_rq_bytes(rq), &nullb->cur_bytes) < 0) { 135462306a36Sopenharmony_ci null_stop_queue(nullb); 135562306a36Sopenharmony_ci /* race with timer */ 135662306a36Sopenharmony_ci if (atomic_long_read(&nullb->cur_bytes) > 0) 135762306a36Sopenharmony_ci null_restart_queue_async(nullb); 135862306a36Sopenharmony_ci /* requeue request */ 135962306a36Sopenharmony_ci sts = BLK_STS_DEV_RESOURCE; 136062306a36Sopenharmony_ci } 136162306a36Sopenharmony_ci return sts; 136262306a36Sopenharmony_ci} 136362306a36Sopenharmony_ci 136462306a36Sopenharmony_cistatic inline blk_status_t null_handle_badblocks(struct nullb_cmd *cmd, 136562306a36Sopenharmony_ci sector_t sector, 136662306a36Sopenharmony_ci sector_t nr_sectors) 136762306a36Sopenharmony_ci{ 136862306a36Sopenharmony_ci struct badblocks *bb = &cmd->nq->dev->badblocks; 136962306a36Sopenharmony_ci sector_t first_bad; 137062306a36Sopenharmony_ci int bad_sectors; 137162306a36Sopenharmony_ci 137262306a36Sopenharmony_ci if (badblocks_check(bb, sector, nr_sectors, &first_bad, &bad_sectors)) 137362306a36Sopenharmony_ci return BLK_STS_IOERR; 137462306a36Sopenharmony_ci 137562306a36Sopenharmony_ci return BLK_STS_OK; 137662306a36Sopenharmony_ci} 137762306a36Sopenharmony_ci 137862306a36Sopenharmony_cistatic inline blk_status_t null_handle_memory_backed(struct nullb_cmd *cmd, 137962306a36Sopenharmony_ci enum req_op op, 138062306a36Sopenharmony_ci sector_t sector, 138162306a36Sopenharmony_ci sector_t nr_sectors) 138262306a36Sopenharmony_ci{ 138362306a36Sopenharmony_ci struct nullb_device *dev = cmd->nq->dev; 138462306a36Sopenharmony_ci int err; 138562306a36Sopenharmony_ci 138662306a36Sopenharmony_ci if (op == REQ_OP_DISCARD) 138762306a36Sopenharmony_ci return null_handle_discard(dev, sector, nr_sectors); 138862306a36Sopenharmony_ci 138962306a36Sopenharmony_ci if (dev->queue_mode == NULL_Q_BIO) 139062306a36Sopenharmony_ci err = null_handle_bio(cmd); 139162306a36Sopenharmony_ci else 139262306a36Sopenharmony_ci err = null_handle_rq(cmd); 139362306a36Sopenharmony_ci 139462306a36Sopenharmony_ci return errno_to_blk_status(err); 139562306a36Sopenharmony_ci} 139662306a36Sopenharmony_ci 139762306a36Sopenharmony_cistatic void nullb_zero_read_cmd_buffer(struct nullb_cmd *cmd) 139862306a36Sopenharmony_ci{ 139962306a36Sopenharmony_ci struct nullb_device *dev = cmd->nq->dev; 140062306a36Sopenharmony_ci struct bio *bio; 140162306a36Sopenharmony_ci 140262306a36Sopenharmony_ci if (dev->memory_backed) 140362306a36Sopenharmony_ci return; 140462306a36Sopenharmony_ci 140562306a36Sopenharmony_ci if (dev->queue_mode == NULL_Q_BIO && bio_op(cmd->bio) == REQ_OP_READ) { 140662306a36Sopenharmony_ci zero_fill_bio(cmd->bio); 140762306a36Sopenharmony_ci } else if (req_op(cmd->rq) == REQ_OP_READ) { 140862306a36Sopenharmony_ci __rq_for_each_bio(bio, cmd->rq) 140962306a36Sopenharmony_ci zero_fill_bio(bio); 141062306a36Sopenharmony_ci } 141162306a36Sopenharmony_ci} 141262306a36Sopenharmony_ci 141362306a36Sopenharmony_cistatic inline void nullb_complete_cmd(struct nullb_cmd *cmd) 141462306a36Sopenharmony_ci{ 141562306a36Sopenharmony_ci /* 141662306a36Sopenharmony_ci * Since root privileges are required to configure the null_blk 141762306a36Sopenharmony_ci * driver, it is fine that this driver does not initialize the 141862306a36Sopenharmony_ci * data buffers of read commands. Zero-initialize these buffers 141962306a36Sopenharmony_ci * anyway if KMSAN is enabled to prevent that KMSAN complains 142062306a36Sopenharmony_ci * about null_blk not initializing read data buffers. 142162306a36Sopenharmony_ci */ 142262306a36Sopenharmony_ci if (IS_ENABLED(CONFIG_KMSAN)) 142362306a36Sopenharmony_ci nullb_zero_read_cmd_buffer(cmd); 142462306a36Sopenharmony_ci 142562306a36Sopenharmony_ci /* Complete IO by inline, softirq or timer */ 142662306a36Sopenharmony_ci switch (cmd->nq->dev->irqmode) { 142762306a36Sopenharmony_ci case NULL_IRQ_SOFTIRQ: 142862306a36Sopenharmony_ci switch (cmd->nq->dev->queue_mode) { 142962306a36Sopenharmony_ci case NULL_Q_MQ: 143062306a36Sopenharmony_ci blk_mq_complete_request(cmd->rq); 143162306a36Sopenharmony_ci break; 143262306a36Sopenharmony_ci case NULL_Q_BIO: 143362306a36Sopenharmony_ci /* 143462306a36Sopenharmony_ci * XXX: no proper submitting cpu information available. 143562306a36Sopenharmony_ci */ 143662306a36Sopenharmony_ci end_cmd(cmd); 143762306a36Sopenharmony_ci break; 143862306a36Sopenharmony_ci } 143962306a36Sopenharmony_ci break; 144062306a36Sopenharmony_ci case NULL_IRQ_NONE: 144162306a36Sopenharmony_ci end_cmd(cmd); 144262306a36Sopenharmony_ci break; 144362306a36Sopenharmony_ci case NULL_IRQ_TIMER: 144462306a36Sopenharmony_ci null_cmd_end_timer(cmd); 144562306a36Sopenharmony_ci break; 144662306a36Sopenharmony_ci } 144762306a36Sopenharmony_ci} 144862306a36Sopenharmony_ci 144962306a36Sopenharmony_ciblk_status_t null_process_cmd(struct nullb_cmd *cmd, enum req_op op, 145062306a36Sopenharmony_ci sector_t sector, unsigned int nr_sectors) 145162306a36Sopenharmony_ci{ 145262306a36Sopenharmony_ci struct nullb_device *dev = cmd->nq->dev; 145362306a36Sopenharmony_ci blk_status_t ret; 145462306a36Sopenharmony_ci 145562306a36Sopenharmony_ci if (dev->badblocks.shift != -1) { 145662306a36Sopenharmony_ci ret = null_handle_badblocks(cmd, sector, nr_sectors); 145762306a36Sopenharmony_ci if (ret != BLK_STS_OK) 145862306a36Sopenharmony_ci return ret; 145962306a36Sopenharmony_ci } 146062306a36Sopenharmony_ci 146162306a36Sopenharmony_ci if (dev->memory_backed) 146262306a36Sopenharmony_ci return null_handle_memory_backed(cmd, op, sector, nr_sectors); 146362306a36Sopenharmony_ci 146462306a36Sopenharmony_ci return BLK_STS_OK; 146562306a36Sopenharmony_ci} 146662306a36Sopenharmony_ci 146762306a36Sopenharmony_cistatic blk_status_t null_handle_cmd(struct nullb_cmd *cmd, sector_t sector, 146862306a36Sopenharmony_ci sector_t nr_sectors, enum req_op op) 146962306a36Sopenharmony_ci{ 147062306a36Sopenharmony_ci struct nullb_device *dev = cmd->nq->dev; 147162306a36Sopenharmony_ci struct nullb *nullb = dev->nullb; 147262306a36Sopenharmony_ci blk_status_t sts; 147362306a36Sopenharmony_ci 147462306a36Sopenharmony_ci if (test_bit(NULLB_DEV_FL_THROTTLED, &dev->flags)) { 147562306a36Sopenharmony_ci sts = null_handle_throttled(cmd); 147662306a36Sopenharmony_ci if (sts != BLK_STS_OK) 147762306a36Sopenharmony_ci return sts; 147862306a36Sopenharmony_ci } 147962306a36Sopenharmony_ci 148062306a36Sopenharmony_ci if (op == REQ_OP_FLUSH) { 148162306a36Sopenharmony_ci cmd->error = errno_to_blk_status(null_handle_flush(nullb)); 148262306a36Sopenharmony_ci goto out; 148362306a36Sopenharmony_ci } 148462306a36Sopenharmony_ci 148562306a36Sopenharmony_ci if (dev->zoned) 148662306a36Sopenharmony_ci sts = null_process_zoned_cmd(cmd, op, sector, nr_sectors); 148762306a36Sopenharmony_ci else 148862306a36Sopenharmony_ci sts = null_process_cmd(cmd, op, sector, nr_sectors); 148962306a36Sopenharmony_ci 149062306a36Sopenharmony_ci /* Do not overwrite errors (e.g. timeout errors) */ 149162306a36Sopenharmony_ci if (cmd->error == BLK_STS_OK) 149262306a36Sopenharmony_ci cmd->error = sts; 149362306a36Sopenharmony_ci 149462306a36Sopenharmony_ciout: 149562306a36Sopenharmony_ci nullb_complete_cmd(cmd); 149662306a36Sopenharmony_ci return BLK_STS_OK; 149762306a36Sopenharmony_ci} 149862306a36Sopenharmony_ci 149962306a36Sopenharmony_cistatic enum hrtimer_restart nullb_bwtimer_fn(struct hrtimer *timer) 150062306a36Sopenharmony_ci{ 150162306a36Sopenharmony_ci struct nullb *nullb = container_of(timer, struct nullb, bw_timer); 150262306a36Sopenharmony_ci ktime_t timer_interval = ktime_set(0, TIMER_INTERVAL); 150362306a36Sopenharmony_ci unsigned int mbps = nullb->dev->mbps; 150462306a36Sopenharmony_ci 150562306a36Sopenharmony_ci if (atomic_long_read(&nullb->cur_bytes) == mb_per_tick(mbps)) 150662306a36Sopenharmony_ci return HRTIMER_NORESTART; 150762306a36Sopenharmony_ci 150862306a36Sopenharmony_ci atomic_long_set(&nullb->cur_bytes, mb_per_tick(mbps)); 150962306a36Sopenharmony_ci null_restart_queue_async(nullb); 151062306a36Sopenharmony_ci 151162306a36Sopenharmony_ci hrtimer_forward_now(&nullb->bw_timer, timer_interval); 151262306a36Sopenharmony_ci 151362306a36Sopenharmony_ci return HRTIMER_RESTART; 151462306a36Sopenharmony_ci} 151562306a36Sopenharmony_ci 151662306a36Sopenharmony_cistatic void nullb_setup_bwtimer(struct nullb *nullb) 151762306a36Sopenharmony_ci{ 151862306a36Sopenharmony_ci ktime_t timer_interval = ktime_set(0, TIMER_INTERVAL); 151962306a36Sopenharmony_ci 152062306a36Sopenharmony_ci hrtimer_init(&nullb->bw_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); 152162306a36Sopenharmony_ci nullb->bw_timer.function = nullb_bwtimer_fn; 152262306a36Sopenharmony_ci atomic_long_set(&nullb->cur_bytes, mb_per_tick(nullb->dev->mbps)); 152362306a36Sopenharmony_ci hrtimer_start(&nullb->bw_timer, timer_interval, HRTIMER_MODE_REL); 152462306a36Sopenharmony_ci} 152562306a36Sopenharmony_ci 152662306a36Sopenharmony_cistatic struct nullb_queue *nullb_to_queue(struct nullb *nullb) 152762306a36Sopenharmony_ci{ 152862306a36Sopenharmony_ci int index = 0; 152962306a36Sopenharmony_ci 153062306a36Sopenharmony_ci if (nullb->nr_queues != 1) 153162306a36Sopenharmony_ci index = raw_smp_processor_id() / ((nr_cpu_ids + nullb->nr_queues - 1) / nullb->nr_queues); 153262306a36Sopenharmony_ci 153362306a36Sopenharmony_ci return &nullb->queues[index]; 153462306a36Sopenharmony_ci} 153562306a36Sopenharmony_ci 153662306a36Sopenharmony_cistatic void null_submit_bio(struct bio *bio) 153762306a36Sopenharmony_ci{ 153862306a36Sopenharmony_ci sector_t sector = bio->bi_iter.bi_sector; 153962306a36Sopenharmony_ci sector_t nr_sectors = bio_sectors(bio); 154062306a36Sopenharmony_ci struct nullb *nullb = bio->bi_bdev->bd_disk->private_data; 154162306a36Sopenharmony_ci struct nullb_queue *nq = nullb_to_queue(nullb); 154262306a36Sopenharmony_ci 154362306a36Sopenharmony_ci null_handle_cmd(alloc_cmd(nq, bio), sector, nr_sectors, bio_op(bio)); 154462306a36Sopenharmony_ci} 154562306a36Sopenharmony_ci 154662306a36Sopenharmony_ci#ifdef CONFIG_BLK_DEV_NULL_BLK_FAULT_INJECTION 154762306a36Sopenharmony_ci 154862306a36Sopenharmony_cistatic bool should_timeout_request(struct request *rq) 154962306a36Sopenharmony_ci{ 155062306a36Sopenharmony_ci struct nullb_cmd *cmd = blk_mq_rq_to_pdu(rq); 155162306a36Sopenharmony_ci struct nullb_device *dev = cmd->nq->dev; 155262306a36Sopenharmony_ci 155362306a36Sopenharmony_ci return should_fail(&dev->timeout_config.attr, 1); 155462306a36Sopenharmony_ci} 155562306a36Sopenharmony_ci 155662306a36Sopenharmony_cistatic bool should_requeue_request(struct request *rq) 155762306a36Sopenharmony_ci{ 155862306a36Sopenharmony_ci struct nullb_cmd *cmd = blk_mq_rq_to_pdu(rq); 155962306a36Sopenharmony_ci struct nullb_device *dev = cmd->nq->dev; 156062306a36Sopenharmony_ci 156162306a36Sopenharmony_ci return should_fail(&dev->requeue_config.attr, 1); 156262306a36Sopenharmony_ci} 156362306a36Sopenharmony_ci 156462306a36Sopenharmony_cistatic bool should_init_hctx_fail(struct nullb_device *dev) 156562306a36Sopenharmony_ci{ 156662306a36Sopenharmony_ci return should_fail(&dev->init_hctx_fault_config.attr, 1); 156762306a36Sopenharmony_ci} 156862306a36Sopenharmony_ci 156962306a36Sopenharmony_ci#else 157062306a36Sopenharmony_ci 157162306a36Sopenharmony_cistatic bool should_timeout_request(struct request *rq) 157262306a36Sopenharmony_ci{ 157362306a36Sopenharmony_ci return false; 157462306a36Sopenharmony_ci} 157562306a36Sopenharmony_ci 157662306a36Sopenharmony_cistatic bool should_requeue_request(struct request *rq) 157762306a36Sopenharmony_ci{ 157862306a36Sopenharmony_ci return false; 157962306a36Sopenharmony_ci} 158062306a36Sopenharmony_ci 158162306a36Sopenharmony_cistatic bool should_init_hctx_fail(struct nullb_device *dev) 158262306a36Sopenharmony_ci{ 158362306a36Sopenharmony_ci return false; 158462306a36Sopenharmony_ci} 158562306a36Sopenharmony_ci 158662306a36Sopenharmony_ci#endif 158762306a36Sopenharmony_ci 158862306a36Sopenharmony_cistatic void null_map_queues(struct blk_mq_tag_set *set) 158962306a36Sopenharmony_ci{ 159062306a36Sopenharmony_ci struct nullb *nullb = set->driver_data; 159162306a36Sopenharmony_ci int i, qoff; 159262306a36Sopenharmony_ci unsigned int submit_queues = g_submit_queues; 159362306a36Sopenharmony_ci unsigned int poll_queues = g_poll_queues; 159462306a36Sopenharmony_ci 159562306a36Sopenharmony_ci if (nullb) { 159662306a36Sopenharmony_ci struct nullb_device *dev = nullb->dev; 159762306a36Sopenharmony_ci 159862306a36Sopenharmony_ci /* 159962306a36Sopenharmony_ci * Refer nr_hw_queues of the tag set to check if the expected 160062306a36Sopenharmony_ci * number of hardware queues are prepared. If block layer failed 160162306a36Sopenharmony_ci * to prepare them, use previous numbers of submit queues and 160262306a36Sopenharmony_ci * poll queues to map queues. 160362306a36Sopenharmony_ci */ 160462306a36Sopenharmony_ci if (set->nr_hw_queues == 160562306a36Sopenharmony_ci dev->submit_queues + dev->poll_queues) { 160662306a36Sopenharmony_ci submit_queues = dev->submit_queues; 160762306a36Sopenharmony_ci poll_queues = dev->poll_queues; 160862306a36Sopenharmony_ci } else if (set->nr_hw_queues == 160962306a36Sopenharmony_ci dev->prev_submit_queues + dev->prev_poll_queues) { 161062306a36Sopenharmony_ci submit_queues = dev->prev_submit_queues; 161162306a36Sopenharmony_ci poll_queues = dev->prev_poll_queues; 161262306a36Sopenharmony_ci } else { 161362306a36Sopenharmony_ci pr_warn("tag set has unexpected nr_hw_queues: %d\n", 161462306a36Sopenharmony_ci set->nr_hw_queues); 161562306a36Sopenharmony_ci WARN_ON_ONCE(true); 161662306a36Sopenharmony_ci submit_queues = 1; 161762306a36Sopenharmony_ci poll_queues = 0; 161862306a36Sopenharmony_ci } 161962306a36Sopenharmony_ci } 162062306a36Sopenharmony_ci 162162306a36Sopenharmony_ci for (i = 0, qoff = 0; i < set->nr_maps; i++) { 162262306a36Sopenharmony_ci struct blk_mq_queue_map *map = &set->map[i]; 162362306a36Sopenharmony_ci 162462306a36Sopenharmony_ci switch (i) { 162562306a36Sopenharmony_ci case HCTX_TYPE_DEFAULT: 162662306a36Sopenharmony_ci map->nr_queues = submit_queues; 162762306a36Sopenharmony_ci break; 162862306a36Sopenharmony_ci case HCTX_TYPE_READ: 162962306a36Sopenharmony_ci map->nr_queues = 0; 163062306a36Sopenharmony_ci continue; 163162306a36Sopenharmony_ci case HCTX_TYPE_POLL: 163262306a36Sopenharmony_ci map->nr_queues = poll_queues; 163362306a36Sopenharmony_ci break; 163462306a36Sopenharmony_ci } 163562306a36Sopenharmony_ci map->queue_offset = qoff; 163662306a36Sopenharmony_ci qoff += map->nr_queues; 163762306a36Sopenharmony_ci blk_mq_map_queues(map); 163862306a36Sopenharmony_ci } 163962306a36Sopenharmony_ci} 164062306a36Sopenharmony_ci 164162306a36Sopenharmony_cistatic int null_poll(struct blk_mq_hw_ctx *hctx, struct io_comp_batch *iob) 164262306a36Sopenharmony_ci{ 164362306a36Sopenharmony_ci struct nullb_queue *nq = hctx->driver_data; 164462306a36Sopenharmony_ci LIST_HEAD(list); 164562306a36Sopenharmony_ci int nr = 0; 164662306a36Sopenharmony_ci struct request *rq; 164762306a36Sopenharmony_ci 164862306a36Sopenharmony_ci spin_lock(&nq->poll_lock); 164962306a36Sopenharmony_ci list_splice_init(&nq->poll_list, &list); 165062306a36Sopenharmony_ci list_for_each_entry(rq, &list, queuelist) 165162306a36Sopenharmony_ci blk_mq_set_request_complete(rq); 165262306a36Sopenharmony_ci spin_unlock(&nq->poll_lock); 165362306a36Sopenharmony_ci 165462306a36Sopenharmony_ci while (!list_empty(&list)) { 165562306a36Sopenharmony_ci struct nullb_cmd *cmd; 165662306a36Sopenharmony_ci struct request *req; 165762306a36Sopenharmony_ci 165862306a36Sopenharmony_ci req = list_first_entry(&list, struct request, queuelist); 165962306a36Sopenharmony_ci list_del_init(&req->queuelist); 166062306a36Sopenharmony_ci cmd = blk_mq_rq_to_pdu(req); 166162306a36Sopenharmony_ci cmd->error = null_process_cmd(cmd, req_op(req), blk_rq_pos(req), 166262306a36Sopenharmony_ci blk_rq_sectors(req)); 166362306a36Sopenharmony_ci if (!blk_mq_add_to_batch(req, iob, (__force int) cmd->error, 166462306a36Sopenharmony_ci blk_mq_end_request_batch)) 166562306a36Sopenharmony_ci end_cmd(cmd); 166662306a36Sopenharmony_ci nr++; 166762306a36Sopenharmony_ci } 166862306a36Sopenharmony_ci 166962306a36Sopenharmony_ci return nr; 167062306a36Sopenharmony_ci} 167162306a36Sopenharmony_ci 167262306a36Sopenharmony_cistatic enum blk_eh_timer_return null_timeout_rq(struct request *rq) 167362306a36Sopenharmony_ci{ 167462306a36Sopenharmony_ci struct blk_mq_hw_ctx *hctx = rq->mq_hctx; 167562306a36Sopenharmony_ci struct nullb_cmd *cmd = blk_mq_rq_to_pdu(rq); 167662306a36Sopenharmony_ci 167762306a36Sopenharmony_ci if (hctx->type == HCTX_TYPE_POLL) { 167862306a36Sopenharmony_ci struct nullb_queue *nq = hctx->driver_data; 167962306a36Sopenharmony_ci 168062306a36Sopenharmony_ci spin_lock(&nq->poll_lock); 168162306a36Sopenharmony_ci /* The request may have completed meanwhile. */ 168262306a36Sopenharmony_ci if (blk_mq_request_completed(rq)) { 168362306a36Sopenharmony_ci spin_unlock(&nq->poll_lock); 168462306a36Sopenharmony_ci return BLK_EH_DONE; 168562306a36Sopenharmony_ci } 168662306a36Sopenharmony_ci list_del_init(&rq->queuelist); 168762306a36Sopenharmony_ci spin_unlock(&nq->poll_lock); 168862306a36Sopenharmony_ci } 168962306a36Sopenharmony_ci 169062306a36Sopenharmony_ci pr_info("rq %p timed out\n", rq); 169162306a36Sopenharmony_ci 169262306a36Sopenharmony_ci /* 169362306a36Sopenharmony_ci * If the device is marked as blocking (i.e. memory backed or zoned 169462306a36Sopenharmony_ci * device), the submission path may be blocked waiting for resources 169562306a36Sopenharmony_ci * and cause real timeouts. For these real timeouts, the submission 169662306a36Sopenharmony_ci * path will complete the request using blk_mq_complete_request(). 169762306a36Sopenharmony_ci * Only fake timeouts need to execute blk_mq_complete_request() here. 169862306a36Sopenharmony_ci */ 169962306a36Sopenharmony_ci cmd->error = BLK_STS_TIMEOUT; 170062306a36Sopenharmony_ci if (cmd->fake_timeout || hctx->type == HCTX_TYPE_POLL) 170162306a36Sopenharmony_ci blk_mq_complete_request(rq); 170262306a36Sopenharmony_ci return BLK_EH_DONE; 170362306a36Sopenharmony_ci} 170462306a36Sopenharmony_ci 170562306a36Sopenharmony_cistatic blk_status_t null_queue_rq(struct blk_mq_hw_ctx *hctx, 170662306a36Sopenharmony_ci const struct blk_mq_queue_data *bd) 170762306a36Sopenharmony_ci{ 170862306a36Sopenharmony_ci struct request *rq = bd->rq; 170962306a36Sopenharmony_ci struct nullb_cmd *cmd = blk_mq_rq_to_pdu(rq); 171062306a36Sopenharmony_ci struct nullb_queue *nq = hctx->driver_data; 171162306a36Sopenharmony_ci sector_t nr_sectors = blk_rq_sectors(rq); 171262306a36Sopenharmony_ci sector_t sector = blk_rq_pos(rq); 171362306a36Sopenharmony_ci const bool is_poll = hctx->type == HCTX_TYPE_POLL; 171462306a36Sopenharmony_ci 171562306a36Sopenharmony_ci might_sleep_if(hctx->flags & BLK_MQ_F_BLOCKING); 171662306a36Sopenharmony_ci 171762306a36Sopenharmony_ci if (!is_poll && nq->dev->irqmode == NULL_IRQ_TIMER) { 171862306a36Sopenharmony_ci hrtimer_init(&cmd->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); 171962306a36Sopenharmony_ci cmd->timer.function = null_cmd_timer_expired; 172062306a36Sopenharmony_ci } 172162306a36Sopenharmony_ci cmd->rq = rq; 172262306a36Sopenharmony_ci cmd->error = BLK_STS_OK; 172362306a36Sopenharmony_ci cmd->nq = nq; 172462306a36Sopenharmony_ci cmd->fake_timeout = should_timeout_request(rq) || 172562306a36Sopenharmony_ci blk_should_fake_timeout(rq->q); 172662306a36Sopenharmony_ci 172762306a36Sopenharmony_ci blk_mq_start_request(rq); 172862306a36Sopenharmony_ci 172962306a36Sopenharmony_ci if (should_requeue_request(rq)) { 173062306a36Sopenharmony_ci /* 173162306a36Sopenharmony_ci * Alternate between hitting the core BUSY path, and the 173262306a36Sopenharmony_ci * driver driven requeue path 173362306a36Sopenharmony_ci */ 173462306a36Sopenharmony_ci nq->requeue_selection++; 173562306a36Sopenharmony_ci if (nq->requeue_selection & 1) 173662306a36Sopenharmony_ci return BLK_STS_RESOURCE; 173762306a36Sopenharmony_ci blk_mq_requeue_request(rq, true); 173862306a36Sopenharmony_ci return BLK_STS_OK; 173962306a36Sopenharmony_ci } 174062306a36Sopenharmony_ci 174162306a36Sopenharmony_ci if (is_poll) { 174262306a36Sopenharmony_ci spin_lock(&nq->poll_lock); 174362306a36Sopenharmony_ci list_add_tail(&rq->queuelist, &nq->poll_list); 174462306a36Sopenharmony_ci spin_unlock(&nq->poll_lock); 174562306a36Sopenharmony_ci return BLK_STS_OK; 174662306a36Sopenharmony_ci } 174762306a36Sopenharmony_ci if (cmd->fake_timeout) 174862306a36Sopenharmony_ci return BLK_STS_OK; 174962306a36Sopenharmony_ci 175062306a36Sopenharmony_ci return null_handle_cmd(cmd, sector, nr_sectors, req_op(rq)); 175162306a36Sopenharmony_ci} 175262306a36Sopenharmony_ci 175362306a36Sopenharmony_cistatic void cleanup_queue(struct nullb_queue *nq) 175462306a36Sopenharmony_ci{ 175562306a36Sopenharmony_ci bitmap_free(nq->tag_map); 175662306a36Sopenharmony_ci kfree(nq->cmds); 175762306a36Sopenharmony_ci} 175862306a36Sopenharmony_ci 175962306a36Sopenharmony_cistatic void cleanup_queues(struct nullb *nullb) 176062306a36Sopenharmony_ci{ 176162306a36Sopenharmony_ci int i; 176262306a36Sopenharmony_ci 176362306a36Sopenharmony_ci for (i = 0; i < nullb->nr_queues; i++) 176462306a36Sopenharmony_ci cleanup_queue(&nullb->queues[i]); 176562306a36Sopenharmony_ci 176662306a36Sopenharmony_ci kfree(nullb->queues); 176762306a36Sopenharmony_ci} 176862306a36Sopenharmony_ci 176962306a36Sopenharmony_cistatic void null_exit_hctx(struct blk_mq_hw_ctx *hctx, unsigned int hctx_idx) 177062306a36Sopenharmony_ci{ 177162306a36Sopenharmony_ci struct nullb_queue *nq = hctx->driver_data; 177262306a36Sopenharmony_ci struct nullb *nullb = nq->dev->nullb; 177362306a36Sopenharmony_ci 177462306a36Sopenharmony_ci nullb->nr_queues--; 177562306a36Sopenharmony_ci} 177662306a36Sopenharmony_ci 177762306a36Sopenharmony_cistatic void null_init_queue(struct nullb *nullb, struct nullb_queue *nq) 177862306a36Sopenharmony_ci{ 177962306a36Sopenharmony_ci init_waitqueue_head(&nq->wait); 178062306a36Sopenharmony_ci nq->queue_depth = nullb->queue_depth; 178162306a36Sopenharmony_ci nq->dev = nullb->dev; 178262306a36Sopenharmony_ci INIT_LIST_HEAD(&nq->poll_list); 178362306a36Sopenharmony_ci spin_lock_init(&nq->poll_lock); 178462306a36Sopenharmony_ci} 178562306a36Sopenharmony_ci 178662306a36Sopenharmony_cistatic int null_init_hctx(struct blk_mq_hw_ctx *hctx, void *driver_data, 178762306a36Sopenharmony_ci unsigned int hctx_idx) 178862306a36Sopenharmony_ci{ 178962306a36Sopenharmony_ci struct nullb *nullb = hctx->queue->queuedata; 179062306a36Sopenharmony_ci struct nullb_queue *nq; 179162306a36Sopenharmony_ci 179262306a36Sopenharmony_ci if (should_init_hctx_fail(nullb->dev)) 179362306a36Sopenharmony_ci return -EFAULT; 179462306a36Sopenharmony_ci 179562306a36Sopenharmony_ci nq = &nullb->queues[hctx_idx]; 179662306a36Sopenharmony_ci hctx->driver_data = nq; 179762306a36Sopenharmony_ci null_init_queue(nullb, nq); 179862306a36Sopenharmony_ci nullb->nr_queues++; 179962306a36Sopenharmony_ci 180062306a36Sopenharmony_ci return 0; 180162306a36Sopenharmony_ci} 180262306a36Sopenharmony_ci 180362306a36Sopenharmony_cistatic const struct blk_mq_ops null_mq_ops = { 180462306a36Sopenharmony_ci .queue_rq = null_queue_rq, 180562306a36Sopenharmony_ci .complete = null_complete_rq, 180662306a36Sopenharmony_ci .timeout = null_timeout_rq, 180762306a36Sopenharmony_ci .poll = null_poll, 180862306a36Sopenharmony_ci .map_queues = null_map_queues, 180962306a36Sopenharmony_ci .init_hctx = null_init_hctx, 181062306a36Sopenharmony_ci .exit_hctx = null_exit_hctx, 181162306a36Sopenharmony_ci}; 181262306a36Sopenharmony_ci 181362306a36Sopenharmony_cistatic void null_del_dev(struct nullb *nullb) 181462306a36Sopenharmony_ci{ 181562306a36Sopenharmony_ci struct nullb_device *dev; 181662306a36Sopenharmony_ci 181762306a36Sopenharmony_ci if (!nullb) 181862306a36Sopenharmony_ci return; 181962306a36Sopenharmony_ci 182062306a36Sopenharmony_ci dev = nullb->dev; 182162306a36Sopenharmony_ci 182262306a36Sopenharmony_ci ida_simple_remove(&nullb_indexes, nullb->index); 182362306a36Sopenharmony_ci 182462306a36Sopenharmony_ci list_del_init(&nullb->list); 182562306a36Sopenharmony_ci 182662306a36Sopenharmony_ci del_gendisk(nullb->disk); 182762306a36Sopenharmony_ci 182862306a36Sopenharmony_ci if (test_bit(NULLB_DEV_FL_THROTTLED, &nullb->dev->flags)) { 182962306a36Sopenharmony_ci hrtimer_cancel(&nullb->bw_timer); 183062306a36Sopenharmony_ci atomic_long_set(&nullb->cur_bytes, LONG_MAX); 183162306a36Sopenharmony_ci null_restart_queue_async(nullb); 183262306a36Sopenharmony_ci } 183362306a36Sopenharmony_ci 183462306a36Sopenharmony_ci put_disk(nullb->disk); 183562306a36Sopenharmony_ci if (dev->queue_mode == NULL_Q_MQ && 183662306a36Sopenharmony_ci nullb->tag_set == &nullb->__tag_set) 183762306a36Sopenharmony_ci blk_mq_free_tag_set(nullb->tag_set); 183862306a36Sopenharmony_ci cleanup_queues(nullb); 183962306a36Sopenharmony_ci if (null_cache_active(nullb)) 184062306a36Sopenharmony_ci null_free_device_storage(nullb->dev, true); 184162306a36Sopenharmony_ci kfree(nullb); 184262306a36Sopenharmony_ci dev->nullb = NULL; 184362306a36Sopenharmony_ci} 184462306a36Sopenharmony_ci 184562306a36Sopenharmony_cistatic void null_config_discard(struct nullb *nullb) 184662306a36Sopenharmony_ci{ 184762306a36Sopenharmony_ci if (nullb->dev->discard == false) 184862306a36Sopenharmony_ci return; 184962306a36Sopenharmony_ci 185062306a36Sopenharmony_ci if (!nullb->dev->memory_backed) { 185162306a36Sopenharmony_ci nullb->dev->discard = false; 185262306a36Sopenharmony_ci pr_info("discard option is ignored without memory backing\n"); 185362306a36Sopenharmony_ci return; 185462306a36Sopenharmony_ci } 185562306a36Sopenharmony_ci 185662306a36Sopenharmony_ci if (nullb->dev->zoned) { 185762306a36Sopenharmony_ci nullb->dev->discard = false; 185862306a36Sopenharmony_ci pr_info("discard option is ignored in zoned mode\n"); 185962306a36Sopenharmony_ci return; 186062306a36Sopenharmony_ci } 186162306a36Sopenharmony_ci 186262306a36Sopenharmony_ci nullb->q->limits.discard_granularity = nullb->dev->blocksize; 186362306a36Sopenharmony_ci blk_queue_max_discard_sectors(nullb->q, UINT_MAX >> 9); 186462306a36Sopenharmony_ci} 186562306a36Sopenharmony_ci 186662306a36Sopenharmony_cistatic const struct block_device_operations null_bio_ops = { 186762306a36Sopenharmony_ci .owner = THIS_MODULE, 186862306a36Sopenharmony_ci .submit_bio = null_submit_bio, 186962306a36Sopenharmony_ci .report_zones = null_report_zones, 187062306a36Sopenharmony_ci}; 187162306a36Sopenharmony_ci 187262306a36Sopenharmony_cistatic const struct block_device_operations null_rq_ops = { 187362306a36Sopenharmony_ci .owner = THIS_MODULE, 187462306a36Sopenharmony_ci .report_zones = null_report_zones, 187562306a36Sopenharmony_ci}; 187662306a36Sopenharmony_ci 187762306a36Sopenharmony_cistatic int setup_commands(struct nullb_queue *nq) 187862306a36Sopenharmony_ci{ 187962306a36Sopenharmony_ci struct nullb_cmd *cmd; 188062306a36Sopenharmony_ci int i; 188162306a36Sopenharmony_ci 188262306a36Sopenharmony_ci nq->cmds = kcalloc(nq->queue_depth, sizeof(*cmd), GFP_KERNEL); 188362306a36Sopenharmony_ci if (!nq->cmds) 188462306a36Sopenharmony_ci return -ENOMEM; 188562306a36Sopenharmony_ci 188662306a36Sopenharmony_ci nq->tag_map = bitmap_zalloc(nq->queue_depth, GFP_KERNEL); 188762306a36Sopenharmony_ci if (!nq->tag_map) { 188862306a36Sopenharmony_ci kfree(nq->cmds); 188962306a36Sopenharmony_ci return -ENOMEM; 189062306a36Sopenharmony_ci } 189162306a36Sopenharmony_ci 189262306a36Sopenharmony_ci for (i = 0; i < nq->queue_depth; i++) { 189362306a36Sopenharmony_ci cmd = &nq->cmds[i]; 189462306a36Sopenharmony_ci cmd->tag = -1U; 189562306a36Sopenharmony_ci } 189662306a36Sopenharmony_ci 189762306a36Sopenharmony_ci return 0; 189862306a36Sopenharmony_ci} 189962306a36Sopenharmony_ci 190062306a36Sopenharmony_cistatic int setup_queues(struct nullb *nullb) 190162306a36Sopenharmony_ci{ 190262306a36Sopenharmony_ci int nqueues = nr_cpu_ids; 190362306a36Sopenharmony_ci 190462306a36Sopenharmony_ci if (g_poll_queues) 190562306a36Sopenharmony_ci nqueues += g_poll_queues; 190662306a36Sopenharmony_ci 190762306a36Sopenharmony_ci nullb->queues = kcalloc(nqueues, sizeof(struct nullb_queue), 190862306a36Sopenharmony_ci GFP_KERNEL); 190962306a36Sopenharmony_ci if (!nullb->queues) 191062306a36Sopenharmony_ci return -ENOMEM; 191162306a36Sopenharmony_ci 191262306a36Sopenharmony_ci nullb->queue_depth = nullb->dev->hw_queue_depth; 191362306a36Sopenharmony_ci return 0; 191462306a36Sopenharmony_ci} 191562306a36Sopenharmony_ci 191662306a36Sopenharmony_cistatic int init_driver_queues(struct nullb *nullb) 191762306a36Sopenharmony_ci{ 191862306a36Sopenharmony_ci struct nullb_queue *nq; 191962306a36Sopenharmony_ci int i, ret = 0; 192062306a36Sopenharmony_ci 192162306a36Sopenharmony_ci for (i = 0; i < nullb->dev->submit_queues; i++) { 192262306a36Sopenharmony_ci nq = &nullb->queues[i]; 192362306a36Sopenharmony_ci 192462306a36Sopenharmony_ci null_init_queue(nullb, nq); 192562306a36Sopenharmony_ci 192662306a36Sopenharmony_ci ret = setup_commands(nq); 192762306a36Sopenharmony_ci if (ret) 192862306a36Sopenharmony_ci return ret; 192962306a36Sopenharmony_ci nullb->nr_queues++; 193062306a36Sopenharmony_ci } 193162306a36Sopenharmony_ci return 0; 193262306a36Sopenharmony_ci} 193362306a36Sopenharmony_ci 193462306a36Sopenharmony_cistatic int null_gendisk_register(struct nullb *nullb) 193562306a36Sopenharmony_ci{ 193662306a36Sopenharmony_ci sector_t size = ((sector_t)nullb->dev->size * SZ_1M) >> SECTOR_SHIFT; 193762306a36Sopenharmony_ci struct gendisk *disk = nullb->disk; 193862306a36Sopenharmony_ci 193962306a36Sopenharmony_ci set_capacity(disk, size); 194062306a36Sopenharmony_ci 194162306a36Sopenharmony_ci disk->major = null_major; 194262306a36Sopenharmony_ci disk->first_minor = nullb->index; 194362306a36Sopenharmony_ci disk->minors = 1; 194462306a36Sopenharmony_ci if (queue_is_mq(nullb->q)) 194562306a36Sopenharmony_ci disk->fops = &null_rq_ops; 194662306a36Sopenharmony_ci else 194762306a36Sopenharmony_ci disk->fops = &null_bio_ops; 194862306a36Sopenharmony_ci disk->private_data = nullb; 194962306a36Sopenharmony_ci strncpy(disk->disk_name, nullb->disk_name, DISK_NAME_LEN); 195062306a36Sopenharmony_ci 195162306a36Sopenharmony_ci if (nullb->dev->zoned) { 195262306a36Sopenharmony_ci int ret = null_register_zoned_dev(nullb); 195362306a36Sopenharmony_ci 195462306a36Sopenharmony_ci if (ret) 195562306a36Sopenharmony_ci return ret; 195662306a36Sopenharmony_ci } 195762306a36Sopenharmony_ci 195862306a36Sopenharmony_ci return add_disk(disk); 195962306a36Sopenharmony_ci} 196062306a36Sopenharmony_ci 196162306a36Sopenharmony_cistatic int null_init_tag_set(struct nullb *nullb, struct blk_mq_tag_set *set) 196262306a36Sopenharmony_ci{ 196362306a36Sopenharmony_ci unsigned int flags = BLK_MQ_F_SHOULD_MERGE; 196462306a36Sopenharmony_ci int hw_queues, numa_node; 196562306a36Sopenharmony_ci unsigned int queue_depth; 196662306a36Sopenharmony_ci int poll_queues; 196762306a36Sopenharmony_ci 196862306a36Sopenharmony_ci if (nullb) { 196962306a36Sopenharmony_ci hw_queues = nullb->dev->submit_queues; 197062306a36Sopenharmony_ci poll_queues = nullb->dev->poll_queues; 197162306a36Sopenharmony_ci queue_depth = nullb->dev->hw_queue_depth; 197262306a36Sopenharmony_ci numa_node = nullb->dev->home_node; 197362306a36Sopenharmony_ci if (nullb->dev->no_sched) 197462306a36Sopenharmony_ci flags |= BLK_MQ_F_NO_SCHED; 197562306a36Sopenharmony_ci if (nullb->dev->shared_tag_bitmap) 197662306a36Sopenharmony_ci flags |= BLK_MQ_F_TAG_HCTX_SHARED; 197762306a36Sopenharmony_ci if (nullb->dev->blocking) 197862306a36Sopenharmony_ci flags |= BLK_MQ_F_BLOCKING; 197962306a36Sopenharmony_ci } else { 198062306a36Sopenharmony_ci hw_queues = g_submit_queues; 198162306a36Sopenharmony_ci poll_queues = g_poll_queues; 198262306a36Sopenharmony_ci queue_depth = g_hw_queue_depth; 198362306a36Sopenharmony_ci numa_node = g_home_node; 198462306a36Sopenharmony_ci if (g_no_sched) 198562306a36Sopenharmony_ci flags |= BLK_MQ_F_NO_SCHED; 198662306a36Sopenharmony_ci if (g_shared_tag_bitmap) 198762306a36Sopenharmony_ci flags |= BLK_MQ_F_TAG_HCTX_SHARED; 198862306a36Sopenharmony_ci if (g_blocking) 198962306a36Sopenharmony_ci flags |= BLK_MQ_F_BLOCKING; 199062306a36Sopenharmony_ci } 199162306a36Sopenharmony_ci 199262306a36Sopenharmony_ci set->ops = &null_mq_ops; 199362306a36Sopenharmony_ci set->cmd_size = sizeof(struct nullb_cmd); 199462306a36Sopenharmony_ci set->flags = flags; 199562306a36Sopenharmony_ci set->driver_data = nullb; 199662306a36Sopenharmony_ci set->nr_hw_queues = hw_queues; 199762306a36Sopenharmony_ci set->queue_depth = queue_depth; 199862306a36Sopenharmony_ci set->numa_node = numa_node; 199962306a36Sopenharmony_ci if (poll_queues) { 200062306a36Sopenharmony_ci set->nr_hw_queues += poll_queues; 200162306a36Sopenharmony_ci set->nr_maps = 3; 200262306a36Sopenharmony_ci } else { 200362306a36Sopenharmony_ci set->nr_maps = 1; 200462306a36Sopenharmony_ci } 200562306a36Sopenharmony_ci 200662306a36Sopenharmony_ci return blk_mq_alloc_tag_set(set); 200762306a36Sopenharmony_ci} 200862306a36Sopenharmony_ci 200962306a36Sopenharmony_cistatic int null_validate_conf(struct nullb_device *dev) 201062306a36Sopenharmony_ci{ 201162306a36Sopenharmony_ci if (dev->queue_mode == NULL_Q_RQ) { 201262306a36Sopenharmony_ci pr_err("legacy IO path is no longer available\n"); 201362306a36Sopenharmony_ci return -EINVAL; 201462306a36Sopenharmony_ci } 201562306a36Sopenharmony_ci 201662306a36Sopenharmony_ci dev->blocksize = round_down(dev->blocksize, 512); 201762306a36Sopenharmony_ci dev->blocksize = clamp_t(unsigned int, dev->blocksize, 512, 4096); 201862306a36Sopenharmony_ci 201962306a36Sopenharmony_ci if (dev->queue_mode == NULL_Q_MQ && dev->use_per_node_hctx) { 202062306a36Sopenharmony_ci if (dev->submit_queues != nr_online_nodes) 202162306a36Sopenharmony_ci dev->submit_queues = nr_online_nodes; 202262306a36Sopenharmony_ci } else if (dev->submit_queues > nr_cpu_ids) 202362306a36Sopenharmony_ci dev->submit_queues = nr_cpu_ids; 202462306a36Sopenharmony_ci else if (dev->submit_queues == 0) 202562306a36Sopenharmony_ci dev->submit_queues = 1; 202662306a36Sopenharmony_ci dev->prev_submit_queues = dev->submit_queues; 202762306a36Sopenharmony_ci 202862306a36Sopenharmony_ci if (dev->poll_queues > g_poll_queues) 202962306a36Sopenharmony_ci dev->poll_queues = g_poll_queues; 203062306a36Sopenharmony_ci dev->prev_poll_queues = dev->poll_queues; 203162306a36Sopenharmony_ci 203262306a36Sopenharmony_ci dev->queue_mode = min_t(unsigned int, dev->queue_mode, NULL_Q_MQ); 203362306a36Sopenharmony_ci dev->irqmode = min_t(unsigned int, dev->irqmode, NULL_IRQ_TIMER); 203462306a36Sopenharmony_ci 203562306a36Sopenharmony_ci /* Do memory allocation, so set blocking */ 203662306a36Sopenharmony_ci if (dev->memory_backed) 203762306a36Sopenharmony_ci dev->blocking = true; 203862306a36Sopenharmony_ci else /* cache is meaningless */ 203962306a36Sopenharmony_ci dev->cache_size = 0; 204062306a36Sopenharmony_ci dev->cache_size = min_t(unsigned long, ULONG_MAX / 1024 / 1024, 204162306a36Sopenharmony_ci dev->cache_size); 204262306a36Sopenharmony_ci dev->mbps = min_t(unsigned int, 1024 * 40, dev->mbps); 204362306a36Sopenharmony_ci /* can not stop a queue */ 204462306a36Sopenharmony_ci if (dev->queue_mode == NULL_Q_BIO) 204562306a36Sopenharmony_ci dev->mbps = 0; 204662306a36Sopenharmony_ci 204762306a36Sopenharmony_ci if (dev->zoned && 204862306a36Sopenharmony_ci (!dev->zone_size || !is_power_of_2(dev->zone_size))) { 204962306a36Sopenharmony_ci pr_err("zone_size must be power-of-two\n"); 205062306a36Sopenharmony_ci return -EINVAL; 205162306a36Sopenharmony_ci } 205262306a36Sopenharmony_ci 205362306a36Sopenharmony_ci return 0; 205462306a36Sopenharmony_ci} 205562306a36Sopenharmony_ci 205662306a36Sopenharmony_ci#ifdef CONFIG_BLK_DEV_NULL_BLK_FAULT_INJECTION 205762306a36Sopenharmony_cistatic bool __null_setup_fault(struct fault_attr *attr, char *str) 205862306a36Sopenharmony_ci{ 205962306a36Sopenharmony_ci if (!str[0]) 206062306a36Sopenharmony_ci return true; 206162306a36Sopenharmony_ci 206262306a36Sopenharmony_ci if (!setup_fault_attr(attr, str)) 206362306a36Sopenharmony_ci return false; 206462306a36Sopenharmony_ci 206562306a36Sopenharmony_ci attr->verbose = 0; 206662306a36Sopenharmony_ci return true; 206762306a36Sopenharmony_ci} 206862306a36Sopenharmony_ci#endif 206962306a36Sopenharmony_ci 207062306a36Sopenharmony_cistatic bool null_setup_fault(void) 207162306a36Sopenharmony_ci{ 207262306a36Sopenharmony_ci#ifdef CONFIG_BLK_DEV_NULL_BLK_FAULT_INJECTION 207362306a36Sopenharmony_ci if (!__null_setup_fault(&null_timeout_attr, g_timeout_str)) 207462306a36Sopenharmony_ci return false; 207562306a36Sopenharmony_ci if (!__null_setup_fault(&null_requeue_attr, g_requeue_str)) 207662306a36Sopenharmony_ci return false; 207762306a36Sopenharmony_ci if (!__null_setup_fault(&null_init_hctx_attr, g_init_hctx_str)) 207862306a36Sopenharmony_ci return false; 207962306a36Sopenharmony_ci#endif 208062306a36Sopenharmony_ci return true; 208162306a36Sopenharmony_ci} 208262306a36Sopenharmony_ci 208362306a36Sopenharmony_cistatic int null_add_dev(struct nullb_device *dev) 208462306a36Sopenharmony_ci{ 208562306a36Sopenharmony_ci struct nullb *nullb; 208662306a36Sopenharmony_ci int rv; 208762306a36Sopenharmony_ci 208862306a36Sopenharmony_ci rv = null_validate_conf(dev); 208962306a36Sopenharmony_ci if (rv) 209062306a36Sopenharmony_ci return rv; 209162306a36Sopenharmony_ci 209262306a36Sopenharmony_ci nullb = kzalloc_node(sizeof(*nullb), GFP_KERNEL, dev->home_node); 209362306a36Sopenharmony_ci if (!nullb) { 209462306a36Sopenharmony_ci rv = -ENOMEM; 209562306a36Sopenharmony_ci goto out; 209662306a36Sopenharmony_ci } 209762306a36Sopenharmony_ci nullb->dev = dev; 209862306a36Sopenharmony_ci dev->nullb = nullb; 209962306a36Sopenharmony_ci 210062306a36Sopenharmony_ci spin_lock_init(&nullb->lock); 210162306a36Sopenharmony_ci 210262306a36Sopenharmony_ci rv = setup_queues(nullb); 210362306a36Sopenharmony_ci if (rv) 210462306a36Sopenharmony_ci goto out_free_nullb; 210562306a36Sopenharmony_ci 210662306a36Sopenharmony_ci if (dev->queue_mode == NULL_Q_MQ) { 210762306a36Sopenharmony_ci if (shared_tags) { 210862306a36Sopenharmony_ci nullb->tag_set = &tag_set; 210962306a36Sopenharmony_ci rv = 0; 211062306a36Sopenharmony_ci } else { 211162306a36Sopenharmony_ci nullb->tag_set = &nullb->__tag_set; 211262306a36Sopenharmony_ci rv = null_init_tag_set(nullb, nullb->tag_set); 211362306a36Sopenharmony_ci } 211462306a36Sopenharmony_ci 211562306a36Sopenharmony_ci if (rv) 211662306a36Sopenharmony_ci goto out_cleanup_queues; 211762306a36Sopenharmony_ci 211862306a36Sopenharmony_ci nullb->tag_set->timeout = 5 * HZ; 211962306a36Sopenharmony_ci nullb->disk = blk_mq_alloc_disk(nullb->tag_set, nullb); 212062306a36Sopenharmony_ci if (IS_ERR(nullb->disk)) { 212162306a36Sopenharmony_ci rv = PTR_ERR(nullb->disk); 212262306a36Sopenharmony_ci goto out_cleanup_tags; 212362306a36Sopenharmony_ci } 212462306a36Sopenharmony_ci nullb->q = nullb->disk->queue; 212562306a36Sopenharmony_ci } else if (dev->queue_mode == NULL_Q_BIO) { 212662306a36Sopenharmony_ci rv = -ENOMEM; 212762306a36Sopenharmony_ci nullb->disk = blk_alloc_disk(nullb->dev->home_node); 212862306a36Sopenharmony_ci if (!nullb->disk) 212962306a36Sopenharmony_ci goto out_cleanup_queues; 213062306a36Sopenharmony_ci 213162306a36Sopenharmony_ci nullb->q = nullb->disk->queue; 213262306a36Sopenharmony_ci rv = init_driver_queues(nullb); 213362306a36Sopenharmony_ci if (rv) 213462306a36Sopenharmony_ci goto out_cleanup_disk; 213562306a36Sopenharmony_ci } 213662306a36Sopenharmony_ci 213762306a36Sopenharmony_ci if (dev->mbps) { 213862306a36Sopenharmony_ci set_bit(NULLB_DEV_FL_THROTTLED, &dev->flags); 213962306a36Sopenharmony_ci nullb_setup_bwtimer(nullb); 214062306a36Sopenharmony_ci } 214162306a36Sopenharmony_ci 214262306a36Sopenharmony_ci if (dev->cache_size > 0) { 214362306a36Sopenharmony_ci set_bit(NULLB_DEV_FL_CACHE, &nullb->dev->flags); 214462306a36Sopenharmony_ci blk_queue_write_cache(nullb->q, true, true); 214562306a36Sopenharmony_ci } 214662306a36Sopenharmony_ci 214762306a36Sopenharmony_ci if (dev->zoned) { 214862306a36Sopenharmony_ci rv = null_init_zoned_dev(dev, nullb->q); 214962306a36Sopenharmony_ci if (rv) 215062306a36Sopenharmony_ci goto out_cleanup_disk; 215162306a36Sopenharmony_ci } 215262306a36Sopenharmony_ci 215362306a36Sopenharmony_ci nullb->q->queuedata = nullb; 215462306a36Sopenharmony_ci blk_queue_flag_set(QUEUE_FLAG_NONROT, nullb->q); 215562306a36Sopenharmony_ci 215662306a36Sopenharmony_ci mutex_lock(&lock); 215762306a36Sopenharmony_ci rv = ida_simple_get(&nullb_indexes, 0, 0, GFP_KERNEL); 215862306a36Sopenharmony_ci if (rv < 0) { 215962306a36Sopenharmony_ci mutex_unlock(&lock); 216062306a36Sopenharmony_ci goto out_cleanup_zone; 216162306a36Sopenharmony_ci } 216262306a36Sopenharmony_ci nullb->index = rv; 216362306a36Sopenharmony_ci dev->index = rv; 216462306a36Sopenharmony_ci mutex_unlock(&lock); 216562306a36Sopenharmony_ci 216662306a36Sopenharmony_ci blk_queue_logical_block_size(nullb->q, dev->blocksize); 216762306a36Sopenharmony_ci blk_queue_physical_block_size(nullb->q, dev->blocksize); 216862306a36Sopenharmony_ci if (dev->max_sectors) 216962306a36Sopenharmony_ci blk_queue_max_hw_sectors(nullb->q, dev->max_sectors); 217062306a36Sopenharmony_ci 217162306a36Sopenharmony_ci if (dev->virt_boundary) 217262306a36Sopenharmony_ci blk_queue_virt_boundary(nullb->q, PAGE_SIZE - 1); 217362306a36Sopenharmony_ci 217462306a36Sopenharmony_ci null_config_discard(nullb); 217562306a36Sopenharmony_ci 217662306a36Sopenharmony_ci if (config_item_name(&dev->group.cg_item)) { 217762306a36Sopenharmony_ci /* Use configfs dir name as the device name */ 217862306a36Sopenharmony_ci snprintf(nullb->disk_name, sizeof(nullb->disk_name), 217962306a36Sopenharmony_ci "%s", config_item_name(&dev->group.cg_item)); 218062306a36Sopenharmony_ci } else { 218162306a36Sopenharmony_ci sprintf(nullb->disk_name, "nullb%d", nullb->index); 218262306a36Sopenharmony_ci } 218362306a36Sopenharmony_ci 218462306a36Sopenharmony_ci rv = null_gendisk_register(nullb); 218562306a36Sopenharmony_ci if (rv) 218662306a36Sopenharmony_ci goto out_ida_free; 218762306a36Sopenharmony_ci 218862306a36Sopenharmony_ci mutex_lock(&lock); 218962306a36Sopenharmony_ci list_add_tail(&nullb->list, &nullb_list); 219062306a36Sopenharmony_ci mutex_unlock(&lock); 219162306a36Sopenharmony_ci 219262306a36Sopenharmony_ci pr_info("disk %s created\n", nullb->disk_name); 219362306a36Sopenharmony_ci 219462306a36Sopenharmony_ci return 0; 219562306a36Sopenharmony_ci 219662306a36Sopenharmony_ciout_ida_free: 219762306a36Sopenharmony_ci ida_free(&nullb_indexes, nullb->index); 219862306a36Sopenharmony_ciout_cleanup_zone: 219962306a36Sopenharmony_ci null_free_zoned_dev(dev); 220062306a36Sopenharmony_ciout_cleanup_disk: 220162306a36Sopenharmony_ci put_disk(nullb->disk); 220262306a36Sopenharmony_ciout_cleanup_tags: 220362306a36Sopenharmony_ci if (dev->queue_mode == NULL_Q_MQ && nullb->tag_set == &nullb->__tag_set) 220462306a36Sopenharmony_ci blk_mq_free_tag_set(nullb->tag_set); 220562306a36Sopenharmony_ciout_cleanup_queues: 220662306a36Sopenharmony_ci cleanup_queues(nullb); 220762306a36Sopenharmony_ciout_free_nullb: 220862306a36Sopenharmony_ci kfree(nullb); 220962306a36Sopenharmony_ci dev->nullb = NULL; 221062306a36Sopenharmony_ciout: 221162306a36Sopenharmony_ci return rv; 221262306a36Sopenharmony_ci} 221362306a36Sopenharmony_ci 221462306a36Sopenharmony_cistatic struct nullb *null_find_dev_by_name(const char *name) 221562306a36Sopenharmony_ci{ 221662306a36Sopenharmony_ci struct nullb *nullb = NULL, *nb; 221762306a36Sopenharmony_ci 221862306a36Sopenharmony_ci mutex_lock(&lock); 221962306a36Sopenharmony_ci list_for_each_entry(nb, &nullb_list, list) { 222062306a36Sopenharmony_ci if (strcmp(nb->disk_name, name) == 0) { 222162306a36Sopenharmony_ci nullb = nb; 222262306a36Sopenharmony_ci break; 222362306a36Sopenharmony_ci } 222462306a36Sopenharmony_ci } 222562306a36Sopenharmony_ci mutex_unlock(&lock); 222662306a36Sopenharmony_ci 222762306a36Sopenharmony_ci return nullb; 222862306a36Sopenharmony_ci} 222962306a36Sopenharmony_ci 223062306a36Sopenharmony_cistatic int null_create_dev(void) 223162306a36Sopenharmony_ci{ 223262306a36Sopenharmony_ci struct nullb_device *dev; 223362306a36Sopenharmony_ci int ret; 223462306a36Sopenharmony_ci 223562306a36Sopenharmony_ci dev = null_alloc_dev(); 223662306a36Sopenharmony_ci if (!dev) 223762306a36Sopenharmony_ci return -ENOMEM; 223862306a36Sopenharmony_ci 223962306a36Sopenharmony_ci ret = null_add_dev(dev); 224062306a36Sopenharmony_ci if (ret) { 224162306a36Sopenharmony_ci null_free_dev(dev); 224262306a36Sopenharmony_ci return ret; 224362306a36Sopenharmony_ci } 224462306a36Sopenharmony_ci 224562306a36Sopenharmony_ci return 0; 224662306a36Sopenharmony_ci} 224762306a36Sopenharmony_ci 224862306a36Sopenharmony_cistatic void null_destroy_dev(struct nullb *nullb) 224962306a36Sopenharmony_ci{ 225062306a36Sopenharmony_ci struct nullb_device *dev = nullb->dev; 225162306a36Sopenharmony_ci 225262306a36Sopenharmony_ci null_del_dev(nullb); 225362306a36Sopenharmony_ci null_free_device_storage(dev, false); 225462306a36Sopenharmony_ci null_free_dev(dev); 225562306a36Sopenharmony_ci} 225662306a36Sopenharmony_ci 225762306a36Sopenharmony_cistatic int __init null_init(void) 225862306a36Sopenharmony_ci{ 225962306a36Sopenharmony_ci int ret = 0; 226062306a36Sopenharmony_ci unsigned int i; 226162306a36Sopenharmony_ci struct nullb *nullb; 226262306a36Sopenharmony_ci 226362306a36Sopenharmony_ci if (g_bs > PAGE_SIZE) { 226462306a36Sopenharmony_ci pr_warn("invalid block size\n"); 226562306a36Sopenharmony_ci pr_warn("defaults block size to %lu\n", PAGE_SIZE); 226662306a36Sopenharmony_ci g_bs = PAGE_SIZE; 226762306a36Sopenharmony_ci } 226862306a36Sopenharmony_ci 226962306a36Sopenharmony_ci if (g_home_node != NUMA_NO_NODE && g_home_node >= nr_online_nodes) { 227062306a36Sopenharmony_ci pr_err("invalid home_node value\n"); 227162306a36Sopenharmony_ci g_home_node = NUMA_NO_NODE; 227262306a36Sopenharmony_ci } 227362306a36Sopenharmony_ci 227462306a36Sopenharmony_ci if (!null_setup_fault()) 227562306a36Sopenharmony_ci return -EINVAL; 227662306a36Sopenharmony_ci 227762306a36Sopenharmony_ci if (g_queue_mode == NULL_Q_RQ) { 227862306a36Sopenharmony_ci pr_err("legacy IO path is no longer available\n"); 227962306a36Sopenharmony_ci return -EINVAL; 228062306a36Sopenharmony_ci } 228162306a36Sopenharmony_ci 228262306a36Sopenharmony_ci if (g_queue_mode == NULL_Q_MQ && g_use_per_node_hctx) { 228362306a36Sopenharmony_ci if (g_submit_queues != nr_online_nodes) { 228462306a36Sopenharmony_ci pr_warn("submit_queues param is set to %u.\n", 228562306a36Sopenharmony_ci nr_online_nodes); 228662306a36Sopenharmony_ci g_submit_queues = nr_online_nodes; 228762306a36Sopenharmony_ci } 228862306a36Sopenharmony_ci } else if (g_submit_queues > nr_cpu_ids) { 228962306a36Sopenharmony_ci g_submit_queues = nr_cpu_ids; 229062306a36Sopenharmony_ci } else if (g_submit_queues <= 0) { 229162306a36Sopenharmony_ci g_submit_queues = 1; 229262306a36Sopenharmony_ci } 229362306a36Sopenharmony_ci 229462306a36Sopenharmony_ci if (g_queue_mode == NULL_Q_MQ && shared_tags) { 229562306a36Sopenharmony_ci ret = null_init_tag_set(NULL, &tag_set); 229662306a36Sopenharmony_ci if (ret) 229762306a36Sopenharmony_ci return ret; 229862306a36Sopenharmony_ci } 229962306a36Sopenharmony_ci 230062306a36Sopenharmony_ci config_group_init(&nullb_subsys.su_group); 230162306a36Sopenharmony_ci mutex_init(&nullb_subsys.su_mutex); 230262306a36Sopenharmony_ci 230362306a36Sopenharmony_ci ret = configfs_register_subsystem(&nullb_subsys); 230462306a36Sopenharmony_ci if (ret) 230562306a36Sopenharmony_ci goto err_tagset; 230662306a36Sopenharmony_ci 230762306a36Sopenharmony_ci mutex_init(&lock); 230862306a36Sopenharmony_ci 230962306a36Sopenharmony_ci null_major = register_blkdev(0, "nullb"); 231062306a36Sopenharmony_ci if (null_major < 0) { 231162306a36Sopenharmony_ci ret = null_major; 231262306a36Sopenharmony_ci goto err_conf; 231362306a36Sopenharmony_ci } 231462306a36Sopenharmony_ci 231562306a36Sopenharmony_ci for (i = 0; i < nr_devices; i++) { 231662306a36Sopenharmony_ci ret = null_create_dev(); 231762306a36Sopenharmony_ci if (ret) 231862306a36Sopenharmony_ci goto err_dev; 231962306a36Sopenharmony_ci } 232062306a36Sopenharmony_ci 232162306a36Sopenharmony_ci pr_info("module loaded\n"); 232262306a36Sopenharmony_ci return 0; 232362306a36Sopenharmony_ci 232462306a36Sopenharmony_cierr_dev: 232562306a36Sopenharmony_ci while (!list_empty(&nullb_list)) { 232662306a36Sopenharmony_ci nullb = list_entry(nullb_list.next, struct nullb, list); 232762306a36Sopenharmony_ci null_destroy_dev(nullb); 232862306a36Sopenharmony_ci } 232962306a36Sopenharmony_ci unregister_blkdev(null_major, "nullb"); 233062306a36Sopenharmony_cierr_conf: 233162306a36Sopenharmony_ci configfs_unregister_subsystem(&nullb_subsys); 233262306a36Sopenharmony_cierr_tagset: 233362306a36Sopenharmony_ci if (g_queue_mode == NULL_Q_MQ && shared_tags) 233462306a36Sopenharmony_ci blk_mq_free_tag_set(&tag_set); 233562306a36Sopenharmony_ci return ret; 233662306a36Sopenharmony_ci} 233762306a36Sopenharmony_ci 233862306a36Sopenharmony_cistatic void __exit null_exit(void) 233962306a36Sopenharmony_ci{ 234062306a36Sopenharmony_ci struct nullb *nullb; 234162306a36Sopenharmony_ci 234262306a36Sopenharmony_ci configfs_unregister_subsystem(&nullb_subsys); 234362306a36Sopenharmony_ci 234462306a36Sopenharmony_ci unregister_blkdev(null_major, "nullb"); 234562306a36Sopenharmony_ci 234662306a36Sopenharmony_ci mutex_lock(&lock); 234762306a36Sopenharmony_ci while (!list_empty(&nullb_list)) { 234862306a36Sopenharmony_ci nullb = list_entry(nullb_list.next, struct nullb, list); 234962306a36Sopenharmony_ci null_destroy_dev(nullb); 235062306a36Sopenharmony_ci } 235162306a36Sopenharmony_ci mutex_unlock(&lock); 235262306a36Sopenharmony_ci 235362306a36Sopenharmony_ci if (g_queue_mode == NULL_Q_MQ && shared_tags) 235462306a36Sopenharmony_ci blk_mq_free_tag_set(&tag_set); 235562306a36Sopenharmony_ci} 235662306a36Sopenharmony_ci 235762306a36Sopenharmony_cimodule_init(null_init); 235862306a36Sopenharmony_cimodule_exit(null_exit); 235962306a36Sopenharmony_ci 236062306a36Sopenharmony_ciMODULE_AUTHOR("Jens Axboe <axboe@kernel.dk>"); 236162306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 2362