#include <smbsrv/smb_kproto.h>
#include <smbsrv/smb_fsops.h>
#include <sys/nbmlock.h>
#include <sys/param.h>
extern caller_context_t smb_ct;
#ifdef DEBUG
int smb_lock_debug = 0;
static void smb_lock_dump1(smb_lock_t *);
static void smb_lock_dumplist(smb_llist_t *);
static void smb_lock_dumpnode(smb_node_t *);
#endif
static void smb_lock_posix_unlock(smb_node_t *, smb_lock_t *, cred_t *);
static boolean_t smb_is_range_unlocked(uint64_t, uint64_t, uint32_t,
smb_llist_t *, uint64_t *);
static int smb_lock_range_overlap(smb_lock_t *, uint64_t, uint64_t);
static uint32_t smb_lock_range_lckrules(smb_ofile_t *, smb_lock_t *,
smb_lock_t **);
static uint32_t smb_lock_wait(smb_request_t *, smb_lock_t *, smb_lock_t *);
static uint32_t smb_lock_range_ulckrules(smb_ofile_t *,
uint64_t, uint64_t, uint32_t, smb_lock_t **);
static smb_lock_t *smb_lock_create(smb_request_t *, uint64_t, uint64_t,
uint32_t, uint32_t, uint32_t);
static void smb_lock_destroy(smb_lock_t *);
static void smb_lock_free(smb_lock_t *);
uint32_t
smb_lock_get_lock_count(smb_node_t *node, smb_ofile_t *of)
{
smb_lock_t *lock;
smb_llist_t *llist;
uint32_t count = 0;
SMB_NODE_VALID(node);
SMB_OFILE_VALID(of);
llist = &node->n_lock_list;
smb_llist_enter(llist, RW_READER);
for (lock = smb_llist_head(llist);
lock != NULL;
lock = smb_llist_next(llist, lock)) {
if (lock->l_file == of)
++count;
}
smb_llist_exit(llist);
return (count);
}
uint32_t
smb_unlock_range(
smb_request_t *sr,
uint64_t start,
uint64_t length,
uint32_t pid)
{
smb_ofile_t *file = sr->fid_ofile;
smb_node_t *node = file->f_node;
smb_lock_t *lock = NULL;
uint32_t status;
if (length > 1 &&
(start + length) < start)
return (NT_STATUS_INVALID_LOCK_RANGE);
#ifdef DEBUG
if (smb_lock_debug) {
cmn_err(CE_CONT, "smb_unlock_range "
"off=0x%llx, len=0x%llx, f=%p, pid=%d\n",
(long long)start, (long long)length,
(void *)sr->fid_ofile, pid);
}
#endif
smb_llist_enter(&node->n_lock_list, RW_WRITER);
status = smb_lock_range_ulckrules(file, start, length, pid, &lock);
if (status != NT_STATUS_SUCCESS) {
ASSERT(lock == NULL);
}
if (lock != NULL) {
smb_llist_remove(&node->n_lock_list, lock);
smb_lock_posix_unlock(node, lock, sr->user_cr);
}
#ifdef DEBUG
if (smb_lock_debug && lock == NULL) {
cmn_err(CE_CONT, "unlock failed, 0x%x\n", status);
smb_lock_dumpnode(node);
}
#endif
smb_llist_exit(&node->n_lock_list);
if (lock != NULL)
smb_lock_destroy(lock);
return (status);
}
uint32_t
smb_lock_range(
smb_request_t *sr,
uint64_t start,
uint64_t length,
uint32_t pid,
uint32_t locktype,
uint32_t timeout)
{
smb_ofile_t *file = sr->fid_ofile;
smb_node_t *node = file->f_node;
smb_lock_t *lock;
smb_lock_t *conflict = NULL;
uint32_t result;
int rc;
boolean_t lock_has_timeout =
(timeout != 0 && timeout != UINT_MAX);
if (length > 1 &&
(start + length) < start)
return (NT_STATUS_INVALID_LOCK_RANGE);
#ifdef DEBUG
if (smb_lock_debug) {
cmn_err(CE_CONT, "smb_lock_range "
"off=0x%llx, len=0x%llx, "
"f=%p, pid=%d, typ=%d, tmo=%d\n",
(long long)start, (long long)length,
(void *)sr->fid_ofile, pid, locktype, timeout);
}
#endif
lock = smb_lock_create(sr, start, length, pid, locktype, timeout);
smb_llist_enter(&node->n_lock_list, RW_WRITER);
for (;;) {
result = smb_lock_range_lckrules(file, lock, &conflict);
switch (result) {
case NT_STATUS_LOCK_NOT_GRANTED:
break;
case NT_STATUS_SUCCESS:
case NT_STATUS_FILE_CLOSED:
goto break_loop;
default:
cmn_err(CE_CONT, "smb_lock_range1, status 0x%x\n",
result);
goto break_loop;
}
if (timeout == 0)
goto break_loop;
result = smb_lock_wait(sr, lock, conflict);
conflict = NULL;
switch (result) {
case NT_STATUS_SUCCESS:
break;
case NT_STATUS_TIMEOUT:
timeout = 0;
break;
case NT_STATUS_CANCELLED:
case NT_STATUS_FILE_CLOSED:
goto break_loop;
default:
cmn_err(CE_CONT, "smb_lock_range2, status 0x%x\n",
result);
goto break_loop;
}
}
break_loop:
lock->l_blocked_by = NULL;
if (result != NT_STATUS_SUCCESS) {
if (result == NT_STATUS_FILE_CLOSED)
result = NT_STATUS_RANGE_NOT_LOCKED;
if (sr->session->dialect <= NT_LM_0_12 &&
result == NT_STATUS_LOCK_NOT_GRANTED) {
if (lock_has_timeout)
result = NT_STATUS_FILE_LOCK_CONFLICT;
if ((lock->l_start >= 0xef000000) &&
!(lock->l_start & (1ULL << 63))) {
result = NT_STATUS_FILE_LOCK_CONFLICT;
}
mutex_enter(&file->f_mutex);
if ((file->f_flags & SMB_OFLAGS_LLF_POS_VALID) &&
(lock->l_start == file->f_llf_pos)) {
result = NT_STATUS_FILE_LOCK_CONFLICT;
}
mutex_exit(&file->f_mutex);
}
mutex_enter(&file->f_mutex);
file->f_llf_pos = lock->l_start;
file->f_flags |= SMB_OFLAGS_LLF_POS_VALID;
mutex_exit(&file->f_mutex);
smb_lock_free(lock);
} else {
rc = smb_fsop_frlock(node, lock, B_FALSE, sr->user_cr);
if (rc != 0) {
#ifdef DEBUG
if (smb_lock_debug)
cmn_err(CE_CONT, "fop_frlock, err=%d\n", rc);
#endif
result = NT_STATUS_FILE_LOCK_CONFLICT;
} else {
if (lock->l_type == SMB_LOCK_TYPE_READWRITE)
smb_llist_insert_head(&node->n_lock_list, lock);
else
smb_llist_insert_tail(&node->n_lock_list, lock);
}
}
#ifdef DEBUG
if (smb_lock_debug && result != 0) {
cmn_err(CE_CONT, "lock failed, 0x%x\n", result);
smb_lock_dumpnode(node);
}
#endif
smb_llist_exit(&node->n_lock_list);
if (result == NT_STATUS_SUCCESS) {
(void) smb_oplock_break_WRITE(node, file);
}
return (result);
}
int
smb_lock_range_access(
smb_request_t *sr,
smb_node_t *node,
uint64_t start,
uint64_t length,
boolean_t will_write)
{
smb_lock_t *lock;
smb_llist_t *llist;
uint32_t lk_pid = 0;
int status = NT_STATUS_SUCCESS;
if (length == 0)
return (status);
if (sr->session->dialect < SMB_VERS_2_BASE)
lk_pid = sr->smb_pid & 0xFFFF;
llist = &node->n_lock_list;
smb_llist_enter(llist, RW_READER);
for (lock = smb_llist_head(llist);
lock != NULL;
lock = smb_llist_next(llist, lock)) {
if (!smb_lock_range_overlap(lock, start, length))
continue;
if (lock->l_type == SMB_LOCK_TYPE_READONLY && !will_write)
continue;
if (lock->l_type == SMB_LOCK_TYPE_READWRITE &&
lock->l_file == sr->fid_ofile &&
lock->l_pid == lk_pid)
continue;
#ifdef DEBUG
if (smb_lock_debug) {
cmn_err(CE_CONT, "smb_lock_range_access conflict: "
"off=0x%llx, len=0x%llx, "
"f=%p, pid=%d, typ=%d\n",
(long long)lock->l_start,
(long long)lock->l_length,
(void *)lock->l_file,
lock->l_pid, lock->l_type);
}
#endif
status = NT_STATUS_FILE_LOCK_CONFLICT;
break;
}
smb_llist_exit(llist);
return (status);
}
void
smb_node_destroy_lock_by_ofile(smb_node_t *node, smb_ofile_t *file)
{
cred_t *kcr = zone_kcred();
smb_lock_t *lock;
smb_lock_t *nxtl;
list_t destroy_list;
SMB_NODE_VALID(node);
ASSERT(node->n_refcnt);
smb_llist_enter(&node->n_wlock_list, RW_READER);
for (lock = smb_llist_head(&node->n_wlock_list);
lock != NULL;
lock = smb_llist_next(&node->n_wlock_list, lock)) {
if (lock->l_file == file) {
mutex_enter(&lock->l_mutex);
lock->l_blocked_by = NULL;
lock->l_flags |= SMB_LOCK_FLAG_CLOSED;
cv_broadcast(&lock->l_cv);
mutex_exit(&lock->l_mutex);
}
}
smb_llist_exit(&node->n_wlock_list);
list_create(&destroy_list, sizeof (smb_lock_t),
offsetof(smb_lock_t, l_lnd));
smb_llist_enter(&node->n_lock_list, RW_WRITER);
lock = smb_llist_head(&node->n_lock_list);
while (lock) {
nxtl = smb_llist_next(&node->n_lock_list, lock);
if (lock->l_file == file) {
smb_llist_remove(&node->n_lock_list, lock);
smb_lock_posix_unlock(node, lock, kcr);
list_insert_tail(&destroy_list, lock);
}
lock = nxtl;
}
smb_llist_exit(&node->n_lock_list);
lock = list_head(&destroy_list);
while (lock) {
nxtl = list_next(&destroy_list, lock);
list_remove(&destroy_list, lock);
smb_lock_destroy(lock);
lock = nxtl;
}
list_destroy(&destroy_list);
}
uint32_t
smb_lock_range_cancel(smb_request_t *sr,
uint64_t start, uint64_t length, uint32_t pid)
{
smb_node_t *node;
smb_lock_t *lock;
uint32_t status = NT_STATUS_RANGE_NOT_LOCKED;
int cnt = 0;
node = sr->fid_ofile->f_node;
smb_llist_enter(&node->n_wlock_list, RW_READER);
#ifdef DEBUG
if (smb_lock_debug) {
cmn_err(CE_CONT, "smb_lock_range_cancel:\n"
"\tstart=0x%llx, len=0x%llx, of=%p, pid=%d\n",
(long long)start, (long long)length,
(void *)sr->fid_ofile, pid);
}
#endif
for (lock = smb_llist_head(&node->n_wlock_list);
lock != NULL;
lock = smb_llist_next(&node->n_wlock_list, lock)) {
if ((start == lock->l_start) &&
(length == lock->l_length) &&
lock->l_file == sr->fid_ofile &&
lock->l_pid == pid) {
mutex_enter(&lock->l_mutex);
lock->l_blocked_by = NULL;
lock->l_flags |= SMB_LOCK_FLAG_CANCELLED;
cv_broadcast(&lock->l_cv);
mutex_exit(&lock->l_mutex);
status = NT_STATUS_SUCCESS;
cnt++;
}
}
#ifdef DEBUG
if (smb_lock_debug && cnt != 1) {
cmn_err(CE_CONT, "cancel found %d\n", cnt);
smb_lock_dumpnode(node);
}
#endif
smb_llist_exit(&node->n_wlock_list);
return (status);
}
void
smb_lock_range_error(smb_request_t *sr, uint32_t status32)
{
uint16_t errcode;
if (status32 == NT_STATUS_CANCELLED) {
status32 = NT_STATUS_FILE_LOCK_CONFLICT;
errcode = ERROR_LOCK_VIOLATION;
} else {
errcode = ERRlock;
}
smbsr_error(sr, status32, ERRDOS, errcode);
}
DWORD
smb_nbl_conflict(smb_node_t *node, uint64_t off, uint64_t len, nbl_op_t op)
{
int svmand;
SMB_NODE_VALID(node);
ASSERT(smb_node_in_crit(node));
ASSERT(op == NBL_READ || op == NBL_WRITE || op == NBL_READWRITE ||
op == NBL_REMOVE || op == NBL_RENAME);
if (smb_node_is_dir(node))
return (NT_STATUS_SUCCESS);
if (nbl_share_conflict(node->vp, op, &smb_ct))
return (NT_STATUS_SHARING_VIOLATION);
if (op == NBL_RENAME || op == NBL_REMOVE)
op = NBL_READWRITE;
if (nbl_svmand(node->vp, zone_kcred(), &svmand))
svmand = 1;
if (nbl_lock_conflict(node->vp, op, off, len, svmand, &smb_ct))
return (NT_STATUS_FILE_LOCK_CONFLICT);
return (NT_STATUS_SUCCESS);
}
static void
smb_lock_posix_unlock(smb_node_t *node, smb_lock_t *lock, cred_t *cr)
{
uint64_t new_mark;
uint64_t unlock_start;
uint64_t unlock_end;
smb_lock_t new_unlock;
smb_llist_t *llist;
boolean_t can_unlock;
new_mark = 0;
unlock_start = lock->l_start;
unlock_end = unlock_start + lock->l_length;
llist = &node->n_lock_list;
for (;;) {
can_unlock = smb_is_range_unlocked(unlock_start, unlock_end,
lock->l_file->f_uniqid, llist, &new_mark);
if (can_unlock) {
if (new_mark) {
new_unlock = *lock;
new_unlock.l_start = unlock_start;
new_unlock.l_length = new_mark - unlock_start;
(void) smb_fsop_frlock(node, &new_unlock,
B_TRUE, cr);
unlock_start = new_mark;
} else {
new_unlock = *lock;
new_unlock.l_start = unlock_start;
new_unlock.l_length = unlock_end - unlock_start;
(void) smb_fsop_frlock(node, &new_unlock,
B_TRUE, cr);
break;
}
} else if (new_mark) {
unlock_start = new_mark;
} else {
break;
}
}
}
#define RANGE_NO_OVERLAP 0
#define RANGE_OVERLAP 1
static int
smb_lock_range_overlap(struct smb_lock *lock, uint64_t start, uint64_t length)
{
if (length == 0) {
if ((lock->l_start < start) &&
((lock->l_start + lock->l_length) > start))
return (RANGE_OVERLAP);
return (RANGE_NO_OVERLAP);
}
if ((start == lock->l_start) && (length == lock->l_length))
return (RANGE_OVERLAP);
if (start < lock->l_start) {
if (start + length > lock->l_start)
return (RANGE_OVERLAP);
} else if (start < lock->l_start + lock->l_length)
return (RANGE_OVERLAP);
return (RANGE_NO_OVERLAP);
}
static uint32_t
smb_lock_range_lckrules(
smb_ofile_t *file,
smb_lock_t *dlock,
smb_lock_t **conflictp)
{
smb_node_t *node = file->f_node;
smb_lock_t *lock;
uint32_t status = NT_STATUS_SUCCESS;
if (!smb_ofile_is_open(file)) {
return (NT_STATUS_FILE_CLOSED);
}
for (lock = smb_llist_head(&node->n_lock_list);
lock != NULL;
lock = smb_llist_next(&node->n_lock_list, lock)) {
if (!smb_lock_range_overlap(lock, dlock->l_start,
dlock->l_length))
continue;
if ((lock->l_type == SMB_LOCK_TYPE_READONLY) &&
(dlock->l_type == SMB_LOCK_TYPE_READONLY)) {
continue;
}
if ((dlock->l_type == SMB_LOCK_TYPE_READONLY) &&
!(lock->l_type == SMB_LOCK_TYPE_READONLY)) {
if (lock->l_file == dlock->l_file &&
lock->l_pid == dlock->l_pid) {
continue;
}
}
*conflictp = lock;
status = NT_STATUS_LOCK_NOT_GRANTED;
break;
}
return (status);
}
static void
smb_lock_cancel_sr(smb_request_t *sr)
{
smb_lock_t *lock = sr->cancel_arg2;
ASSERT(lock->l_magic == SMB_LOCK_MAGIC);
mutex_enter(&lock->l_mutex);
lock->l_blocked_by = NULL;
lock->l_flags |= SMB_LOCK_FLAG_CANCELLED;
cv_broadcast(&lock->l_cv);
mutex_exit(&lock->l_mutex);
}
static uint32_t
smb_lock_wait(smb_request_t *sr, smb_lock_t *lock, smb_lock_t *conflict)
{
smb_node_t *node;
clock_t rc;
uint32_t status = NT_STATUS_SUCCESS;
node = lock->l_file->f_node;
ASSERT(node == conflict->l_file->f_node);
mutex_enter(&lock->l_mutex);
lock->l_blocked_by = conflict;
mutex_exit(&lock->l_mutex);
mutex_enter(&conflict->l_mutex);
conflict->l_conflicts++;
mutex_exit(&conflict->l_mutex);
smb_llist_enter(&node->n_wlock_list, RW_WRITER);
smb_llist_insert_tail(&node->n_wlock_list, lock);
smb_llist_exit(&node->n_wlock_list);
#ifdef DEBUG
if (smb_lock_debug) {
cmn_err(CE_CONT, "smb_lock_wait: lock=%p conflict=%p\n",
(void *)lock, (void *)conflict);
smb_lock_dumpnode(node);
}
#endif
smb_llist_exit(&node->n_lock_list);
conflict = NULL;
mutex_enter(&sr->sr_mutex);
if (sr->sr_state == SMB_REQ_STATE_ACTIVE) {
sr->sr_state = SMB_REQ_STATE_WAITING_LOCK;
sr->cancel_method = smb_lock_cancel_sr;
sr->cancel_arg2 = lock;
} else {
status = NT_STATUS_CANCELLED;
}
mutex_exit(&sr->sr_mutex);
mutex_enter(&lock->l_mutex);
while (status == NT_STATUS_SUCCESS &&
lock->l_blocked_by != NULL) {
if (lock->l_flags & SMB_LOCK_FLAG_INDEFINITE) {
cv_wait(&lock->l_cv, &lock->l_mutex);
} else {
rc = cv_timedwait(&lock->l_cv,
&lock->l_mutex, lock->l_end_time);
if (rc < 0)
status = NT_STATUS_TIMEOUT;
}
}
if (status == NT_STATUS_SUCCESS) {
if (lock->l_flags & SMB_LOCK_FLAG_CANCELLED)
status = NT_STATUS_CANCELLED;
if (lock->l_flags & SMB_LOCK_FLAG_CLOSED)
status = NT_STATUS_FILE_CLOSED;
}
mutex_exit(&lock->l_mutex);
mutex_enter(&sr->sr_mutex);
switch_state:
switch (sr->sr_state) {
case SMB_REQ_STATE_WAITING_LOCK:
sr->sr_state = SMB_REQ_STATE_ACTIVE;
break;
case SMB_REQ_STATE_CANCEL_PENDING:
cv_wait(&sr->sr_st_cv, &sr->sr_mutex);
goto switch_state;
case SMB_REQ_STATE_CANCELLED:
if (status == NT_STATUS_SUCCESS)
status = NT_STATUS_CANCELLED;
break;
default:
break;
}
sr->cancel_method = NULL;
sr->cancel_arg2 = NULL;
mutex_exit(&sr->sr_mutex);
smb_llist_enter(&node->n_lock_list, RW_WRITER);
smb_llist_enter(&node->n_wlock_list, RW_WRITER);
smb_llist_remove(&node->n_wlock_list, lock);
smb_llist_exit(&node->n_wlock_list);
return (status);
}
static uint32_t
smb_lock_range_ulckrules(
smb_ofile_t *file,
uint64_t start,
uint64_t length,
uint32_t pid,
smb_lock_t **foundlock)
{
smb_node_t *node = file->f_node;
smb_lock_t *lock;
uint32_t status = NT_STATUS_RANGE_NOT_LOCKED;
for (lock = smb_llist_head(&node->n_lock_list);
lock != NULL;
lock = smb_llist_next(&node->n_lock_list, lock)) {
if ((start == lock->l_start) &&
(length == lock->l_length) &&
lock->l_file == file &&
lock->l_pid == pid) {
*foundlock = lock;
status = NT_STATUS_SUCCESS;
break;
}
}
return (status);
}
static smb_lock_t *
smb_lock_create(
smb_request_t *sr,
uint64_t start,
uint64_t length,
uint32_t pid,
uint32_t locktype,
uint32_t timeout)
{
smb_lock_t *lock;
ASSERT(locktype == SMB_LOCK_TYPE_READWRITE ||
locktype == SMB_LOCK_TYPE_READONLY);
lock = kmem_cache_alloc(smb_cache_lock, KM_SLEEP);
bzero(lock, sizeof (*lock));
lock->l_magic = SMB_LOCK_MAGIC;
lock->l_file = sr->fid_ofile;
lock->l_pid = pid;
lock->l_type = locktype;
lock->l_start = start;
lock->l_length = length;
lock->l_end_time = ddi_get_lbolt() + MSEC_TO_TICK(timeout);
if (timeout == UINT_MAX)
lock->l_flags |= SMB_LOCK_FLAG_INDEFINITE;
mutex_init(&lock->l_mutex, NULL, MUTEX_DEFAULT, NULL);
cv_init(&lock->l_cv, NULL, CV_DEFAULT, NULL);
return (lock);
}
static void
smb_lock_free(smb_lock_t *lock)
{
lock->l_magic = 0;
cv_destroy(&lock->l_cv);
mutex_destroy(&lock->l_mutex);
kmem_cache_free(smb_cache_lock, lock);
}
static void
smb_lock_destroy(smb_lock_t *lock)
{
smb_lock_t *tl;
smb_node_t *node;
uint32_t ccnt;
mutex_enter(&lock->l_mutex);
ccnt = lock->l_conflicts;
lock->l_conflicts = 0;
mutex_exit(&lock->l_mutex);
node = lock->l_file->f_node;
while (ccnt) {
smb_llist_enter(&node->n_wlock_list, RW_READER);
for (tl = smb_llist_head(&node->n_wlock_list);
tl != NULL;
tl = smb_llist_next(&node->n_wlock_list, tl)) {
mutex_enter(&tl->l_mutex);
if (tl->l_blocked_by == lock) {
tl->l_blocked_by = NULL;
cv_broadcast(&tl->l_cv);
mutex_exit(&tl->l_mutex);
goto woke_one;
}
mutex_exit(&tl->l_mutex);
}
ccnt = 0;
woke_one:
smb_llist_exit(&node->n_wlock_list);
if (ccnt) {
delay(MSEC_TO_TICK(1));
}
}
smb_lock_free(lock);
}
static boolean_t
smb_is_range_unlocked(uint64_t start, uint64_t end, uint32_t uniqid,
smb_llist_t *llist_head, uint64_t *new_mark)
{
struct smb_lock *lk = NULL;
uint64_t low_water_mark = MAXOFFSET_T;
uint64_t lk_start;
uint64_t lk_end;
*new_mark = 0;
lk = smb_llist_head(llist_head);
while (lk) {
if (lk->l_length == 0) {
lk = smb_llist_next(llist_head, lk);
continue;
}
if (lk->l_file->f_uniqid != uniqid) {
lk = smb_llist_next(llist_head, lk);
continue;
}
lk_end = lk->l_start + lk->l_length - 1;
lk_start = lk->l_start;
if (lk_end < start) {
lk = smb_llist_next(llist_head, lk);
continue;
}
if (lk_start > end) {
lk = smb_llist_next(llist_head, lk);
continue;
}
if ((lk_start <= start) && (lk_end >= end)) {
return (B_FALSE);
}
if ((start >= lk_start) && (start <= lk_end)) {
if (end > lk_end)
*new_mark = lk_end + 1;
return (B_FALSE);
}
if ((lk_start >= start) && (lk_start <= end)) {
if (low_water_mark > lk_start)
low_water_mark = lk_start;
}
lk = smb_llist_next(llist_head, lk);
}
if (low_water_mark != MAXOFFSET_T) {
*new_mark = low_water_mark;
return (B_TRUE);
}
return (B_TRUE);
}
#ifdef DEBUG
static void
smb_lock_dump1(smb_lock_t *lock)
{
cmn_err(CE_CONT, "\t0x%p: 0x%llx, 0x%llx, %p, %d\n",
(void *)lock,
(long long)lock->l_start,
(long long)lock->l_length,
(void *)lock->l_file,
lock->l_pid);
}
static void
smb_lock_dumplist(smb_llist_t *llist)
{
smb_lock_t *lock;
for (lock = smb_llist_head(llist);
lock != NULL;
lock = smb_llist_next(llist, lock)) {
smb_lock_dump1(lock);
}
}
static void
smb_lock_dumpnode(smb_node_t *node)
{
cmn_err(CE_CONT, "Granted Locks on %p (%d)\n",
(void *)node, node->n_lock_list.ll_count);
smb_lock_dumplist(&node->n_lock_list);
cmn_err(CE_CONT, "Waiting Locks on %p (%d)\n",
(void *)node, node->n_wlock_list.ll_count);
smb_lock_dumplist(&node->n_wlock_list);
}
#endif