@@ -155,6 +155,7 @@ void transport_dump_dev_info(struct se_device *, struct se_lun *,
bool target_check_fua(struct se_device *dev);
void __target_execute_cmd(struct se_cmd *, bool);
void target_release_session(struct se_session *);
+int target_close_session_sync(struct se_portal_group *, int);
/* target_core_stat.c */
void target_stat_setup_dev_default_groups(struct se_device *);
@@ -650,6 +650,7 @@ static int target_release_res(struct se_device *dev, void *data)
void transport_deregister_session(struct se_session *se_sess)
{
struct se_portal_group *se_tpg = se_sess->se_tpg;
+ struct completion *removal_comp;
unsigned long flags;
if (!se_tpg) {
@@ -660,6 +661,8 @@ void transport_deregister_session(struct se_session *se_sess)
spin_lock_irqsave(&se_tpg->session_lock, flags);
list_del(&se_sess->sess_list);
se_sess->se_tpg = NULL;
+ removal_comp = se_sess->removal_comp;
+ se_sess->removal_comp = NULL;
spin_unlock_irqrestore(&se_tpg->session_lock, flags);
/*
@@ -680,11 +683,98 @@ void transport_deregister_session(struct se_session *se_sess)
*/
transport_free_session(se_sess);
+
+ if (removal_comp)
+ complete(removal_comp);
}
EXPORT_SYMBOL(transport_deregister_session);
+/**
+ * target_close_session_sync - Request fabric remove session and wait removal
+ * @se_tpg: se_portal_group that is the parent of the sess to remove
+ * @sid: session id
+ */
+int target_close_session_sync(struct se_portal_group *se_tpg, int sid)
+{
+ DECLARE_COMPLETION_ONSTACK(removal_comp);
+ struct se_session *se_sess;
+ unsigned long flags;
+ int ret;
+
+retry:
+ spin_lock_irqsave(&se_tpg->session_lock, flags);
+ list_for_each_entry(se_sess, &se_tpg->tpg_sess_list, sess_list) {
+ if (se_sess->sid == sid) {
+ config_group_get(&se_sess->group);
+ goto found;
+ }
+ }
+ spin_unlock_irqrestore(&se_tpg->session_lock, flags);
+ return -ENODEV;
+
+found:
+ if (!se_sess->tfo->close_session) {
+ pr_err("Session %d does not support configfs session removal.",
+ se_sess->sid);
+ return -EINVAL;
+ }
+
+ if (se_sess->sess_tearing_down || se_sess->sess_remove_running ||
+ se_sess->removal_comp) {
+ spin_unlock_irqrestore(&se_tpg->session_lock, flags);
+ config_group_put(&se_sess->group);
+ /*
+ * Either the transport started a removal already or another
+ * caller of this function did. Wait for it to be torn down,
+ * so caller knows it's safe to proceed with operations like
+ * parent removals when this returns.
+ */
+ msleep(250);
+ goto retry;
+ }
+
+ se_sess->removal_comp = &removal_comp;
+ pr_debug("Closing session-%d\n", se_sess->sid);
+ spin_unlock_irqrestore(&se_tpg->session_lock, flags);
+
+ ret = se_sess->tfo->close_session(se_sess);
+ if (ret < 0) {
+ pr_debug("Close for session-%d failed %d\n", se_sess->sid, ret);
+ if (ret != -ENODEV) {
+ spin_lock_irqsave(&se_tpg->session_lock, flags);
+ se_sess->removal_comp = NULL;
+ spin_unlock_irqrestore(&se_tpg->session_lock, flags);
+ goto put_sess;
+ }
+ /*
+ * Raced with fabric specific nexus interface, but we set our
+ * compeltion before they called target_remove_session, so we
+ * can just wait below for them to call complete.
+ */
+ }
+
+ wait_for_completion(&removal_comp);
+
+put_sess:
+ config_group_put(&se_sess->group);
+ return ret;
+}
+
void target_remove_session(struct se_session *se_sess)
{
+ struct se_portal_group *se_tpg = se_sess->se_tpg;
+ unsigned long flags;
+
+ pr_debug("Removing session-%d\n", se_sess->sid);
+
+ spin_lock_irqsave(&se_tpg->session_lock, flags);
+ if (se_sess->sess_remove_running) {
+ spin_unlock_irqrestore(&se_tpg->session_lock, flags);
+ return;
+ }
+ se_sess->sess_remove_running = 1;
+ spin_unlock_irqrestore(&se_tpg->session_lock, flags);
+
transport_deregister_session_configfs(se_sess);
transport_deregister_session(se_sess);
}
@@ -609,12 +609,14 @@ static inline struct se_node_acl *fabric_stat_to_nacl(struct config_item *item)
struct se_session {
unsigned sess_tearing_down:1;
+ unsigned sess_remove_running:1;
u64 sess_bin_isid;
enum target_prot_op sup_prot_ops;
enum target_prot_type sess_prot_type;
struct se_node_acl *se_node_acl;
struct se_portal_group *se_tpg;
void *fabric_sess_ptr;
+ struct completion *removal_comp;
struct percpu_ref cmd_count;
struct list_head sess_list;
struct list_head sess_acl_list;
We need to be able to delete sessions from userspace like is done for the ACL path or is done for other objects like tpg, lun, etc. This patch adds a helper function that calls the fabric module close_session callback and then waits for the removal to complete. It will be used in the next patch so userspace can remove sessions before deleting TPGs/ACLs. Signed-off-by: Mike Christie <michael.christie@oracle.com> --- drivers/target/target_core_internal.h | 1 + drivers/target/target_core_transport.c | 90 ++++++++++++++++++++++++++++++++++ include/target/target_core_base.h | 2 + 3 files changed, 93 insertions(+)