Print this page
NEX-20098 idm_refcnt_unref_task() fails to hold mutex before calling REFCNT_AUDIT
Reviewed by: Rob Gittins <rob.gittins@nexenta.com>
Reviewed by: Evan Layton <evan.layton@nexenta.com>
NEX-9981 Deadman timer panic from idm_refcnt_wait_ref thread while offlining iSCSI targets
Reviewed by: Evan Layton <evan.layton@nexenta.com>
Reviewed by: Sanjay Nadkarni <sanjay.nadkarni@nexenta.com>
Reviewed by: Rob Gittins <rob.gittins@nexenta.com>
NEX-6018 Return of the walking dead idm_refcnt_wait_ref comstar threads
Reviewed by: Rick McNeal <rick.mcneal@nexenta.com>
Reviewed by: Evan Layton <evan.layton@nexenta.com>
*** 18,27 ****
--- 18,28 ----
*
* CDDL HEADER END
*/
/*
* Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2019 Nexenta Systems, Inc. All rights reserved.
*/
#include <sys/cpuvar.h>
#include <sys/conf.h>
#include <sys/file.h>
*** 59,69 ****
static int _idm_fini(void);
static void idm_buf_bind_in_locked(idm_task_t *idt, idm_buf_t *buf);
static void idm_buf_bind_out_locked(idm_task_t *idt, idm_buf_t *buf);
static void idm_buf_unbind_in_locked(idm_task_t *idt, idm_buf_t *buf);
static void idm_buf_unbind_out_locked(idm_task_t *idt, idm_buf_t *buf);
! static void idm_task_abort_one(idm_conn_t *ic, idm_task_t *idt,
idm_abort_type_t abort_type);
static void idm_task_aborted(idm_task_t *idt, idm_status_t status);
static idm_pdu_t *idm_pdu_alloc_common(uint_t hdrlen, uint_t datalen,
int sleepflag);
--- 60,70 ----
static int _idm_fini(void);
static void idm_buf_bind_in_locked(idm_task_t *idt, idm_buf_t *buf);
static void idm_buf_bind_out_locked(idm_task_t *idt, idm_buf_t *buf);
static void idm_buf_unbind_in_locked(idm_task_t *idt, idm_buf_t *buf);
static void idm_buf_unbind_out_locked(idm_task_t *idt, idm_buf_t *buf);
! static stmf_status_t idm_task_abort_one(idm_conn_t *ic, idm_task_t *idt,
idm_abort_type_t abort_type);
static void idm_task_aborted(idm_task_t *idt, idm_status_t status);
static idm_pdu_t *idm_pdu_alloc_common(uint_t hdrlen, uint_t datalen,
int sleepflag);
*** 1520,1534 ****
idm_task_rele(idm_task_t *idt)
{
idm_refcnt_rele(&idt->idt_refcnt);
}
! void
idm_task_abort(idm_conn_t *ic, idm_task_t *idt, idm_abort_type_t abort_type)
{
idm_task_t *task;
int idx;
/*
* Passing NULL as the task indicates that all tasks
* for this connection should be aborted.
*/
--- 1521,1536 ----
idm_task_rele(idm_task_t *idt)
{
idm_refcnt_rele(&idt->idt_refcnt);
}
! stmf_status_t
idm_task_abort(idm_conn_t *ic, idm_task_t *idt, idm_abort_type_t abort_type)
{
idm_task_t *task;
int idx;
+ stmf_status_t s = STMF_SUCCESS;
/*
* Passing NULL as the task indicates that all tasks
* for this connection should be aborted.
*/
*** 1546,1565 ****
mutex_enter(&task->idt_mutex);
if ((task->idt_state != TASK_IDLE) &&
(task->idt_state != TASK_COMPLETE) &&
(task->idt_ic == ic)) {
rw_exit(&idm.idm_taskid_table_lock);
! idm_task_abort_one(ic, task, abort_type);
rw_enter(&idm.idm_taskid_table_lock, RW_READER);
} else
mutex_exit(&task->idt_mutex);
}
rw_exit(&idm.idm_taskid_table_lock);
} else {
mutex_enter(&idt->idt_mutex);
! idm_task_abort_one(ic, idt, abort_type);
}
}
static void
idm_task_abort_unref_cb(void *ref)
{
--- 1548,1568 ----
mutex_enter(&task->idt_mutex);
if ((task->idt_state != TASK_IDLE) &&
(task->idt_state != TASK_COMPLETE) &&
(task->idt_ic == ic)) {
rw_exit(&idm.idm_taskid_table_lock);
! s = idm_task_abort_one(ic, task, abort_type);
rw_enter(&idm.idm_taskid_table_lock, RW_READER);
} else
mutex_exit(&task->idt_mutex);
}
rw_exit(&idm.idm_taskid_table_lock);
} else {
mutex_enter(&idt->idt_mutex);
! s = idm_task_abort_one(ic, idt, abort_type);
}
+ return (s);
}
static void
idm_task_abort_unref_cb(void *ref)
{
*** 1586,1598 ****
/*
* Abort the idm task.
* Caller must hold the task mutex, which will be released before return
*/
! static void
idm_task_abort_one(idm_conn_t *ic, idm_task_t *idt, idm_abort_type_t abort_type)
{
/* Caller must hold connection mutex */
ASSERT(mutex_owned(&idt->idt_mutex));
switch (idt->idt_state) {
case TASK_ACTIVE:
switch (abort_type) {
--- 1589,1603 ----
/*
* Abort the idm task.
* Caller must hold the task mutex, which will be released before return
*/
! static stmf_status_t
idm_task_abort_one(idm_conn_t *ic, idm_task_t *idt, idm_abort_type_t abort_type)
{
+ stmf_status_t s = STMF_SUCCESS;
+
/* Caller must hold connection mutex */
ASSERT(mutex_owned(&idt->idt_mutex));
switch (idt->idt_state) {
case TASK_ACTIVE:
switch (abort_type) {
*** 1607,1617 ****
* references are released the callback will call
* idm_task_aborted().
*/
idm_refcnt_async_wait_ref(&idt->idt_refcnt,
&idm_task_abort_unref_cb);
! return;
case AT_INTERNAL_ABORT:
case AT_TASK_MGMT_ABORT:
idt->idt_state = TASK_ABORTING;
mutex_exit(&idt->idt_mutex);
ic->ic_transport_ops->it_free_task_rsrc(idt);
--- 1612,1622 ----
* references are released the callback will call
* idm_task_aborted().
*/
idm_refcnt_async_wait_ref(&idt->idt_refcnt,
&idm_task_abort_unref_cb);
! return (s);
case AT_INTERNAL_ABORT:
case AT_TASK_MGMT_ABORT:
idt->idt_state = TASK_ABORTING;
mutex_exit(&idt->idt_mutex);
ic->ic_transport_ops->it_free_task_rsrc(idt);
*** 1621,1631 ****
* references are released the callback will call
* idm_task_aborted().
*/
idm_refcnt_async_wait_ref(&idt->idt_refcnt,
&idm_task_abort_unref_cb);
! return;
default:
ASSERT(0);
}
break;
case TASK_SUSPENDING:
--- 1626,1636 ----
* references are released the callback will call
* idm_task_aborted().
*/
idm_refcnt_async_wait_ref(&idt->idt_refcnt,
&idm_task_abort_unref_cb);
! return (s);
default:
ASSERT(0);
}
break;
case TASK_SUSPENDING:
*** 1661,1671 ****
* set the state to TASK_ABORTING instead of
* TASK_ABORTED so we can use the same code path.
*/
idm_refcnt_async_wait_ref(&idt->idt_refcnt,
&idm_task_abort_unref_cb);
! return;
default:
ASSERT(0);
}
break;
case TASK_ABORTING:
--- 1666,1676 ----
* set the state to TASK_ABORTING instead of
* TASK_ABORTED so we can use the same code path.
*/
idm_refcnt_async_wait_ref(&idt->idt_refcnt,
&idm_task_abort_unref_cb);
! return (s);
default:
ASSERT(0);
}
break;
case TASK_ABORTING:
*** 1680,1700 ****
default:
ASSERT(0);
}
break;
case TASK_COMPLETE:
! /*
! * In this case, let it go. The status has already been
! * sent (which may or may not get successfully transmitted)
! * and we don't want to end up in a race between completing
! * the status PDU and marking the task suspended.
! */
break;
default:
ASSERT(0);
}
mutex_exit(&idt->idt_mutex);
}
static void
idm_task_aborted(idm_task_t *idt, idm_status_t status)
{
--- 1685,1703 ----
default:
ASSERT(0);
}
break;
case TASK_COMPLETE:
! idm_refcnt_wait_ref(&idt->idt_refcnt);
! s = STMF_ABORT_SUCCESS;
break;
default:
ASSERT(0);
}
mutex_exit(&idt->idt_mutex);
+
+ return (s);
}
static void
idm_task_aborted(idm_task_t *idt, idm_status_t status)
{
*** 2153,2163 ****
--- 2156,2168 ----
static void
idm_refcnt_unref_task(void *refcnt_void)
{
idm_refcnt_t *refcnt = refcnt_void;
+ mutex_enter(&refcnt->ir_mutex);
REFCNT_AUDIT(refcnt);
+ mutex_exit(&refcnt->ir_mutex);
(*refcnt->ir_cb)(refcnt->ir_referenced_obj);
}
void
idm_refcnt_rele(idm_refcnt_t *refcnt)
*** 2261,2270 ****
--- 2266,2298 ----
return;
}
mutex_exit(&refcnt->ir_mutex);
}
+ /*
+ * used to determine the status of the refcnt.
+ *
+ * if refcnt is 0 return is 0
+ * if refcnt is negative return is -1
+ * if refcnt > 0 and no waiters return is 1
+ * if refcnt > 0 and waiters return is 2
+ */
+ int
+ idm_refcnt_is_held(idm_refcnt_t *refcnt)
+ {
+ if (refcnt->ir_refcnt < 0)
+ return (-1);
+
+ if (refcnt->ir_refcnt == 0)
+ return (0);
+
+ if (refcnt->ir_waiting == REF_NOWAIT && refcnt->ir_refcnt > 0)
+ return (1);
+
+ return (2);
+ }
+
void
idm_conn_hold(idm_conn_t *ic)
{
idm_refcnt_hold(&ic->ic_refcnt);
}