mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-12-29 17:25:38 +00:00
scsi: target: Move sess cmd counter to new struct
iSCSI needs to wait on outstanding commands like how SRP and the FC/FCoE drivers do. It can't use target_stop_session() because for MCS support we can't stop the entire session during recovery because if other connections are OK then we want to be able to continue to execute I/O on them. Move the per session cmd counters to a new struct so iSCSI can allocate them per connection. The xcopy code can also just not allocate in the future since it doesn't need to track commands. Signed-off-by: Mike Christie <michael.christie@oracle.com> Link: https://lore.kernel.org/r/20230319015620.96006-2-michael.christie@oracle.com Reviewed-by: Maurizio Lombardi <mlombard@redhat.com> Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
This commit is contained in:
parent
fe15c26ee2
commit
becd9be606
@ -328,7 +328,7 @@ static void target_shutdown_sessions(struct se_node_acl *acl)
|
||||
restart:
|
||||
spin_lock_irqsave(&acl->nacl_sess_lock, flags);
|
||||
list_for_each_entry(sess, &acl->acl_sess_list, sess_acl_list) {
|
||||
if (atomic_read(&sess->stopped))
|
||||
if (sess->cmd_cnt && atomic_read(&sess->cmd_cnt->stopped))
|
||||
continue;
|
||||
|
||||
list_del_init(&sess->sess_acl_list);
|
||||
|
@ -220,11 +220,49 @@ void transport_subsystem_check_init(void)
|
||||
sub_api_initialized = 1;
|
||||
}
|
||||
|
||||
static void target_release_sess_cmd_refcnt(struct percpu_ref *ref)
|
||||
static void target_release_cmd_refcnt(struct percpu_ref *ref)
|
||||
{
|
||||
struct se_session *sess = container_of(ref, typeof(*sess), cmd_count);
|
||||
struct target_cmd_counter *cmd_cnt = container_of(ref,
|
||||
typeof(*cmd_cnt),
|
||||
refcnt);
|
||||
wake_up(&cmd_cnt->refcnt_wq);
|
||||
}
|
||||
|
||||
wake_up(&sess->cmd_count_wq);
|
||||
static struct target_cmd_counter *target_alloc_cmd_counter(void)
|
||||
{
|
||||
struct target_cmd_counter *cmd_cnt;
|
||||
int rc;
|
||||
|
||||
cmd_cnt = kzalloc(sizeof(*cmd_cnt), GFP_KERNEL);
|
||||
if (!cmd_cnt)
|
||||
return NULL;
|
||||
|
||||
init_completion(&cmd_cnt->stop_done);
|
||||
init_waitqueue_head(&cmd_cnt->refcnt_wq);
|
||||
atomic_set(&cmd_cnt->stopped, 0);
|
||||
|
||||
rc = percpu_ref_init(&cmd_cnt->refcnt, target_release_cmd_refcnt, 0,
|
||||
GFP_KERNEL);
|
||||
if (rc)
|
||||
goto free_cmd_cnt;
|
||||
|
||||
return cmd_cnt;
|
||||
|
||||
free_cmd_cnt:
|
||||
kfree(cmd_cnt);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void target_free_cmd_counter(struct target_cmd_counter *cmd_cnt)
|
||||
{
|
||||
/*
|
||||
* Drivers like loop do not call target_stop_session during session
|
||||
* shutdown so we have to drop the ref taken at init time here.
|
||||
*/
|
||||
if (!atomic_read(&cmd_cnt->stopped))
|
||||
percpu_ref_put(&cmd_cnt->refcnt);
|
||||
|
||||
percpu_ref_exit(&cmd_cnt->refcnt);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -238,25 +276,17 @@ int transport_init_session(struct se_session *se_sess)
|
||||
INIT_LIST_HEAD(&se_sess->sess_list);
|
||||
INIT_LIST_HEAD(&se_sess->sess_acl_list);
|
||||
spin_lock_init(&se_sess->sess_cmd_lock);
|
||||
init_waitqueue_head(&se_sess->cmd_count_wq);
|
||||
init_completion(&se_sess->stop_done);
|
||||
atomic_set(&se_sess->stopped, 0);
|
||||
return percpu_ref_init(&se_sess->cmd_count,
|
||||
target_release_sess_cmd_refcnt, 0, GFP_KERNEL);
|
||||
se_sess->cmd_cnt = target_alloc_cmd_counter();
|
||||
if (!se_sess->cmd_cnt)
|
||||
return -ENOMEM;
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(transport_init_session);
|
||||
|
||||
void transport_uninit_session(struct se_session *se_sess)
|
||||
{
|
||||
/*
|
||||
* Drivers like iscsi and loop do not call target_stop_session
|
||||
* during session shutdown so we have to drop the ref taken at init
|
||||
* time here.
|
||||
*/
|
||||
if (!atomic_read(&se_sess->stopped))
|
||||
percpu_ref_put(&se_sess->cmd_count);
|
||||
|
||||
percpu_ref_exit(&se_sess->cmd_count);
|
||||
target_free_cmd_counter(se_sess->cmd_cnt);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -2970,9 +3000,16 @@ int target_get_sess_cmd(struct se_cmd *se_cmd, bool ack_kref)
|
||||
se_cmd->se_cmd_flags |= SCF_ACK_KREF;
|
||||
}
|
||||
|
||||
if (!percpu_ref_tryget_live(&se_sess->cmd_count))
|
||||
ret = -ESHUTDOWN;
|
||||
|
||||
/*
|
||||
* Users like xcopy do not use counters since they never do a stop
|
||||
* and wait.
|
||||
*/
|
||||
if (se_sess->cmd_cnt) {
|
||||
if (!percpu_ref_tryget_live(&se_sess->cmd_cnt->refcnt))
|
||||
ret = -ESHUTDOWN;
|
||||
else
|
||||
se_cmd->cmd_cnt = se_sess->cmd_cnt;
|
||||
}
|
||||
if (ret && ack_kref)
|
||||
target_put_sess_cmd(se_cmd);
|
||||
|
||||
@ -2993,7 +3030,7 @@ static void target_free_cmd_mem(struct se_cmd *cmd)
|
||||
static void target_release_cmd_kref(struct kref *kref)
|
||||
{
|
||||
struct se_cmd *se_cmd = container_of(kref, struct se_cmd, cmd_kref);
|
||||
struct se_session *se_sess = se_cmd->se_sess;
|
||||
struct target_cmd_counter *cmd_cnt = se_cmd->cmd_cnt;
|
||||
struct completion *free_compl = se_cmd->free_compl;
|
||||
struct completion *abrt_compl = se_cmd->abrt_compl;
|
||||
|
||||
@ -3004,7 +3041,8 @@ static void target_release_cmd_kref(struct kref *kref)
|
||||
if (abrt_compl)
|
||||
complete(abrt_compl);
|
||||
|
||||
percpu_ref_put(&se_sess->cmd_count);
|
||||
if (cmd_cnt)
|
||||
percpu_ref_put(&cmd_cnt->refcnt);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -3123,46 +3161,65 @@ void target_show_cmd(const char *pfx, struct se_cmd *cmd)
|
||||
}
|
||||
EXPORT_SYMBOL(target_show_cmd);
|
||||
|
||||
static void target_stop_session_confirm(struct percpu_ref *ref)
|
||||
static void target_stop_cmd_counter_confirm(struct percpu_ref *ref)
|
||||
{
|
||||
struct se_session *se_sess = container_of(ref, struct se_session,
|
||||
cmd_count);
|
||||
complete_all(&se_sess->stop_done);
|
||||
struct target_cmd_counter *cmd_cnt = container_of(ref,
|
||||
struct target_cmd_counter,
|
||||
refcnt);
|
||||
complete_all(&cmd_cnt->stop_done);
|
||||
}
|
||||
|
||||
/**
|
||||
* target_stop_cmd_counter - Stop new IO from being added to the counter.
|
||||
* @cmd_cnt: counter to stop
|
||||
*/
|
||||
static void target_stop_cmd_counter(struct target_cmd_counter *cmd_cnt)
|
||||
{
|
||||
pr_debug("Stopping command counter.\n");
|
||||
if (!atomic_cmpxchg(&cmd_cnt->stopped, 0, 1))
|
||||
percpu_ref_kill_and_confirm(&cmd_cnt->refcnt,
|
||||
target_stop_cmd_counter_confirm);
|
||||
}
|
||||
|
||||
/**
|
||||
* target_stop_session - Stop new IO from being queued on the session.
|
||||
* @se_sess: session to stop
|
||||
* @se_sess: session to stop
|
||||
*/
|
||||
void target_stop_session(struct se_session *se_sess)
|
||||
{
|
||||
pr_debug("Stopping session queue.\n");
|
||||
if (atomic_cmpxchg(&se_sess->stopped, 0, 1) == 0)
|
||||
percpu_ref_kill_and_confirm(&se_sess->cmd_count,
|
||||
target_stop_session_confirm);
|
||||
target_stop_cmd_counter(se_sess->cmd_cnt);
|
||||
}
|
||||
EXPORT_SYMBOL(target_stop_session);
|
||||
|
||||
/**
|
||||
* target_wait_for_sess_cmds - Wait for outstanding commands
|
||||
* @se_sess: session to wait for active I/O
|
||||
* target_wait_for_cmds - Wait for outstanding cmds.
|
||||
* @cmd_cnt: counter to wait for active I/O for.
|
||||
*/
|
||||
void target_wait_for_sess_cmds(struct se_session *se_sess)
|
||||
static void target_wait_for_cmds(struct target_cmd_counter *cmd_cnt)
|
||||
{
|
||||
int ret;
|
||||
|
||||
WARN_ON_ONCE(!atomic_read(&se_sess->stopped));
|
||||
WARN_ON_ONCE(!atomic_read(&cmd_cnt->stopped));
|
||||
|
||||
do {
|
||||
pr_debug("Waiting for running cmds to complete.\n");
|
||||
ret = wait_event_timeout(se_sess->cmd_count_wq,
|
||||
percpu_ref_is_zero(&se_sess->cmd_count),
|
||||
180 * HZ);
|
||||
ret = wait_event_timeout(cmd_cnt->refcnt_wq,
|
||||
percpu_ref_is_zero(&cmd_cnt->refcnt),
|
||||
180 * HZ);
|
||||
} while (ret <= 0);
|
||||
|
||||
wait_for_completion(&se_sess->stop_done);
|
||||
wait_for_completion(&cmd_cnt->stop_done);
|
||||
pr_debug("Waiting for cmds done.\n");
|
||||
}
|
||||
|
||||
/**
|
||||
* target_wait_for_sess_cmds - Wait for outstanding commands
|
||||
* @se_sess: session to wait for active I/O
|
||||
*/
|
||||
void target_wait_for_sess_cmds(struct se_session *se_sess)
|
||||
{
|
||||
target_wait_for_cmds(se_sess->cmd_cnt);
|
||||
}
|
||||
EXPORT_SYMBOL(target_wait_for_sess_cmds);
|
||||
|
||||
/*
|
||||
|
@ -600,6 +600,7 @@ struct iscsit_conn {
|
||||
struct iscsi_tpg_np *tpg_np;
|
||||
/* Pointer to parent session */
|
||||
struct iscsit_session *sess;
|
||||
struct target_cmd_counter *cmd_cnt;
|
||||
int bitmap_id;
|
||||
int rx_thread_active;
|
||||
struct task_struct *rx_thread;
|
||||
|
@ -494,6 +494,7 @@ struct se_cmd {
|
||||
struct se_lun *se_lun;
|
||||
/* Only used for internal passthrough and legacy TCM fabric modules */
|
||||
struct se_session *se_sess;
|
||||
struct target_cmd_counter *cmd_cnt;
|
||||
struct se_tmr_req *se_tmr_req;
|
||||
struct llist_node se_cmd_list;
|
||||
struct completion *free_compl;
|
||||
@ -619,22 +620,26 @@ static inline struct se_node_acl *fabric_stat_to_nacl(struct config_item *item)
|
||||
acl_fabric_stat_group);
|
||||
}
|
||||
|
||||
struct se_session {
|
||||
struct target_cmd_counter {
|
||||
struct percpu_ref refcnt;
|
||||
wait_queue_head_t refcnt_wq;
|
||||
struct completion stop_done;
|
||||
atomic_t stopped;
|
||||
};
|
||||
|
||||
struct se_session {
|
||||
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 percpu_ref cmd_count;
|
||||
struct list_head sess_list;
|
||||
struct list_head sess_acl_list;
|
||||
spinlock_t sess_cmd_lock;
|
||||
wait_queue_head_t cmd_count_wq;
|
||||
struct completion stop_done;
|
||||
void *sess_cmd_map;
|
||||
struct sbitmap_queue sess_tag_pool;
|
||||
struct target_cmd_counter *cmd_cnt;
|
||||
};
|
||||
|
||||
struct se_device;
|
||||
|
Loading…
Reference in New Issue
Block a user