[RHEL7,COMMIT] target: add an user-mode helper to handle changes of a group state

Submitted by Konstantin Khorenko on Jan. 18, 2018, 2:33 p.m.

Details

Message ID 201801181433.w0IEXTxx027528@finist_ce7.work
State New
Series "target: add an user-mode helper to handle changes of a group state"
Headers show

Commit Message

Konstantin Khorenko Jan. 18, 2018, 2:33 p.m.
The commit is pushed to "branch-rh7-3.10.0-693.11.6.vz7.42.x-ovz" and will appear at https://src.openvz.org/scm/ovz/vzkernel.git
after rh7-3.10.0-693.11.6.vz7.42.1
------>
commit e649726bdbf1dfa6fdcab27d2a857d364fd05a11
Author: Andrei Vagin <avagin@openvz.org>
Date:   Thu Jan 18 17:33:29 2018 +0300

    target: add an user-mode helper to handle changes of a group state
    
    An user-mode helper is a user command, which is executed before changing
    a group state. It allows userspace to run required actions to handle a
    new state.
    
    For example, our storage doesn't allow to open one file from several
    nodes, so when a target is switched from standby to active, we have to
    get a lease to an image file and to attach it to a ploop device.
    
    The usermode helper is executed with this set of arguments:
    
    TG_PT_Group ID prev_state new_state {explicit/implicit}
    
    for example:
    default_tg_pt_gp 0 Standby Active/NonOptimized explicit
    
    Here is an example how to set and cleanup a helper:
    $ cd /sys/kernel/config/target/core/
    $ echo -n /usr/sbin/vstrorage_iscsi_alua > \
            fileio_1/iqn.2014-06.com.vstorage:test-2/alua/default_tg_pt_gp/user_helper
    $ echo -n - > fileio_1/iqn.2014-06.com.vstorage:test-2/alua/default_tg_pt_gp/user_helper
    
    Signed-off-by: Andrei Vagin <avagin@openvz.org>
---
 drivers/target/target_core_alua.c     | 84 +++++++++++++++++++++++++++++++++++
 drivers/target/target_core_alua.h     |  4 ++
 drivers/target/target_core_configfs.c | 17 +++++++
 include/target/target_core_base.h     |  1 +
 4 files changed, 106 insertions(+)

Patch hide | download patch | download mbox

diff --git a/drivers/target/target_core_alua.c b/drivers/target/target_core_alua.c
index a73542598e96..a88a51d9b748 100644
--- a/drivers/target/target_core_alua.c
+++ b/drivers/target/target_core_alua.c
@@ -31,6 +31,7 @@ 
 #include <scsi/scsi.h>
 #include <scsi/scsi_cmnd.h>
 #include <asm/unaligned.h>
+#include <linux/kmod.h>
 
 #include <target/target_core_base.h>
 #include <target/target_core_backend.h>
@@ -1027,6 +1028,44 @@  static void core_alua_do_transition_ua(struct t10_alua_tg_pt_gp *tg_pt_gp)
 	spin_unlock(&tg_pt_gp->tg_pt_gp_lock);
 }
 
+static int core_alua_usermode_helper(struct t10_alua_tg_pt_gp *tg_pt_gp, int new_state, int explicit)
+{
+	char *envp[] = { "HOME=/",
+			"TERM=linux",
+			"PATH=/sbin:/usr/sbin:/bin:/usr/bin",
+			NULL };
+	char *argv[7] = {}, str_id[6];
+	int ret;
+
+	if (!tg_pt_gp->tg_pt_gp_usermode_helper)
+		return 0;
+
+	mutex_lock(&tg_pt_gp->tg_pt_gp_transition_mutex);
+	if (!tg_pt_gp->tg_pt_gp_usermode_helper) {
+		mutex_unlock(&tg_pt_gp->tg_pt_gp_transition_mutex);
+		return 0;
+	}
+	argv[0] = kstrdup(tg_pt_gp->tg_pt_gp_usermode_helper, GFP_KERNEL);
+	mutex_unlock(&tg_pt_gp->tg_pt_gp_transition_mutex);
+
+	if (argv[0] == NULL)
+		return -ENOMEM;
+
+	snprintf(str_id, sizeof(str_id), "%hu", tg_pt_gp->tg_pt_gp_id);
+	argv[1] = config_item_name(&tg_pt_gp->tg_pt_gp_group.cg_item);
+	argv[2] = str_id;
+	argv[3] = core_alua_dump_state(tg_pt_gp->tg_pt_gp_alua_access_state);
+	argv[4] = core_alua_dump_state(new_state);
+	argv[5] = (explicit) ? "explicit" : "implicit";
+	argv[6] = NULL;
+
+	ret = call_usermodehelper(argv[0], argv, envp, UMH_WAIT_PROC | UMH_KILLABLE);
+	pr_debug("helper command: %s exit code %u (0x%x)\n",
+			argv[0], (ret >> 8) & 0xff, ret);
+	kfree(argv[0]);
+	return ret;
+}
+
 static int core_alua_do_transition_tg_pt(
 	struct t10_alua_tg_pt_gp *tg_pt_gp,
 	int new_state,
@@ -1034,6 +1073,9 @@  static int core_alua_do_transition_tg_pt(
 {
 	int prev_state;
 
+	if (core_alua_usermode_helper(tg_pt_gp, new_state, explicit))
+		return -EAGAIN;
+
 	mutex_lock(&tg_pt_gp->tg_pt_gp_transition_mutex);
 	/* Nothing to be done here */
 	if (tg_pt_gp->tg_pt_gp_alua_access_state == new_state) {
@@ -1682,6 +1724,7 @@  struct t10_alua_tg_pt_gp *core_alua_allocate_tg_pt_gp(struct se_device *dev,
 	tg_pt_gp->tg_pt_gp_nonop_delay_msecs = ALUA_DEFAULT_NONOP_DELAY_MSECS;
 	tg_pt_gp->tg_pt_gp_trans_delay_msecs = ALUA_DEFAULT_TRANS_DELAY_MSECS;
 	tg_pt_gp->tg_pt_gp_implicit_trans_secs = ALUA_DEFAULT_IMPLICIT_TRANS_SECS;
+	tg_pt_gp->tg_pt_gp_usermode_helper = NULL;
 
 	/*
 	 * Enable all supported states
@@ -1839,6 +1882,8 @@  void core_alua_free_tg_pt_gp(
 	}
 	spin_unlock(&tg_pt_gp->tg_pt_gp_lock);
 
+	kfree(tg_pt_gp->tg_pt_gp_usermode_helper);
+
 	kmem_cache_free(t10_alua_tg_pt_gp_cache, tg_pt_gp);
 }
 
@@ -2167,6 +2212,45 @@  ssize_t core_alua_store_trans_delay_msecs(
 	return count;
 }
 
+ssize_t core_alua_show_user_helper(
+	struct t10_alua_tg_pt_gp *tg_pt_gp,
+	char *page)
+{
+	ssize_t len;
+
+	mutex_lock(&tg_pt_gp->tg_pt_gp_transition_mutex);
+	if (!tg_pt_gp->tg_pt_gp_usermode_helper)
+		len = 0;
+	else
+		len = sprintf(page, "%s\n", tg_pt_gp->tg_pt_gp_usermode_helper);
+	mutex_unlock(&tg_pt_gp->tg_pt_gp_transition_mutex);
+
+	return len;
+}
+
+ssize_t core_alua_store_user_helper(
+	struct t10_alua_tg_pt_gp *tg_pt_gp,
+	const char *page,
+	size_t count)
+{
+	char *h;
+
+	if (count == 1 && page[0] == '-') {
+		h = NULL;
+	} else {
+		h = kstrndup(page, count, GFP_KERNEL);
+		if (h == NULL)
+			return -ENOMEM;
+	}
+
+	mutex_lock(&tg_pt_gp->tg_pt_gp_transition_mutex);
+	kfree(tg_pt_gp->tg_pt_gp_usermode_helper);
+	tg_pt_gp->tg_pt_gp_usermode_helper = h;
+	mutex_unlock(&tg_pt_gp->tg_pt_gp_transition_mutex);
+
+	return count;
+}
+
 ssize_t core_alua_show_implicit_trans_secs(
 	struct t10_alua_tg_pt_gp *tg_pt_gp,
 	char *page)
diff --git a/drivers/target/target_core_alua.h b/drivers/target/target_core_alua.h
index 0a7d65e80404..7673ffe064a6 100644
--- a/drivers/target/target_core_alua.h
+++ b/drivers/target/target_core_alua.h
@@ -137,6 +137,10 @@  extern ssize_t core_alua_show_trans_delay_msecs(struct t10_alua_tg_pt_gp *,
 					char *);
 extern ssize_t core_alua_store_trans_delay_msecs(struct t10_alua_tg_pt_gp *,
 					const char *, size_t);
+extern ssize_t core_alua_show_user_helper(struct t10_alua_tg_pt_gp *,
+					char *);
+extern ssize_t core_alua_store_user_helper(struct t10_alua_tg_pt_gp *,
+					const char *, size_t);
 extern ssize_t core_alua_show_implicit_trans_secs(struct t10_alua_tg_pt_gp *,
 					char *);
 extern ssize_t core_alua_store_implicit_trans_secs(struct t10_alua_tg_pt_gp *,
diff --git a/drivers/target/target_core_configfs.c b/drivers/target/target_core_configfs.c
index 5229bcf27714..7596801b7135 100644
--- a/drivers/target/target_core_configfs.c
+++ b/drivers/target/target_core_configfs.c
@@ -2323,6 +2323,22 @@  static ssize_t target_core_alua_tg_pt_gp_store_attr_trans_delay_msecs(
 
 SE_DEV_ALUA_TG_PT_ATTR(trans_delay_msecs, S_IRUGO | S_IWUSR);
 
+static ssize_t target_core_alua_tg_pt_gp_show_attr_user_helper(
+	struct t10_alua_tg_pt_gp *tg_pt_gp,
+	char *page)
+{
+	return core_alua_show_user_helper(tg_pt_gp, page);
+}
+
+static ssize_t target_core_alua_tg_pt_gp_store_attr_user_helper(
+	struct t10_alua_tg_pt_gp *tg_pt_gp,
+	const char *page,
+	size_t count)
+{
+	return core_alua_store_user_helper(tg_pt_gp, page, count);
+}
+
+SE_DEV_ALUA_TG_PT_ATTR(user_helper, S_IRUGO | S_IWUSR);
 /*
  * implicit_trans_secs
  */
@@ -2474,6 +2490,7 @@  static struct configfs_attribute *target_core_alua_tg_pt_gp_attrs[] = {
 	&target_core_alua_tg_pt_gp_alua_write_metadata.attr,
 	&target_core_alua_tg_pt_gp_nonop_delay_msecs.attr,
 	&target_core_alua_tg_pt_gp_trans_delay_msecs.attr,
+	&target_core_alua_tg_pt_gp_user_helper.attr,
 	&target_core_alua_tg_pt_gp_implicit_trans_secs.attr,
 	&target_core_alua_tg_pt_gp_preferred.attr,
 	&target_core_alua_tg_pt_gp_tg_pt_gp_id.attr,
diff --git a/include/target/target_core_base.h b/include/target/target_core_base.h
index d3d5ae38c534..be3ead42939e 100644
--- a/include/target/target_core_base.h
+++ b/include/target/target_core_base.h
@@ -328,6 +328,7 @@  struct t10_alua_tg_pt_gp {
 	struct list_head tg_pt_gp_mem_list;
 	struct se_port *tg_pt_gp_alua_port;
 	struct se_node_acl *tg_pt_gp_alua_nacl;
+	char *tg_pt_gp_usermode_helper;
 };
 
 struct t10_alua_tg_pt_gp_member {