From 6153a079a3446b4737ee7cb6bac41c45fc719732 Mon Sep 17 00:00:00 2001 From: Vladislav Bolkhovitin Date: Fri, 6 Nov 2009 18:41:37 +0000 Subject: [PATCH] iSCSI target sysfs "enabled" attribute implemented git-svn-id: http://svn.code.sf.net/p/scst/svn/trunk@1324 d57e44dd-8a1f-0410-8b47-8ef2f437770f --- iscsi-scst/include/iscsi_scst.h | 20 ++-- iscsi-scst/kernel/config.c | 38 ++++++ iscsi-scst/kernel/conn.c | 2 +- iscsi-scst/kernel/event.c | 10 +- iscsi-scst/kernel/iscsi.c | 2 + iscsi-scst/kernel/iscsi.h | 16 ++- iscsi-scst/kernel/nthread.c | 2 +- iscsi-scst/kernel/target.c | 199 +++++++++++++++++++++++++++++++- iscsi-scst/usr/event.c | 67 ++++++++++- iscsi-scst/usr/iscsi_scstd.c | 16 --- iscsi-scst/usr/iscsid.c | 8 ++ iscsi-scst/usr/iscsid.h | 3 +- 12 files changed, 343 insertions(+), 40 deletions(-) diff --git a/iscsi-scst/include/iscsi_scst.h b/iscsi-scst/include/iscsi_scst.h index b351fcb0a..6aa9fddd6 100644 --- a/iscsi-scst/include/iscsi_scst.h +++ b/iscsi-scst/include/iscsi_scst.h @@ -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) { diff --git a/iscsi-scst/kernel/config.c b/iscsi-scst/kernel/config.c index ddfc8d236..9d948f274 100644 --- a/iscsi-scst/kernel/config.c +++ b/iscsi-scst/kernel/config.c @@ -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; diff --git a/iscsi-scst/kernel/conn.c b/iscsi-scst/kernel/conn.c index ee8e4194d..76c2f0583 100644 --- a/iscsi-scst/kernel/conn.c +++ b/iscsi-scst/kernel/conn.c @@ -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)); diff --git a/iscsi-scst/kernel/event.c b/iscsi-scst/kernel/event.c index 75d98e9ec..4453fde13 100644 --- a/iscsi-scst/kernel/event.c +++ b/iscsi-scst/kernel/event.c @@ -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; } diff --git a/iscsi-scst/kernel/iscsi.c b/iscsi-scst/kernel/iscsi.c index 90075abd5..4fea97e8f 100644 --- a/iscsi-scst/kernel/iscsi.c +++ b/iscsi-scst/kernel/iscsi.c @@ -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, diff --git a/iscsi-scst/kernel/iscsi.h b/iscsi-scst/kernel/iscsi.h index 9d371e2d9..ae262527f 100644 --- a/iscsi-scst/kernel/iscsi.h +++ b/iscsi-scst/kernel/iscsi.h @@ -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); diff --git a/iscsi-scst/kernel/nthread.c b/iscsi-scst/kernel/nthread.c index 841585064..54acd1dd9 100644 --- a/iscsi-scst/kernel/nthread.c +++ b/iscsi-scst/kernel/nthread.c @@ -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); diff --git a/iscsi-scst/kernel/target.c b/iscsi-scst/kernel/target.c index 2c3009f37..7f56b13a6 100644 --- a/iscsi-scst/kernel/target.c +++ b/iscsi-scst/kernel/target.c @@ -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 */ diff --git a/iscsi-scst/usr/event.c b/iscsi-scst/usr/event.c index a66559448..6a23bf323 100644 --- a/iscsi-scst/usr/event.c +++ b/iscsi-scst/usr/event.c @@ -23,6 +23,7 @@ #include #include #include +#include #include #include @@ -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) diff --git a/iscsi-scst/usr/iscsi_scstd.c b/iscsi-scst/usr/iscsi_scstd.c index 3ef8df568..58f15da27 100644 --- a/iscsi-scst/usr/iscsi_scstd.c +++ b/iscsi-scst/usr/iscsi_scstd.c @@ -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; diff --git a/iscsi-scst/usr/iscsid.c b/iscsi-scst/usr/iscsid.c index 5e517fdbf..653247120 100644 --- a/iscsi-scst/usr/iscsid.c +++ b/iscsi-scst/usr/iscsid.c @@ -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)) { diff --git a/iscsi-scst/usr/iscsid.h b/iscsi-scst/usr/iscsid.h index fba053510..0105a578d 100644 --- a/iscsi-scst/usr/iscsid.h +++ b/iscsi-scst/usr/iscsid.h @@ -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;