// SPDX-License-Identifier: GPL-2.0-only/* * Copyright (C) 2013 Shaohua Li <shli@kernel.org> * Copyright (C) 2014 Red Hat, Inc. * Copyright (C) 2015 Arrikto, Inc. * Copyright (C) 2017 Chinamobile, Inc. */ #include <linux/spinlock.h>#include <linux/module.h>#include <linux/kernel.h>#include <linux/timer.h>#include <linux/parser.h>#include <linux/vmalloc.h>#include <linux/uio_driver.h>#include <linux/xarray.h>#include <linux/stringify.h>#include <linux/bitops.h>#include <linux/highmem.h>#include <linux/configfs.h>#include <linux/mutex.h>#include <linux/workqueue.h>#include <linux/pagemap.h>#include <net/genetlink.h>#include <scsi/scsi_common.h>#include <scsi/scsi_proto.h>#include <target/target_core_base.h>#include <target/target_core_fabric.h>#include <target/target_core_backend.h> #include <linux/target_core_user.h> /** * DOC: Userspace I/O * Userspace I/O * ------------- * * Define a shared-memory interface for LIO to pass SCSI commands and * data to userspace for processing. This is to allow backends that * are too complex for in-kernel support to be possible. * * It uses the UIO framework to do a lot of the device-creation and * introspection work for us. * * See the .h file for how the ring is laid out. Note that while the * command ring is defined, the particulars of the data area are * not. Offset values in the command entry point to other locations * internal to the mmap-ed area. There is separate space outside the * command ring for data buffers. This leaves maximum flexibility for * moving buffer allocations, or even page flipping or other * allocation techniques, without altering the command ring layout. * * SECURITY: * The user process must be assumed to be malicious. There's no way to * prevent it breaking the command ring protocol if it wants, but in * order to prevent other issues we must only ever read *data* from * the shared memory area, not offsets or sizes. This applies to * command ring entries as well as the mailbox. Extra code needed for * this may have a 'UAM' comment. */ #define TCMU_TIME_OUT (30 * MSEC_PER_SEC) /* For mailbox plus cmd ring, the size is fixed 8MB */#define MB_CMDR_SIZE_DEF (8 * 1024 * 1024)/* Offset of cmd ring is size of mailbox */#define CMDR_OFF ((__u32)sizeof(struct tcmu_mailbox))#define CMDR_SIZE_DEF (MB_CMDR_SIZE_DEF - CMDR_OFF) /* * For data area, the default block size is PAGE_SIZE and * the default total size is 256K * PAGE_SIZE. */#define DATA_PAGES_PER_BLK_DEF 1#define DATA_AREA_PAGES_DEF (256 * 1024) #define TCMU_MBS_TO_PAGES(_mbs) ((size_t)_mbs << (20 - PAGE_SHIFT))#define TCMU_PAGES_TO_MBS(_pages) (_pages >> (20 - PAGE_SHIFT)) /* * Default number of global data blocks(512K * PAGE_SIZE) * when the unmap thread will be started. */#define TCMU_GLOBAL_MAX_PAGES_DEF (512 * 1024) static u8 tcmu_kern_cmd_reply_supported;static u8 tcmu_netlink_blocked; static struct device *tcmu_root_device; struct tcmu_hba { u32 host_id;}; #define TCMU_CONFIG_LEN 256 static DEFINE_MUTEX(tcmu_nl_cmd_mutex);static LIST_HEAD(tcmu_nl_cmd_list); struct tcmu_dev; struct tcmu_nl_cmd { /* wake up thread waiting for reply */ struct completion complete; struct list_head nl_list; struct tcmu_dev *udev; int cmd; int status;}; struct tcmu_dev { struct list_head node; struct kref kref; struct se_device se_dev; struct se_dev_plug se_plug; char *name; struct se_hba *hba; #define TCMU_DEV_BIT_OPEN 0#define TCMU_DEV_BIT_BROKEN 1#define TCMU_DEV_BIT_BLOCKED 2#define TCMU_DEV_BIT_TMR_NOTIFY 3#define TCMU_DEV_BIT_PLUGGED 4 unsigned long flags; struct uio_info uio_info; struct inode *inode; uint64_t dev_size; struct tcmu_mailbox *mb_addr; void *cmdr; u32 cmdr_size; u32 cmdr_last_cleaned; /* Offset of data area from start of mb */ /* Must add data_off and mb_addr to get the address */ size_t data_off; int data_area_mb; uint32_t max_blocks; size_t mmap_pages; struct mutex cmdr_lock; struct list_head qfull_queue; struct list_head tmr_queue; uint32_t dbi_max; uint32_t dbi_thresh; unsigned long *data_bitmap; struct xarray data_pages; uint32_t data_pages_per_blk; uint32_t data_blk_size; struct xarray commands; struct timer_list cmd_timer; unsigned int cmd_time_out; struct list_head inflight_queue; struct timer_list qfull_timer; int qfull_time_out; struct list_head timedout_entry; struct tcmu_nl_cmd curr_nl_cmd; char dev_config[TCMU_CONFIG_LEN]; int nl_reply_supported;}; #define TCMU_DEV(_se_dev) container_of(_se_dev, struct tcmu_dev, se_dev) struct tcmu_cmd { struct se_cmd *se_cmd; struct tcmu_dev *tcmu_dev; struct list_head queue_entry; uint16_t cmd_id; /* Can't use se_cmd when cleaning up expired cmds, because if cmd has been completed then accessing se_cmd is off limits */ uint32_t dbi_cnt; uint32_t dbi_bidi_cnt; uint32_t dbi_cur; uint32_t *dbi; uint32_t data_len_bidi; unsigned long deadline; #define TCMU_CMD_BIT_EXPIRED 0#define TCMU_CMD_BIT_KEEP_BUF 1 unsigned long flags;}; struct tcmu_tmr { struct list_head queue_entry; uint8_t tmr_type; uint32_t tmr_cmd_cnt; int16_t tmr_cmd_ids[] __counted_by(tmr_cmd_cnt);}; /* * To avoid dead lock the mutex lock order should always be: * * mutex_lock(&root_udev_mutex); * ... * mutex_lock(&tcmu_dev->cmdr_lock); * mutex_unlock(&tcmu_dev->cmdr_lock); * ... * mutex_unlock(&root_udev_mutex); */static DEFINE_MUTEX(root_udev_mutex);static LIST_HEAD(root_udev); static DEFINE_SPINLOCK(timed_out_udevs_lock);static LIST_HEAD(timed_out_udevs); static struct kmem_cache *tcmu_cmd_cache; static atomic_t global_page_count = ATOMIC_INIT(0);static struct delayed_work tcmu_unmap_work;static int tcmu_global_max_pages = TCMU_GLOBAL_MAX_PAGES_DEF; static int tcmu_set_global_max_data_area(const char *str, const struct kernel_param *kp){ int ret, max_area_mb; ret = kstrtoint(str, 10, &max_area_mb); if (ret) return -EINVAL; if (max_area_mb <= 0) { pr_err("global_max_data_area must be larger than 0.\n"); return -EINVAL; } tcmu_global_max_pages = TCMU_MBS_TO_PAGES(max_area_mb); if (atomic_read(&global_page_count) > tcmu_global_max_pages) schedule_delayed_work(&tcmu_unmap_work, 0); else cancel_delayed_work_sync(&tcmu_unmap_work); return 0;} static int tcmu_get_global_max_data_area(char *buffer, const struct kernel_param *kp){ return sprintf(buffer, "%d\n", TCMU_PAGES_TO_MBS(tcmu_global_max_pages));} static const struct kernel_param_ops tcmu_global_max_data_area_op = { .set = tcmu_set_global_max_data_area, .get = tcmu_get_global_max_data_area,}; module_param_cb(global_max_data_area_mb, &tcmu_global_max_data_area_op, NULL, S_IWUSR | S_IRUGO);MODULE_PARM_DESC(global_max_data_area_mb, "Max MBs allowed to be allocated to all the tcmu device's " "data areas."); static int tcmu_get_block_netlink(char *buffer, const struct kernel_param *kp){ return sprintf(buffer, "%s\n", tcmu_netlink_blocked ? "blocked" : "unblocked");} static int tcmu_set_block_netlink(const char *str, const struct kernel_param *kp){ int ret; u8 val; ret = kstrtou8(str, 0, &val); if (ret < 0) return ret; if (val > 1) { pr_err("Invalid block netlink value %u\n", val); return -EINVAL; } tcmu_netlink_blocked = val; return 0;} static const struct kernel_param_ops tcmu_block_netlink_op = { .set = tcmu_set_block_netlink, .get = tcmu_get_block_netlink,}; module_param_cb(block_netlink, &tcmu_block_netlink_op, NULL, S_IWUSR | S_IRUGO);MODULE_PARM_DESC(block_netlink, "Block new netlink commands."); static int tcmu_fail_netlink_cmd(struct tcmu_nl_cmd *nl_cmd){ struct tcmu_dev *udev = nl_cmd->udev; if (!tcmu_netlink_blocked) { pr_err("Could not reset device's netlink interface. Netlink is not blocked.\n"); return -EBUSY; } if (nl_cmd->cmd != TCMU_CMD_UNSPEC) { pr_debug("Aborting nl cmd %d on %s\n", nl_cmd->cmd, udev->name); nl_cmd->status = -EINTR; list_del(&nl_cmd->nl_list); complete(&nl_cmd->complete); } return 0;} static int tcmu_set_reset_netlink(const char *str, const struct kernel_param *kp){ struct tcmu_nl_cmd *nl_cmd, *tmp_cmd; int ret; u8 val; ret = kstrtou8(str, 0, &val); if (ret < 0) return ret; if (val != 1) { pr_err("Invalid reset netlink value %u\n", val); return -EINVAL; } mutex_lock(&tcmu_nl_cmd_mutex); list_for_each_entry_safe(nl_cmd, tmp_cmd, &tcmu_nl_cmd_list, nl_list) { ret = tcmu_fail_netlink_cmd(nl_cmd); if (ret) break; } mutex_unlock(&tcmu_nl_cmd_mutex); return ret;} static const struct kernel_param_ops tcmu_reset_netlink_op = { .set = tcmu_set_reset_netlink,}; module_param_cb(reset_netlink, &tcmu_reset_netlink_op, NULL, S_IWUSR);MODULE_PARM_DESC(reset_netlink, "Reset netlink commands."); /* multicast group */enum tcmu_multicast_groups { TCMU_MCGRP_CONFIG,}; static const struct genl_multicast_group tcmu_mcgrps[] = { [TCMU_MCGRP_CONFIG] = { .name = "config", },}; static const struct nla_policy tcmu_attr_policy[TCMU_ATTR_MAX + 1] = { [TCMU_ATTR_DEVICE] = { .type = NLA_STRING }, [TCMU_ATTR_MINOR] = { .type = NLA_U32 }, [TCMU_ATTR_CMD_STATUS] = { .type = NLA_S32 }, [TCMU_ATTR_DEVICE_ID] = { .type = NLA_U32 }, [TCMU_ATTR_SUPP_KERN_CMD_REPLY] = { .type = NLA_U8 },}; static int tcmu_genl_cmd_done(struct genl_info *info, int completed_cmd){ struct tcmu_dev *udev = NULL; struct tcmu_nl_cmd *nl_cmd; int dev_id, rc, ret = 0; if (!info->attrs[TCMU_ATTR_CMD_STATUS] || !info->attrs[TCMU_ATTR_DEVICE_ID]) { printk(KERN_ERR "TCMU_ATTR_CMD_STATUS or TCMU_ATTR_DEVICE_ID not set, doing nothing\n"); return -EINVAL; } dev_id = nla_get_u32(info->attrs[TCMU_ATTR_DEVICE_ID]); rc = nla_get_s32(info->attrs[TCMU_ATTR_CMD_STATUS]); mutex_lock(&tcmu_nl_cmd_mutex); list_for_each_entry(nl_cmd, &tcmu_nl_cmd_list, nl_list) { if (nl_cmd->udev->se_dev.dev_index == dev_id) { udev = nl_cmd->udev; break; } } if (!udev) { pr_err("tcmu nl cmd %u/%d completion could not find device with dev id %u.\n", completed_cmd, rc, dev_id); ret = -ENODEV; goto unlock; } list_del(&nl_cmd->nl_list); pr_debug("%s genl cmd done got id %d curr %d done %d rc %d stat %d\n", udev->name, dev_id, nl_cmd->cmd, completed_cmd, rc, nl_cmd->status); if (nl_cmd->cmd != completed_cmd) { pr_err("Mismatched commands on %s (Expecting reply for %d. Current %d).\n", udev->name, completed_cmd, nl_cmd->cmd); ret = -EINVAL; goto unlock; } nl_cmd->status = rc; complete(&nl_cmd->complete);unlock: mutex_unlock(&tcmu_nl_cmd_mutex); return ret;} static int tcmu_genl_rm_dev_done(struct sk_buff *skb, struct genl_info *info){ return tcmu_genl_cmd_done(info, TCMU_CMD_REMOVED_DEVICE);} static int tcmu_genl_add_dev_done(struct sk_buff *skb, struct genl_info *info){ return tcmu_genl_cmd_done(info, TCMU_CMD_ADDED_DEVICE);} static int tcmu_genl_reconfig_dev_done(struct sk_buff *skb, struct genl_info *info){ return tcmu_genl_cmd_done(info, TCMU_CMD_RECONFIG_DEVICE);} static int tcmu_genl_set_features(struct sk_buff *skb, struct genl_info *info){ if (info->attrs[TCMU_ATTR_SUPP_KERN_CMD_REPLY]) { tcmu_kern_cmd_reply_supported = nla_get_u8(info->attrs[TCMU_ATTR_SUPP_KERN_CMD_REPLY]); printk(KERN_INFO "tcmu daemon: command reply support %u.\n", tcmu_kern_cmd_reply_supported); } return 0;} static const struct genl_small_ops tcmu_genl_ops[] = { { .cmd = TCMU_CMD_SET_FEATURES, .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, .flags = GENL_ADMIN_PERM, .doit = tcmu_genl_set_features, }, { .cmd = TCMU_CMD_ADDED_DEVICE_DONE, .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, .flags = GENL_ADMIN_PERM, .doit = tcmu_genl_add_dev_done, }, { .cmd = TCMU_CMD_REMOVED_DEVICE_DONE, .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, .flags = GENL_ADMIN_PERM, .doit = tcmu_genl_rm_dev_done, }, { .cmd = TCMU_CMD_RECONFIG_DEVICE_DONE, .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, .flags = GENL_ADMIN_PERM, .doit = tcmu_genl_reconfig_dev_done, },}; /* Our generic netlink family */static struct genl_family tcmu_genl_family __ro_after_init = { .module = THIS_MODULE, .hdrsize = 0, .name = "TCM-USER", .version = 2, .maxattr = TCMU_ATTR_MAX, .policy = tcmu_attr_policy, .mcgrps = tcmu_mcgrps, .n_mcgrps = ARRAY_SIZE(tcmu_mcgrps), .netnsok = true, .small_ops = tcmu_genl_ops, .n_small_ops = ARRAY_SIZE(tcmu_genl_ops), .resv_start_op = TCMU_CMD_SET_FEATURES + 1,}; #define tcmu_cmd_set_dbi_cur(cmd, index) ((cmd)->dbi_cur = (index))#define tcmu_cmd_reset_dbi_cur(cmd) tcmu_cmd_set_dbi_cur(cmd, 0)#define tcmu_cmd_set_dbi(cmd, index) ((cmd)->dbi[(cmd)->dbi_cur++] = (index))#define tcmu_cmd_get_dbi(cmd) ((cmd)->dbi[(cmd)->dbi_cur++]) static void tcmu_cmd_free_data(struct tcmu_cmd *tcmu_cmd, uint32_t len){ struct tcmu_dev *udev = tcmu_cmd->tcmu_dev; uint32_t i;Lines 1–500 of 3403. Load more
// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (C) 2013 Shaohua Li <shli@kernel.org> * Copyright (C) 2014 Red Hat, Inc. * Copyright (C) 2015 Arrikto, Inc. * Copyright (C) 2017 Chinamobile, Inc. */ #include <linux/spinlock.h> #include <linux/module.h> #include <linux/kernel.h> #include <linux/timer.h> #include <linux/parser.h> #include <linux/vmalloc.h> #include <linux/uio_driver.h> #include <linux/xarray.h> #include <linux/stringify.h> #include <linux/bitops.h> #include <linux/highmem.h> #include <linux/configfs.h> #include <linux/mutex.h> #include <linux/workqueue.h> #include <linux/pagemap.h> #include <net/genetlink.h> #include <scsi/scsi_common.h> #include <scsi/scsi_proto.h> #include <target/target_core_base.h> #include <target/target_core_fabric.h> #include <target/target_core_backend.h> #include <linux/target_core_user.h> /** * DOC: Userspace I/O * Userspace I/O * ------------- * * Define a shared-memory interface for LIO to pass SCSI commands and * data to userspace for processing. This is to allow backends that * are too complex for in-kernel support to be possible. * * It uses the UIO framework to do a lot of the device-creation and * introspection work for us. * * See the .h file for how the ring is laid out. Note that while the * command ring is defined, the particulars of the data area are * not. Offset values in the command entry point to other locations * internal to the mmap-ed area. There is separate space outside the * command ring for data buffers. This leaves maximum flexibility for * moving buffer allocations, or even page flipping or other * allocation techniques, without altering the command ring layout. * * SECURITY: * The user process must be assumed to be malicious. There's no way to * prevent it breaking the command ring protocol if it wants, but in * order to prevent other issues we must only ever read *data* from * the shared memory area, not offsets or sizes. This applies to * command ring entries as well as the mailbox. Extra code needed for * this may have a 'UAM' comment. */ #define TCMU_TIME_OUT (30 * MSEC_PER_SEC) /* For mailbox plus cmd ring, the size is fixed 8MB */ #define MB_CMDR_SIZE_DEF (8 * 1024 * 1024) /* Offset of cmd ring is size of mailbox */ #define CMDR_OFF ((__u32)sizeof(struct tcmu_mailbox)) #define CMDR_SIZE_DEF (MB_CMDR_SIZE_DEF - CMDR_OFF) /* * For data area, the default block size is PAGE_SIZE and * the default total size is 256K * PAGE_SIZE. */ #define DATA_PAGES_PER_BLK_DEF 1 #define DATA_AREA_PAGES_DEF (256 * 1024) #define TCMU_MBS_TO_PAGES(_mbs) ((size_t)_mbs << (20 - PAGE_SHIFT)) #define TCMU_PAGES_TO_MBS(_pages) (_pages >> (20 - PAGE_SHIFT)) /* * Default number of global data blocks(512K * PAGE_SIZE) * when the unmap thread will be started. */ #define TCMU_GLOBAL_MAX_PAGES_DEF (512 * 1024) static u8 tcmu_kern_cmd_reply_supported; static u8 tcmu_netlink_blocked; static struct device *tcmu_root_device; struct tcmu_hba { u32 host_id; }; #define TCMU_CONFIG_LEN 256 static DEFINE_MUTEX(tcmu_nl_cmd_mutex); static LIST_HEAD(tcmu_nl_cmd_list); struct tcmu_dev; struct tcmu_nl_cmd { /* wake up thread waiting for reply */ struct completion complete; struct list_head nl_list; struct tcmu_dev *udev; int cmd; int status; }; struct tcmu_dev { struct list_head node; struct kref kref; struct se_device se_dev; struct se_dev_plug se_plug; char *name; struct se_hba *hba; #define TCMU_DEV_BIT_OPEN 0 #define TCMU_DEV_BIT_BROKEN 1 #define TCMU_DEV_BIT_BLOCKED 2 #define TCMU_DEV_BIT_TMR_NOTIFY 3 #define TCMU_DEV_BIT_PLUGGED 4 unsigned long flags; struct uio_info uio_info; struct inode *inode; uint64_t dev_size; struct tcmu_mailbox *mb_addr; void *cmdr; u32 cmdr_size; u32 cmdr_last_cleaned; /* Offset of data area from start of mb */ /* Must add data_off and mb_addr to get the address */ size_t data_off; int data_area_mb; uint32_t max_blocks; size_t mmap_pages; struct mutex cmdr_lock; struct list_head qfull_queue; struct list_head tmr_queue; uint32_t dbi_max; uint32_t dbi_thresh; unsigned long *data_bitmap; struct xarray data_pages; uint32_t data_pages_per_blk; uint32_t data_blk_size; struct xarray commands; struct timer_list cmd_timer; unsigned int cmd_time_out; struct list_head inflight_queue; struct timer_list qfull_timer; int qfull_time_out; struct list_head timedout_entry; struct tcmu_nl_cmd curr_nl_cmd; char dev_config[TCMU_CONFIG_LEN]; int nl_reply_supported; }; #define TCMU_DEV(_se_dev) container_of(_se_dev, struct tcmu_dev, se_dev) struct tcmu_cmd { struct se_cmd *se_cmd; struct tcmu_dev *tcmu_dev; struct list_head queue_entry; uint16_t cmd_id; /* Can't use se_cmd when cleaning up expired cmds, because if cmd has been completed then accessing se_cmd is off limits */ uint32_t dbi_cnt; uint32_t dbi_bidi_cnt; uint32_t dbi_cur; uint32_t *dbi; uint32_t data_len_bidi; unsigned long deadline; #define TCMU_CMD_BIT_EXPIRED 0 #define TCMU_CMD_BIT_KEEP_BUF 1 unsigned long flags; }; struct tcmu_tmr { struct list_head queue_entry; uint8_t tmr_type; uint32_t tmr_cmd_cnt; int16_t tmr_cmd_ids[] __counted_by(tmr_cmd_cnt); }; /* * To avoid dead lock the mutex lock order should always be: * * mutex_lock(&root_udev_mutex); * ... * mutex_lock(&tcmu_dev->cmdr_lock); * mutex_unlock(&tcmu_dev->cmdr_lock); * ... * mutex_unlock(&root_udev_mutex); */ static DEFINE_MUTEX(root_udev_mutex); static LIST_HEAD(root_udev); static DEFINE_SPINLOCK(timed_out_udevs_lock); static LIST_HEAD(timed_out_udevs); static struct kmem_cache *tcmu_cmd_cache; static atomic_t global_page_count = ATOMIC_INIT(0); static struct delayed_work tcmu_unmap_work; static int tcmu_global_max_pages = TCMU_GLOBAL_MAX_PAGES_DEF; static int tcmu_set_global_max_data_area(const char *str, const struct kernel_param *kp) { int ret, max_area_mb; ret = kstrtoint(str, 10, &max_area_mb); if (ret) return -EINVAL; if (max_area_mb <= 0) { pr_err("global_max_data_area must be larger than 0.\n"); return -EINVAL; } tcmu_global_max_pages = TCMU_MBS_TO_PAGES(max_area_mb); if (atomic_read(&global_page_count) > tcmu_global_max_pages) schedule_delayed_work(&tcmu_unmap_work, 0); else cancel_delayed_work_sync(&tcmu_unmap_work); return 0; } static int tcmu_get_global_max_data_area(char *buffer, const struct kernel_param *kp) { return sprintf(buffer, "%d\n", TCMU_PAGES_TO_MBS(tcmu_global_max_pages)); } static const struct kernel_param_ops tcmu_global_max_data_area_op = { .set = tcmu_set_global_max_data_area, .get = tcmu_get_global_max_data_area, }; module_param_cb(global_max_data_area_mb, &tcmu_global_max_data_area_op, NULL, S_IWUSR | S_IRUGO); MODULE_PARM_DESC(global_max_data_area_mb, "Max MBs allowed to be allocated to all the tcmu device's " "data areas."); static int tcmu_get_block_netlink(char *buffer, const struct kernel_param *kp) { return sprintf(buffer, "%s\n", tcmu_netlink_blocked ? "blocked" : "unblocked"); } static int tcmu_set_block_netlink(const char *str, const struct kernel_param *kp) { int ret; u8 val; ret = kstrtou8(str, 0, &val); if (ret < 0) return ret; if (val > 1) { pr_err("Invalid block netlink value %u\n", val); return -EINVAL; } tcmu_netlink_blocked = val; return 0; } static const struct kernel_param_ops tcmu_block_netlink_op = { .set = tcmu_set_block_netlink, .get = tcmu_get_block_netlink, }; module_param_cb(block_netlink, &tcmu_block_netlink_op, NULL, S_IWUSR | S_IRUGO); MODULE_PARM_DESC(block_netlink, "Block new netlink commands."); static int tcmu_fail_netlink_cmd(struct tcmu_nl_cmd *nl_cmd) { struct tcmu_dev *udev = nl_cmd->udev; if (!tcmu_netlink_blocked) { pr_err("Could not reset device's netlink interface. Netlink is not blocked.\n"); return -EBUSY; } if (nl_cmd->cmd != TCMU_CMD_UNSPEC) { pr_debug("Aborting nl cmd %d on %s\n", nl_cmd->cmd, udev->name); nl_cmd->status = -EINTR; list_del(&nl_cmd->nl_list); complete(&nl_cmd->complete); } return 0; } static int tcmu_set_reset_netlink(const char *str, const struct kernel_param *kp) { struct tcmu_nl_cmd *nl_cmd, *tmp_cmd; int ret; u8 val; ret = kstrtou8(str, 0, &val); if (ret < 0) return ret; if (val != 1) { pr_err("Invalid reset netlink value %u\n", val); return -EINVAL; } mutex_lock(&tcmu_nl_cmd_mutex); list_for_each_entry_safe(nl_cmd, tmp_cmd, &tcmu_nl_cmd_list, nl_list) { ret = tcmu_fail_netlink_cmd(nl_cmd); if (ret) break; } mutex_unlock(&tcmu_nl_cmd_mutex); return ret; } static const struct kernel_param_ops tcmu_reset_netlink_op = { .set = tcmu_set_reset_netlink, }; module_param_cb(reset_netlink, &tcmu_reset_netlink_op, NULL, S_IWUSR); MODULE_PARM_DESC(reset_netlink, "Reset netlink commands."); /* multicast group */ enum tcmu_multicast_groups { TCMU_MCGRP_CONFIG, }; static const struct genl_multicast_group tcmu_mcgrps[] = { [TCMU_MCGRP_CONFIG] = { .name = "config", }, }; static const struct nla_policy tcmu_attr_policy[TCMU_ATTR_MAX + 1] = { [TCMU_ATTR_DEVICE] = { .type = NLA_STRING }, [TCMU_ATTR_MINOR] = { .type = NLA_U32 }, [TCMU_ATTR_CMD_STATUS] = { .type = NLA_S32 }, [TCMU_ATTR_DEVICE_ID] = { .type = NLA_U32 }, [TCMU_ATTR_SUPP_KERN_CMD_REPLY] = { .type = NLA_U8 }, }; static int tcmu_genl_cmd_done(struct genl_info *info, int completed_cmd) { struct tcmu_dev *udev = NULL; struct tcmu_nl_cmd *nl_cmd; int dev_id, rc, ret = 0; if (!info->attrs[TCMU_ATTR_CMD_STATUS] || !info->attrs[TCMU_ATTR_DEVICE_ID]) { printk(KERN_ERR "TCMU_ATTR_CMD_STATUS or TCMU_ATTR_DEVICE_ID not set, doing nothing\n"); return -EINVAL; } dev_id = nla_get_u32(info->attrs[TCMU_ATTR_DEVICE_ID]); rc = nla_get_s32(info->attrs[TCMU_ATTR_CMD_STATUS]); mutex_lock(&tcmu_nl_cmd_mutex); list_for_each_entry(nl_cmd, &tcmu_nl_cmd_list, nl_list) { if (nl_cmd->udev->se_dev.dev_index == dev_id) { udev = nl_cmd->udev; break; } } if (!udev) { pr_err("tcmu nl cmd %u/%d completion could not find device with dev id %u.\n", completed_cmd, rc, dev_id); ret = -ENODEV; goto unlock; } list_del(&nl_cmd->nl_list); pr_debug("%s genl cmd done got id %d curr %d done %d rc %d stat %d\n", udev->name, dev_id, nl_cmd->cmd, completed_cmd, rc, nl_cmd->status); if (nl_cmd->cmd != completed_cmd) { pr_err("Mismatched commands on %s (Expecting reply for %d. Current %d).\n", udev->name, completed_cmd, nl_cmd->cmd); ret = -EINVAL; goto unlock; } nl_cmd->status = rc; complete(&nl_cmd->complete); unlock: mutex_unlock(&tcmu_nl_cmd_mutex); return ret; } static int tcmu_genl_rm_dev_done(struct sk_buff *skb, struct genl_info *info) { return tcmu_genl_cmd_done(info, TCMU_CMD_REMOVED_DEVICE); } static int tcmu_genl_add_dev_done(struct sk_buff *skb, struct genl_info *info) { return tcmu_genl_cmd_done(info, TCMU_CMD_ADDED_DEVICE); } static int tcmu_genl_reconfig_dev_done(struct sk_buff *skb, struct genl_info *info) { return tcmu_genl_cmd_done(info, TCMU_CMD_RECONFIG_DEVICE); } static int tcmu_genl_set_features(struct sk_buff *skb, struct genl_info *info) { if (info->attrs[TCMU_ATTR_SUPP_KERN_CMD_REPLY]) { tcmu_kern_cmd_reply_supported = nla_get_u8(info->attrs[TCMU_ATTR_SUPP_KERN_CMD_REPLY]); printk(KERN_INFO "tcmu daemon: command reply support %u.\n", tcmu_kern_cmd_reply_supported); } return 0; } static const struct genl_small_ops tcmu_genl_ops[] = { { .cmd = TCMU_CMD_SET_FEATURES, .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, .flags = GENL_ADMIN_PERM, .doit = tcmu_genl_set_features, }, { .cmd = TCMU_CMD_ADDED_DEVICE_DONE, .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, .flags = GENL_ADMIN_PERM, .doit = tcmu_genl_add_dev_done, }, { .cmd = TCMU_CMD_REMOVED_DEVICE_DONE, .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, .flags = GENL_ADMIN_PERM, .doit = tcmu_genl_rm_dev_done, }, { .cmd = TCMU_CMD_RECONFIG_DEVICE_DONE, .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, .flags = GENL_ADMIN_PERM, .doit = tcmu_genl_reconfig_dev_done, }, }; /* Our generic netlink family */ static struct genl_family tcmu_genl_family __ro_after_init = { .module = THIS_MODULE, .hdrsize = 0, .name = "TCM-USER", .version = 2, .maxattr = TCMU_ATTR_MAX, .policy = tcmu_attr_policy, .mcgrps = tcmu_mcgrps, .n_mcgrps = ARRAY_SIZE(tcmu_mcgrps), .netnsok = true, .small_ops = tcmu_genl_ops, .n_small_ops = ARRAY_SIZE(tcmu_genl_ops), .resv_start_op = TCMU_CMD_SET_FEATURES + 1, }; #define tcmu_cmd_set_dbi_cur(cmd, index) ((cmd)->dbi_cur = (index)) #define tcmu_cmd_reset_dbi_cur(cmd) tcmu_cmd_set_dbi_cur(cmd, 0) #define tcmu_cmd_set_dbi(cmd, index) ((cmd)->dbi[(cmd)->dbi_cur++] = (index)) #define tcmu_cmd_get_dbi(cmd) ((cmd)->dbi[(cmd)->dbi_cur++]) static void tcmu_cmd_free_data(struct tcmu_cmd *tcmu_cmd, uint32_t len) { struct tcmu_dev *udev = tcmu_cmd->tcmu_dev; uint32_t i;