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,
};
u32 tid;
aligned_u64 sid;
u32 cid;
- u32 state;
+ u32 code;
};
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)
{
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)
{
switch (cmd) {
case ADD_TARGET:
case DEL_TARGET:
+ case ENABLE_TARGET:
+ case DISABLE_TARGET:
case ADD_SESSION:
case DEL_SESSION:
case ISCSI_PARAM_SET:
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;
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));
}
#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;
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;
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;
}
.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,
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];
};
/* 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);
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);
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);
#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);
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);
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)
{
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 */
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
+#include <sys/ioctl.h>
#include <asm/types.h>
#include <sys/socket.h>
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)
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) {
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)
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;
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)) {
struct __qelem sessions_list;
+ unsigned int tgt_enabled:1;
+
u32 tid;
char name[ISCSI_NAME_LEN];
char *alias;
/* 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;