iSCSI target sysfs "enabled" attribute implemented
authorvlnb <vlnb@d57e44dd-8a1f-0410-8b47-8ef2f437770f>
Fri, 6 Nov 2009 18:41:37 +0000 (18:41 +0000)
committervlnb <vlnb@d57e44dd-8a1f-0410-8b47-8ef2f437770f>
Fri, 6 Nov 2009 18:41:37 +0000 (18:41 +0000)
git-svn-id: https://scst.svn.sourceforge.net/svnroot/scst/trunk@1324 d57e44dd-8a1f-0410-8b47-8ef2f437770f

12 files changed:
iscsi-scst/include/iscsi_scst.h
iscsi-scst/kernel/config.c
iscsi-scst/kernel/conn.c
iscsi-scst/kernel/event.c
iscsi-scst/kernel/iscsi.c
iscsi-scst/kernel/iscsi.h
iscsi-scst/kernel/nthread.c
iscsi-scst/kernel/target.c
iscsi-scst/usr/event.c
iscsi-scst/usr/iscsi_scstd.c
iscsi-scst/usr/iscsid.c
iscsi-scst/usr/iscsid.h

index b351fcb..6aa9fdd 100644 (file)
@@ -106,7 +106,9 @@ struct iscsi_kern_param_info {
        s32 target_param[target_key_last];
 };
 
-enum iscsi_kern_event_state {
+enum iscsi_kern_event_code {
+       E_ENABLE_TARGET,
+       E_DISABLE_TARGET,
        E_CONN_CLOSE,
 };
 
@@ -114,7 +116,7 @@ struct iscsi_kern_event {
        u32 tid;
        aligned_u64 sid;
        u32 cid;
-       u32 state;
+       u32 code;
 };
 
 struct iscsi_kern_register_info {
@@ -132,12 +134,14 @@ struct iscsi_kern_register_info {
 
 #define ADD_TARGET             _IOW('s', 1, struct iscsi_kern_target_info)
 #define DEL_TARGET             _IOW('s', 2, struct iscsi_kern_target_info)
-#define ADD_SESSION            _IOW('s', 3, struct iscsi_kern_session_info)
-#define DEL_SESSION            _IOW('s', 4, struct iscsi_kern_session_info)
-#define ADD_CONN               _IOW('s', 5, struct iscsi_kern_conn_info)
-#define DEL_CONN               _IOW('s', 6, struct iscsi_kern_conn_info)
-#define ISCSI_PARAM_SET                _IOW('s', 7, struct iscsi_kern_param_info)
-#define ISCSI_PARAM_GET                _IOWR('s', 8, struct iscsi_kern_param_info)
+#define ENABLE_TARGET          _IOW('s', 3, struct iscsi_kern_target_info)
+#define DISABLE_TARGET         _IOW('s', 4, struct iscsi_kern_target_info)
+#define ADD_SESSION            _IOW('s', 5, struct iscsi_kern_session_info)
+#define DEL_SESSION            _IOW('s', 6, struct iscsi_kern_session_info)
+#define ADD_CONN               _IOW('s', 7, struct iscsi_kern_conn_info)
+#define DEL_CONN               _IOW('s', 8, struct iscsi_kern_conn_info)
+#define ISCSI_PARAM_SET                _IOW('s', 9, struct iscsi_kern_param_info)
+#define ISCSI_PARAM_GET                _IOWR('s', 10, struct iscsi_kern_param_info)
 
 static inline int iscsi_is_key_internal(int key)
 {
index ddfc8d2..9d948f2 100644 (file)
@@ -360,6 +360,36 @@ out:
        return err;
 }
 
+/* target_mgmt_mutex supposed to be locked */
+static int enable_target(void __user *ptr)
+{
+       int err;
+       struct iscsi_kern_target_info info;
+
+       err = copy_from_user(&info, ptr, sizeof(info));
+       if (err < 0)
+               return err;
+
+       err = target_enable(&info);
+
+       return err;
+}
+
+/* target_mgmt_mutex supposed to be locked */
+static int disable_target(void __user *ptr)
+{
+       int err;
+       struct iscsi_kern_target_info info;
+
+       err = copy_from_user(&info, ptr, sizeof(info));
+       if (err < 0)
+               return err;
+
+       err = target_disable(&info);
+
+       return err;
+}
+
 /* target_mgmt_mutex supposed to be locked */
 static int add_target(void __user *ptr)
 {
@@ -419,6 +449,8 @@ static long ioctl(struct file *file, unsigned int cmd, unsigned long arg)
        switch (cmd) {
        case ADD_TARGET:
        case DEL_TARGET:
+       case ENABLE_TARGET:
+       case DISABLE_TARGET:
        case ADD_SESSION:
        case DEL_SESSION:
        case ISCSI_PARAM_SET:
@@ -463,6 +495,12 @@ static long ioctl(struct file *file, unsigned int cmd, unsigned long arg)
        case ADD_TARGET:
                err = add_target((void __user *) arg);
                goto out_unlock;
+       case ENABLE_TARGET:
+               err = enable_target((void __user *) arg);
+               goto out_unlock;
+       case DISABLE_TARGET:
+               err = disable_target((void __user *) arg);
+               goto out_unlock;
        case ADD_SESSION:
                err = add_session(target, (void __user *) arg);
                goto out_unlock;
index ee8e419..76c2f05 100644 (file)
@@ -616,7 +616,7 @@ static int iscsi_conn_alloc(struct iscsi_session *session,
 
 restart:
        list_for_each_entry(c, &session->conn_list, conn_list_entry) {
-               if (strcmp(addr, conn->iscsi_conn_kobj.name) == 0) {
+               if (strcmp(addr, kobject_name(&conn->iscsi_conn_kobj)) == 0) {
                        char c_addr[64];
 
                        iscsi_get_initiator_ip(conn, c_addr, sizeof(c_addr));
index 75d98e9..4453fde 100644 (file)
@@ -86,13 +86,13 @@ static void event_recv(struct sock *sk, int length)
 }
 #endif
 
-static int notify(void *data, int len, gfp_t gfp_mask)
+static int notify(void *data, int len)
 {
        struct sk_buff *skb;
        struct nlmsghdr *nlh;
        static u32 seq;
 
-       skb = alloc_skb(NLMSG_SPACE(len), gfp_mask);
+       skb = alloc_skb(NLMSG_SPACE(len), GFP_KERNEL);
        if (!skb)
                return -ENOMEM;
 
@@ -104,7 +104,7 @@ static int notify(void *data, int len, gfp_t gfp_mask)
        return netlink_unicast(nl, skb, iscsid_pid, 0);
 }
 
-int event_send(u32 tid, u64 sid, u32 cid, u32 state, int atomic)
+int event_send(u32 tid, u64 sid, u32 cid, enum iscsi_kern_event_code code)
 {
        int err;
        struct iscsi_kern_event event;
@@ -112,9 +112,9 @@ int event_send(u32 tid, u64 sid, u32 cid, u32 state, int atomic)
        event.tid = tid;
        event.sid = sid;
        event.cid = cid;
-       event.state = state;
+       event.code = code;
 
-       err = notify(&event, NLMSG_SPACE(sizeof(struct iscsi_kern_event)), 0);
+       err = notify(&event, NLMSG_SPACE(sizeof(struct iscsi_kern_event)));
 
        return err;
 }
index 90075ab..4fea97e 100644 (file)
@@ -3169,6 +3169,8 @@ struct scst_tgt_template iscsi_template = {
        .tgtt_attrs = iscsi_attrs,
        .tgt_attrs = iscsi_tgt_attrs,
        .sess_attrs = iscsi_sess_attrs,
+       .enable_tgt = iscsi_enable_target,
+       .is_tgt_enabled = iscsi_is_target_enabled,
 #endif
 #if defined(CONFIG_SCST_DEBUG) || defined(CONFIG_SCST_TRACING)
        .default_trace_flags = ISCSI_DEFAULT_LOG_FLAGS,
index 9d371e2..ae26252 100644 (file)
@@ -82,6 +82,15 @@ struct iscsi_target {
 
        struct list_head target_list_entry;
        u32 tid;
+
+       /* All protected by target_sysfs_mutex */
+       unsigned int tgt_enabled:1;
+       unsigned int expected_ioctl;
+       int ioctl_res;
+       struct completion *target_enabling_cmpl;
+
+       struct mutex target_sysfs_mutex;
+
        char name[ISCSI_NAME_LEN];
 };
 
@@ -463,9 +472,14 @@ extern void iscsi_task_mgmt_affected_cmds_done(struct scst_mgmt_cmd *scst_mcmd);
 /* target.c */
 #ifndef CONFIG_SCST_PROC
 extern const struct attribute *iscsi_tgt_attrs[];
+extern ssize_t iscsi_enable_target(struct scst_tgt *scst_tgt, const char *buf,
+       size_t size);
+extern bool iscsi_is_target_enabled(struct scst_tgt *scst_tgt);
 #endif
 struct iscsi_target *target_lookup_by_id(u32);
 extern int target_add(struct iscsi_kern_target_info *);
+extern int target_enable(struct iscsi_kern_target_info *);
+extern int target_disable(struct iscsi_kern_target_info *);
 extern int target_del(u32 id);
 extern void target_del_session(struct iscsi_target *target,
        struct iscsi_session *session, int flags);
@@ -500,7 +514,7 @@ extern int iscsi_param_set(struct iscsi_target *,
        struct iscsi_kern_param_info *, int);
 
 /* event.c */
-extern int event_send(u32, u64, u32, u32, int);
+extern int event_send(u32, u64, u32, u32);
 extern int event_init(void);
 extern void event_exit(void);
 
index 8415850..54acd1d 100644 (file)
@@ -549,7 +549,7 @@ static void close_conn(struct iscsi_conn *conn)
 
        TRACE_CONN_CLOSE("Notifying user space about closing connection %p",
                         conn);
-       event_send(target->tid, session->sid, conn->cid, E_CONN_CLOSE, 0);
+       event_send(target->tid, session->sid, conn->cid, E_CONN_CLOSE);
 
 #ifdef CONFIG_SCST_PROC
        mutex_lock(&target->target_mutex);
index 2c3009f..7f56b13 100644 (file)
@@ -19,7 +19,8 @@
 #include "iscsi.h"
 #include "digest.h"
 
-#define        MAX_NR_TARGETS  (1UL << 30)
+#define MAX_NR_TARGETS         (1UL << 30)
+#define SYSFS_WAIT_TIMEOUT     (15 * HZ)
 
 DEFINE_MUTEX(target_mgmt_mutex);
 
@@ -83,6 +84,7 @@ static int iscsi_target_create(struct iscsi_kern_target_info *info, u32 tid)
        strncpy(target->name, name, sizeof(target->name) - 1);
 
        mutex_init(&target->target_mutex);
+       mutex_init(&target->target_sysfs_mutex);
        INIT_LIST_HEAD(&target->session_list);
 
        target->scst_tgt = scst_register(&iscsi_template, target->name);
@@ -152,6 +154,96 @@ static void target_destroy(struct iscsi_target *target)
        module_put(THIS_MODULE);
 }
 
+/* target_mgmt_mutex supposed to be locked */
+int target_enable(struct iscsi_kern_target_info *info)
+{
+       int res = 0;
+       struct iscsi_target *tgt;
+
+       TRACE_ENTRY();
+
+       tgt = target_lookup_by_id(info->tid);
+       if (tgt == NULL) {
+               PRINT_ERROR("Target %d not found", info->tid);
+               res = -EINVAL;
+               goto out;
+       }
+
+       mutex_lock(&tgt->target_sysfs_mutex);
+
+       if (tgt->expected_ioctl != ENABLE_TARGET) {
+               PRINT_ERROR("Unexpected ENABLE_TARGET IOCTL for target %d",
+                       tgt->tid);
+               res = -EINVAL;
+               goto out_unlock;
+       }
+
+       tgt->expected_ioctl = 0;
+
+       WARN_ON(tgt->tgt_enabled);
+       tgt->tgt_enabled = 1;
+
+       tgt->ioctl_res = 0;
+
+       complete_all(tgt->target_enabling_cmpl);
+
+out_unlock:
+       mutex_unlock(&tgt->target_sysfs_mutex);
+
+out:
+       TRACE_EXIT_RES(res);
+       return res;
+}
+
+/* target_mgmt_mutex supposed to be locked */
+int target_disable(struct iscsi_kern_target_info *info)
+{
+       int res = 0;
+       struct iscsi_target *tgt;
+
+       TRACE_ENTRY();
+
+       tgt = target_lookup_by_id(info->tid);
+       if (tgt == NULL) {
+               PRINT_ERROR("Target %d not found", info->tid);
+               res = -EINVAL;
+               goto out;
+       }
+
+       mutex_lock(&tgt->target_sysfs_mutex);
+
+       if (tgt->expected_ioctl != DISABLE_TARGET) {
+               PRINT_ERROR("Unexpected DISABLE_TARGET IOCTL for target %d",
+                       tgt->tid);
+               res = -EINVAL;
+               goto out_unlock;
+       }
+
+       mutex_unlock(&tgt->target_sysfs_mutex);
+
+       mutex_lock(&tgt->target_mutex);
+       target_del_all_sess(tgt, ISCSI_CONN_ACTIVE_CLOSE | ISCSI_CONN_DELETING);
+       mutex_unlock(&tgt->target_mutex);
+
+       mutex_lock(&tgt->target_sysfs_mutex);
+
+       tgt->expected_ioctl = 0;
+
+       WARN_ON(!tgt->tgt_enabled);
+       tgt->tgt_enabled = 0;
+
+       tgt->ioctl_res = 0;
+
+       complete_all(tgt->target_enabling_cmpl);
+
+out_unlock:
+       mutex_unlock(&tgt->target_sysfs_mutex);
+
+out:
+       TRACE_EXIT_RES(res);
+       return res;
+}
+
 /* target_mgmt_mutex supposed to be locked */
 int target_del(u32 id)
 {
@@ -370,4 +462,109 @@ const struct attribute *iscsi_tgt_attrs[] = {
        NULL,
 };
 
+ssize_t iscsi_enable_target(struct scst_tgt *scst_tgt, const char *buf,
+       size_t size)
+{
+       struct iscsi_target *tgt =
+               (struct iscsi_target *)scst_tgt_get_tgt_priv(scst_tgt);
+       DECLARE_COMPLETION_ONSTACK(enabling_cmpl);
+       int res = 0, rc, ioctl_res = 0;
+       bool enable;
+
+       TRACE_ENTRY();
+
+       mutex_lock(&tgt->target_sysfs_mutex);
+
+       if (tgt->target_enabling_cmpl != NULL) {
+               TRACE_DBG("A sysfs command is being processed for target %d",
+                       tgt->tid);
+               res = -ETXTBSY;
+               goto out_unlock;
+       }
+
+       tgt->target_enabling_cmpl = &enabling_cmpl;
+
+       switch (buf[0]) {
+       case '0':
+               if (!tgt->tgt_enabled) {
+                       TRACE_DBG("Target %d already disabled", tgt->tid);
+                       goto out_null_unlock;
+               }
+               enable = true;
+               tgt->expected_ioctl = DISABLE_TARGET;
+               res = event_send(tgt->tid, 0, 0, E_DISABLE_TARGET);
+               if (res <= 0) {
+                       PRINT_ERROR("event_send() failed: %d", res);
+                       goto out_null_unlock;
+               }
+               break;
+       case '1':
+               if (tgt->tgt_enabled) {
+                       TRACE_DBG("Target %d already enabled", tgt->tid);
+                       goto out_null_unlock;
+               }
+               enable = false;
+               tgt->expected_ioctl = ENABLE_TARGET;
+               res = event_send(tgt->tid, 0, 0, E_ENABLE_TARGET);
+               if (res <= 0) {
+                       PRINT_ERROR("event_send() failed: %d", res);
+                       goto out_null_unlock;
+               }
+               break;
+       default:
+               PRINT_ERROR("%s: Requested action not understood: %s",
+                      __func__, buf);
+               res = -EINVAL;
+               goto out_null_unlock;
+       }
+
+       mutex_unlock(&tgt->target_sysfs_mutex);
+
+       TRACE_DBG("Waiting for completion of enable/disable (%d) "
+               "target %d", enable, tgt->tid);
+
+       rc = wait_for_completion_interruptible_timeout(&enabling_cmpl,
+                                       SYSFS_WAIT_TIMEOUT);
+       if (res == 0) {
+               PRINT_ERROR("Timeout attempting to %s target %d",
+                       enable ? "enable" : "disable", tgt->tid);
+               res = -EBUSY;
+               /* go through */
+       } else if (res < 0) {
+               if (res != -ERESTARTSYS)
+                       PRINT_ERROR("wait_for_completion() failed: %d", res);
+               /* go through */
+       }
+
+       TRACE_DBG("Waiting for completion of enable/disable (%d) "
+               "target %d finished with res %d", enable, tgt->tid, res);
+
+       mutex_lock(&tgt->target_sysfs_mutex);
+
+       ioctl_res = tgt->ioctl_res;
+
+out_null_unlock:
+       tgt->target_enabling_cmpl = NULL;
+
+out_unlock:
+       mutex_unlock(&tgt->target_sysfs_mutex);
+
+       if (res == 0) {
+               res = ioctl_res;
+               if (res == 0)
+                       res = size;
+       }
+
+       TRACE_EXIT_RES(res);
+       return res;
+}
+
+bool iscsi_is_target_enabled(struct scst_tgt *scst_tgt)
+{
+       struct iscsi_target *tgt =
+               (struct iscsi_target *)scst_tgt_get_tgt_priv(scst_tgt);
+
+       return tgt->tgt_enabled;
+}
+
 #endif /* CONFIG_SCST_PROC */
index a665594..6a23bf3 100644 (file)
@@ -23,6 +23,7 @@
 #include <stdlib.h>
 #include <string.h>
 #include <unistd.h>
+#include <sys/ioctl.h>
 
 #include <asm/types.h>
 #include <sys/socket.h>
@@ -82,10 +83,10 @@ void handle_iscsi_events(int fd)
        struct session *session;
        struct connection *conn;
        struct iscsi_kern_event event;
-       int res;
+       int rc;
 
 retry:
-       if ((res = nl_read(fd, &event, sizeof(event))) < 0) {
+       if ((rc = nl_read(fd, &event, sizeof(event))) < 0) {
                if (errno == EAGAIN)
                        return;
                if (errno == EINTR)
@@ -94,10 +95,60 @@ retry:
                exit(1);
        }
 
-       log_debug(1, "conn %u session %#" PRIx64 " target %u, state %u",
-                 event.cid, event.sid, event.tid, event.state);
+       log_debug(1, "conn %u session %#" PRIx64 " target %u, code %u",
+                 event.cid, event.sid, event.tid, event.code);
+
+       switch (event.code) {
+       case E_ENABLE_TARGET:
+       {
+               struct target *target;
+               struct iscsi_kern_target_info info;
+
+               target = target_find_by_id(event.tid);
+               if (target == NULL) {
+                       log_error("Target %d not found", event.tid);
+                       goto out;
+               }
+
+               target->tgt_enabled = 1;
+
+               memset(&info, 0, sizeof(info));
+
+               info.tid = event.tid;
+               rc = ioctl(ctrl_fd, ENABLE_TARGET, &info);
+               if (rc < 0) {
+                       log_error("Can't enable target %u: %s\n", event.tid,
+                               strerror(errno));
+                       goto out;
+               }
+               break;
+       }
+
+       case E_DISABLE_TARGET:
+       {
+               struct target *target;
+               struct iscsi_kern_target_info info;
+
+               target = target_find_by_id(event.tid);
+               if (target == NULL) {
+                       log_error("Target %d not found", event.tid);
+                       goto out;
+               }
+
+               target->tgt_enabled = 0;
+
+               memset(&info, 0, sizeof(info));
+
+               info.tid = event.tid;
+               rc = ioctl(ctrl_fd, DISABLE_TARGET, &info);
+               if (rc < 0) {
+                       log_error("Can't disable target %u: %s\n", event.tid,
+                               strerror(errno));
+                       goto out;
+               }
+               break;
+       }
 
-       switch (event.state) {
        case E_CONN_CLOSE:
                session = session_find_id(event.tid, event.sid);
                if (session == NULL) {
@@ -117,11 +168,15 @@ retry:
                if (list_empty(&session->conn_list))
                        session_free(session);
                break;
+
        default:
-               log_warning("%s(%d) %u\n", __FUNCTION__, __LINE__, event.state);
+               log_warning("Unknown event %u", event.code);
                exit(-1);
                break;
        }
+
+out:
+       return;
 }
 
 int nl_open(void)
index 3ef8df5..58f15da 100644 (file)
@@ -461,22 +461,6 @@ out:
        return;
 }
 
-void wait_4_iscsi_event(int timeout)
-{
-       int res;
-
-       do {
-               res = poll(&poll_array[POLL_NL], 1, timeout);
-       } while (res < 0 && errno == EINTR);
-
-       if (poll_array[POLL_NL].revents && res > 0)
-               handle_iscsi_events(nl_fd);
-       else {
-               log_error("%s: unexpected error %d %d\n", __FUNCTION__, res,
-                       errno);
-       }
-}
-
 static void event_loop(int timeout)
 {
        int res, i;
index 5e517fd..6532471 100644 (file)
@@ -508,6 +508,14 @@ static void login_start(struct connection *conn)
                        return;
                }
 
+               if (!target->tgt_enabled) {
+                       log_debug(1, "Connect from %s to disabled target %s",
+                               name, target_name);
+                       rsp->status_class = ISCSI_STATUS_TARGET_ERR;
+                       conn->state = STATE_CLOSE;
+                       return;
+               }
+
                conn->tid = target->tid;
                if (config_initiator_access(conn->tid, conn->fd) ||
                    isns_scn_access(conn->tid, conn->fd, name)) {
index fba0535..0105a57 100644 (file)
@@ -153,6 +153,8 @@ struct target {
 
        struct __qelem sessions_list;
 
+       unsigned int tgt_enabled:1;
+
        u32 tid;
        char name[ISCSI_NAME_LEN];
        char *alias;
@@ -178,7 +180,6 @@ extern void conn_free_rsp_buf_list(struct connection *conn);
 /* iscsi_scstd.c */
 extern uint16_t server_port;
 extern void isns_set_fd(int isns, int scn_listen, int scn);
-extern void wait_4_iscsi_event(int timeout);
 
 /* iscsid.c */
 extern int iscsi_debug;