|
@@ -94,6 +94,7 @@
|
|
|
#define TCMU_GLOBAL_MAX_BLOCKS_DEF (512 * 1024)
|
|
|
|
|
|
static u8 tcmu_kern_cmd_reply_supported;
|
|
|
+static u8 tcmu_netlink_blocked;
|
|
|
|
|
|
static struct device *tcmu_root_device;
|
|
|
|
|
@@ -255,6 +256,92 @@ 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,
|
|
@@ -303,8 +390,9 @@ static int tcmu_genl_cmd_done(struct genl_info *info, int completed_cmd)
|
|
|
}
|
|
|
list_del(&nl_cmd->nl_list);
|
|
|
|
|
|
- pr_debug("%s genl cmd done got id %d curr %d done %d rc %d\n",
|
|
|
- udev->name, dev_id, nl_cmd->cmd, completed_cmd, rc);
|
|
|
+ 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",
|
|
@@ -1547,6 +1635,13 @@ static int tcmu_init_genl_cmd_reply(struct tcmu_dev *udev, int cmd)
|
|
|
|
|
|
mutex_lock(&tcmu_nl_cmd_mutex);
|
|
|
|
|
|
+ if (tcmu_netlink_blocked) {
|
|
|
+ mutex_unlock(&tcmu_nl_cmd_mutex);
|
|
|
+ pr_warn("Failing nl cmd %d on %s. Interface is blocked.\n", cmd,
|
|
|
+ udev->name);
|
|
|
+ return -EAGAIN;
|
|
|
+ }
|
|
|
+
|
|
|
if (nl_cmd->cmd != TCMU_CMD_UNSPEC) {
|
|
|
mutex_unlock(&tcmu_nl_cmd_mutex);
|
|
|
pr_warn("netlink cmd %d already executing on %s\n",
|
|
@@ -1583,7 +1678,6 @@ static int tcmu_wait_genl_cmd_reply(struct tcmu_dev *udev)
|
|
|
mutex_lock(&tcmu_nl_cmd_mutex);
|
|
|
nl_cmd->cmd = TCMU_CMD_UNSPEC;
|
|
|
ret = nl_cmd->status;
|
|
|
- nl_cmd->status = 0;
|
|
|
mutex_unlock(&tcmu_nl_cmd_mutex);
|
|
|
|
|
|
return ret;
|