#include <sys/param.h>
#include <sys/systm.h>
#include <sys/thread.h>
#include <sys/fcntl.h>
#include <sys/flock.h>
#include <sys/mount.h>
#include <sys/priv.h>
#include <sys/proc.h>
#include <sys/share.h>
#include <sys/socket.h>
#include <sys/syscall.h>
#include <sys/syslog.h>
#include <sys/systm.h>
#include <sys/class.h>
#include <sys/unistd.h>
#include <sys/vnode.h>
#include <sys/vfs.h>
#include <sys/queue.h>
#include <sys/bitmap.h>
#include <sys/sdt.h>
#include <netinet/in.h>
#include <rpc/rpc.h>
#include <rpc/xdr.h>
#include <rpc/pmap_prot.h>
#include <rpc/pmap_clnt.h>
#include <rpc/rpcb_prot.h>
#include <rpcsvc/nlm_prot.h>
#include <rpcsvc/sm_inter.h>
#include <rpcsvc/nsm_addr.h>
#include <nfs/nfs.h>
#include <nfs/nfs_clnt.h>
#include <nfs/export.h>
#include <nfs/rnode.h>
#include <nfs/lm.h>
#include "nlm_impl.h"
struct nlm_knc {
struct knetconfig n_knc;
const char *n_netid;
};
#define NLM_NSM_RPCBIND_RETRIES 10
#define NLM_NSM_RPCBIND_TIMEOUT 5
#define NLM_BMAP_NITEMS (LM_SYSID_MAX + 1)
#define NLM_BMAP_WORDS (NLM_BMAP_NITEMS / BT_NBIPUL)
#define SIGN(x) (((x) > 0) - ((x) < 0))
#define ARRSIZE(arr) (sizeof (arr) / sizeof ((arr)[0]))
#define NLM_KNCS ARRSIZE(nlm_netconfigs)
krwlock_t lm_lck;
static const struct timeval nlm_rpctv_zero = { 0, 0 };
static struct nlm_globals_list nlm_zones_list;
static struct kmem_cache *nlm_hosts_cache = NULL;
static struct kmem_cache *nlm_vhold_cache = NULL;
static ulong_t nlm_sysid_bmap[NLM_BMAP_WORDS];
static int nlm_sysid_nidx;
static SVC_CALLOUT nlm_svcs[] = {
{ NLM_PROG, 4, 4, nlm_prog_4 },
{ NLM_PROG, 1, 3, nlm_prog_3 }
};
static SVC_CALLOUT_TABLE nlm_sct = {
ARRSIZE(nlm_svcs),
FALSE,
nlm_svcs
};
static struct nlm_knc nlm_netconfigs[] = {
{
{ NC_TPI_CLTS, NC_INET, NC_UDP, NODEV },
"udp",
},
{
{ NC_TPI_COTS_ORD, NC_INET, NC_TCP, NODEV },
"tcp",
},
{
{ NC_TPI_CLTS, NC_INET6, NC_UDP, NODEV },
"udp6",
},
{
{ NC_TPI_COTS_ORD, NC_INET6, NC_TCP, NODEV },
"tcp6",
},
{
{ NC_TPI_CLTS, NC_LOOPBACK, NC_NOPROTO, NODEV },
"ticlts",
},
{
{ NC_TPI_COTS_ORD, NC_LOOPBACK, NC_NOPROTO, NODEV },
"ticotsord",
},
};
static void nlm_copy_netbuf(struct netbuf *, struct netbuf *);
static int nlm_netbuf_addrs_cmp(struct netbuf *, struct netbuf *);
static void nlm_kmem_reclaim(void *);
static void nlm_pool_shutdown(void);
static void nlm_suspend_zone(struct nlm_globals *);
static void nlm_resume_zone(struct nlm_globals *);
static void nlm_nsm_clnt_init(CLIENT *, struct nlm_nsm *);
static void nlm_netbuf_to_netobj(struct netbuf *, int *, netobj *);
static void nlm_gc(struct nlm_globals *);
static void nlm_reclaimer(struct nlm_host *);
static int nlm_init_local_knc(struct knetconfig *);
static int nlm_nsm_init_local(struct nlm_nsm *);
static int nlm_nsm_init(struct nlm_nsm *, struct knetconfig *, struct netbuf *);
static void nlm_nsm_fini(struct nlm_nsm *);
static enum clnt_stat nlm_nsm_simu_crash(struct nlm_nsm *);
static enum clnt_stat nlm_nsm_stat(struct nlm_nsm *, int32_t *);
static enum clnt_stat nlm_nsm_mon(struct nlm_nsm *, char *, uint16_t);
static enum clnt_stat nlm_nsm_unmon(struct nlm_nsm *, char *);
static int nlm_host_ctor(void *, void *, int);
static void nlm_host_dtor(void *, void *);
static void nlm_host_destroy(struct nlm_host *);
static struct nlm_host *nlm_host_create(char *, const char *,
struct knetconfig *, struct netbuf *, struct netbuf *);
static struct nlm_host *nlm_host_find_locked(struct nlm_globals *,
const char *, struct netbuf *, avl_index_t *);
static void nlm_host_unregister(struct nlm_globals *, struct nlm_host *);
static void nlm_host_gc_vholds(struct nlm_host *);
static bool_t nlm_host_has_srv_locks(struct nlm_host *);
static bool_t nlm_host_has_cli_locks(struct nlm_host *);
static bool_t nlm_host_has_locks(struct nlm_host *);
static int nlm_vhold_ctor(void *, void *, int);
static void nlm_vhold_dtor(void *, void *);
static void nlm_vhold_destroy(struct nlm_host *,
struct nlm_vhold *);
static bool_t nlm_vhold_busy(struct nlm_host *, struct nlm_vhold *);
static void nlm_vhold_clean(struct nlm_vhold *, int);
struct nlm_slreq *nlm_slreq_find_locked(struct nlm_host *,
struct nlm_vhold *, struct flock64 *);
static struct nlm_shres *nlm_shres_create_item(struct shrlock *, vnode_t *);
static void nlm_shres_destroy_item(struct nlm_shres *);
static bool_t nlm_shres_equal(struct shrlock *, struct shrlock *);
void
nlm_init(void)
{
nlm_hosts_cache = kmem_cache_create("nlm_host_cache",
sizeof (struct nlm_host), 0, nlm_host_ctor, nlm_host_dtor,
nlm_kmem_reclaim, NULL, NULL, 0);
nlm_vhold_cache = kmem_cache_create("nlm_vhold_cache",
sizeof (struct nlm_vhold), 0, nlm_vhold_ctor, nlm_vhold_dtor,
NULL, NULL, NULL, 0);
nlm_rpc_init();
TAILQ_INIT(&nlm_zones_list);
bzero(nlm_sysid_bmap, sizeof (nlm_sysid_bmap));
nlm_sysid_nidx = 1;
BT_SET(nlm_sysid_bmap, 0);
}
void
nlm_globals_register(struct nlm_globals *g)
{
rw_enter(&lm_lck, RW_WRITER);
TAILQ_INSERT_TAIL(&nlm_zones_list, g, nlm_link);
rw_exit(&lm_lck);
}
void
nlm_globals_unregister(struct nlm_globals *g)
{
rw_enter(&lm_lck, RW_WRITER);
TAILQ_REMOVE(&nlm_zones_list, g, nlm_link);
rw_exit(&lm_lck);
}
static void
nlm_kmem_reclaim(void *cdrarg)
{
struct nlm_globals *g;
rw_enter(&lm_lck, RW_READER);
TAILQ_FOREACH(g, &nlm_zones_list, nlm_link)
cv_broadcast(&g->nlm_gc_sched_cv);
rw_exit(&lm_lck);
}
static void
nlm_gc(struct nlm_globals *g)
{
struct nlm_host *hostp;
clock_t now, idle_period;
idle_period = SEC_TO_TICK(g->cn_idle_tmo);
mutex_enter(&g->lock);
for (;;) {
(void) cv_timedwait(&g->nlm_gc_sched_cv, &g->lock,
ddi_get_lbolt() + idle_period);
if (g->run_status == NLM_ST_STOPPING)
break;
now = ddi_get_lbolt();
DTRACE_PROBE2(gc__start, struct nlm_globals *, g,
clock_t, now);
for (hostp = avl_first(&g->nlm_hosts_tree); hostp != NULL;
hostp = AVL_NEXT(&g->nlm_hosts_tree, hostp)) {
struct nlm_vhold *nvp;
mutex_enter(&hostp->nh_lock);
nvp = TAILQ_FIRST(&hostp->nh_vholds_list);
while (nvp != NULL) {
struct nlm_vhold *new_nvp;
new_nvp = TAILQ_NEXT(nvp, nv_link);
if (nvp->nv_refcnt == 0 &&
nvp->nv_vp->v_filocks == NULL &&
nvp->nv_vp->v_shrlocks == NULL) {
nlm_vhold_destroy(hostp, nvp);
}
nvp = new_nvp;
}
mutex_exit(&hostp->nh_lock);
}
while ((hostp = TAILQ_FIRST(&g->nlm_idle_hosts)) != NULL) {
bool_t has_locks;
if (hostp->nh_idle_timeout > now)
break;
mutex_exit(&g->lock);
mutex_enter(&hostp->nh_lock);
nlm_host_gc_vholds(hostp);
has_locks = nlm_host_has_locks(hostp);
mutex_exit(&hostp->nh_lock);
mutex_enter(&g->lock);
if ((hostp->nh_flags & NLM_NH_INIDLE) == 0 ||
hostp->nh_idle_timeout > now)
continue;
if (has_locks) {
TAILQ_REMOVE(&g->nlm_idle_hosts,
hostp, nh_link);
hostp->nh_idle_timeout = now + idle_period;
TAILQ_INSERT_TAIL(&g->nlm_idle_hosts,
hostp, nh_link);
continue;
}
nlm_host_unregister(g, hostp);
mutex_exit(&g->lock);
nlm_host_unmonitor(g, hostp);
nlm_host_destroy(hostp);
mutex_enter(&g->lock);
if (g->run_status == NLM_ST_STOPPING)
break;
}
DTRACE_PROBE(gc__end);
}
DTRACE_PROBE1(gc__exit, struct nlm_globals *, g);
g->nlm_gc_thread = NULL;
mutex_exit(&g->lock);
cv_broadcast(&g->nlm_gc_finish_cv);
zthread_exit();
}
static void
nlm_reclaimer(struct nlm_host *hostp)
{
struct nlm_globals *g;
mutex_enter(&hostp->nh_lock);
hostp->nh_reclaimer = curthread;
mutex_exit(&hostp->nh_lock);
g = zone_getspecific(nlm_zone_key, curzone);
nlm_reclaim_client(g, hostp);
mutex_enter(&hostp->nh_lock);
hostp->nh_flags &= ~NLM_NH_RECLAIM;
hostp->nh_reclaimer = NULL;
cv_broadcast(&hostp->nh_recl_cv);
mutex_exit(&hostp->nh_lock);
nlm_host_release(g, hostp);
zthread_exit();
}
void
nlm_copy_netobj(struct netobj *dst, struct netobj *src)
{
dst->n_len = src->n_len;
dst->n_bytes = kmem_alloc(src->n_len, KM_SLEEP);
bcopy(src->n_bytes, dst->n_bytes, src->n_len);
}
enum clnt_stat
nlm_clnt_call(CLIENT *clnt, rpcproc_t procnum, xdrproc_t xdr_args,
caddr_t argsp, xdrproc_t xdr_result, caddr_t resultp, struct timeval wait)
{
k_sigset_t oldmask;
enum clnt_stat stat;
bool_t sig_blocked = FALSE;
if (procnum >= NLM_TEST_RES && procnum <= NLM_GRANTED_RES)
wait = nlm_rpctv_zero;
if (procnum == NLM_CANCEL) {
k_sigset_t newmask;
sigfillset(&newmask);
sigreplace(&newmask, &oldmask);
sig_blocked = TRUE;
}
stat = clnt_call(clnt, procnum, xdr_args,
argsp, xdr_result, resultp, wait);
if (sig_blocked)
sigreplace(&oldmask, (k_sigset_t *)NULL);
return (stat);
}
static void
nlm_suspend_zone(struct nlm_globals *g)
{
struct nlm_host *hostp;
struct nlm_host_list all_hosts;
TAILQ_INIT(&all_hosts);
mutex_enter(&g->lock);
for (hostp = avl_first(&g->nlm_hosts_tree); hostp != NULL;
hostp = AVL_NEXT(&g->nlm_hosts_tree, hostp)) {
if (hostp->nh_flags & NLM_NH_INIDLE) {
TAILQ_REMOVE(&g->nlm_idle_hosts, hostp, nh_link);
hostp->nh_flags &= ~NLM_NH_INIDLE;
}
hostp->nh_refs++;
TAILQ_INSERT_TAIL(&all_hosts, hostp, nh_link);
}
mutex_exit(&g->lock);
while ((hostp = TAILQ_FIRST(&all_hosts)) != NULL) {
mutex_enter(&hostp->nh_lock);
if (nlm_host_has_locks(hostp))
hostp->nh_flags |= NLM_NH_SUSPEND;
mutex_exit(&hostp->nh_lock);
TAILQ_REMOVE(&all_hosts, hostp, nh_link);
}
}
static void
nlm_resume_zone(struct nlm_globals *g)
{
struct nlm_host *hostp, *h_next;
mutex_enter(&g->lock);
hostp = avl_first(&g->nlm_hosts_tree);
while (hostp != NULL) {
struct nlm_nsm nsm;
enum clnt_stat stat;
int32_t sm_state;
int error;
bool_t resume_failed = FALSE;
h_next = AVL_NEXT(&g->nlm_hosts_tree, hostp);
mutex_exit(&g->lock);
DTRACE_PROBE1(resume__host, struct nlm_host *, hostp);
if (!(hostp->nh_flags & NLM_NH_SUSPEND))
goto cycle_end;
error = nlm_nsm_init(&nsm, &hostp->nh_knc, &hostp->nh_addr);
if (error != 0) {
NLM_ERR("Resume: Failed to contact to NSM of host %s "
"[error=%d]\n", hostp->nh_name, error);
resume_failed = TRUE;
goto cycle_end;
}
stat = nlm_nsm_stat(&nsm, &sm_state);
if (stat != RPC_SUCCESS) {
NLM_ERR("Resume: Failed to call SM_STAT operation for "
"host %s [stat=%d]\n", hostp->nh_name, stat);
resume_failed = TRUE;
nlm_nsm_fini(&nsm);
goto cycle_end;
}
if (sm_state != hostp->nh_state) {
nlm_host_notify_client(hostp, sm_state);
nlm_host_notify_server(hostp, sm_state);
}
nlm_nsm_fini(&nsm);
cycle_end:
if (resume_failed) {
nlm_host_notify_server(hostp, 0);
nlm_client_cancel_all(g, hostp);
}
hostp->nh_flags &= ~NLM_NH_SUSPEND;
nlm_host_release(g, hostp);
hostp = h_next;
mutex_enter(&g->lock);
}
mutex_exit(&g->lock);
}
static int
nlm_init_local_knc(struct knetconfig *knc)
{
int error;
vnode_t *vp;
bzero(knc, sizeof (*knc));
error = lookupname("/dev/tcp", UIO_SYSSPACE,
FOLLOW, NULLVPP, &vp);
if (error != 0)
return (error);
knc->knc_semantics = NC_TPI_COTS;
knc->knc_protofmly = NC_INET;
knc->knc_proto = NC_TCP;
knc->knc_rdev = vp->v_rdev;
VN_RELE(vp);
return (0);
}
static int
nlm_nsm_init_local(struct nlm_nsm *nsm)
{
int error;
struct knetconfig knc;
struct sockaddr_in sin;
struct netbuf nb;
error = nlm_init_local_knc(&knc);
if (error != 0)
return (error);
bzero(&sin, sizeof (sin));
sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
sin.sin_family = AF_INET;
nb.buf = (char *)&sin;
nb.len = nb.maxlen = sizeof (sin);
return (nlm_nsm_init(nsm, &knc, &nb));
}
static int
nlm_nsm_init(struct nlm_nsm *nsm, struct knetconfig *knc, struct netbuf *nb)
{
enum clnt_stat stat;
int error, retries;
bzero(nsm, sizeof (*nsm));
nsm->ns_knc = *knc;
nlm_copy_netbuf(&nsm->ns_addr, nb);
for (retries = 0; retries < NLM_NSM_RPCBIND_RETRIES; retries++) {
stat = rpcbind_getaddr(&nsm->ns_knc, SM_PROG,
SM_VERS, &nsm->ns_addr);
if (stat != RPC_SUCCESS) {
if (stat == RPC_PROGNOTREGISTERED) {
delay(SEC_TO_TICK(NLM_NSM_RPCBIND_TIMEOUT));
continue;
}
}
break;
}
if (stat != RPC_SUCCESS) {
DTRACE_PROBE2(rpcbind__error, enum clnt_stat, stat,
int, retries);
error = ENOENT;
goto error;
}
error = clnt_tli_kcreate(&nsm->ns_knc, &nsm->ns_addr, SM_PROG, SM_VERS,
0, NLM_RPC_RETRIES, zone_kcred(), &nsm->ns_handle);
if (error != 0)
goto error;
error = clnt_tli_kcreate(&nsm->ns_knc, &nsm->ns_addr, NSM_ADDR_PROGRAM,
NSM_ADDR_V1, 0, NLM_RPC_RETRIES, zone_kcred(),
&nsm->ns_addr_handle);
if (error != 0)
goto error;
sema_init(&nsm->ns_sem, 1, NULL, SEMA_DEFAULT, NULL);
return (0);
error:
kmem_free(nsm->ns_addr.buf, nsm->ns_addr.maxlen);
if (nsm->ns_handle) {
ASSERT(nsm->ns_handle->cl_auth != NULL);
auth_destroy(nsm->ns_handle->cl_auth);
CLNT_DESTROY(nsm->ns_handle);
}
return (error);
}
static void
nlm_nsm_fini(struct nlm_nsm *nsm)
{
kmem_free(nsm->ns_addr.buf, nsm->ns_addr.maxlen);
if (nsm->ns_addr_handle->cl_auth != NULL)
auth_destroy(nsm->ns_addr_handle->cl_auth);
CLNT_DESTROY(nsm->ns_addr_handle);
nsm->ns_addr_handle = NULL;
if (nsm->ns_handle->cl_auth != NULL)
auth_destroy(nsm->ns_handle->cl_auth);
CLNT_DESTROY(nsm->ns_handle);
nsm->ns_handle = NULL;
sema_destroy(&nsm->ns_sem);
}
static enum clnt_stat
nlm_nsm_simu_crash(struct nlm_nsm *nsm)
{
enum clnt_stat stat;
sema_p(&nsm->ns_sem);
nlm_nsm_clnt_init(nsm->ns_handle, nsm);
stat = sm_simu_crash_1(NULL, NULL, nsm->ns_handle);
sema_v(&nsm->ns_sem);
return (stat);
}
static enum clnt_stat
nlm_nsm_stat(struct nlm_nsm *nsm, int32_t *out_stat)
{
struct sm_name args;
struct sm_stat_res res;
enum clnt_stat stat;
args.mon_name = uts_nodename();
bzero(&res, sizeof (res));
sema_p(&nsm->ns_sem);
nlm_nsm_clnt_init(nsm->ns_handle, nsm);
stat = sm_stat_1(&args, &res, nsm->ns_handle);
sema_v(&nsm->ns_sem);
if (stat == RPC_SUCCESS)
*out_stat = res.state;
return (stat);
}
static enum clnt_stat
nlm_nsm_mon(struct nlm_nsm *nsm, char *hostname, uint16_t priv)
{
struct mon args;
struct sm_stat_res res;
enum clnt_stat stat;
bzero(&args, sizeof (args));
bzero(&res, sizeof (res));
args.mon_id.mon_name = hostname;
args.mon_id.my_id.my_name = uts_nodename();
args.mon_id.my_id.my_prog = NLM_PROG;
args.mon_id.my_id.my_vers = NLM_SM;
args.mon_id.my_id.my_proc = NLM_SM_NOTIFY1;
bcopy(&priv, args.priv, sizeof (priv));
sema_p(&nsm->ns_sem);
nlm_nsm_clnt_init(nsm->ns_handle, nsm);
stat = sm_mon_1(&args, &res, nsm->ns_handle);
sema_v(&nsm->ns_sem);
return (stat);
}
static enum clnt_stat
nlm_nsm_unmon(struct nlm_nsm *nsm, char *hostname)
{
struct mon_id args;
struct sm_stat res;
enum clnt_stat stat;
bzero(&args, sizeof (args));
bzero(&res, sizeof (res));
args.mon_name = hostname;
args.my_id.my_name = uts_nodename();
args.my_id.my_prog = NLM_PROG;
args.my_id.my_vers = NLM_SM;
args.my_id.my_proc = NLM_SM_NOTIFY1;
sema_p(&nsm->ns_sem);
nlm_nsm_clnt_init(nsm->ns_handle, nsm);
stat = sm_unmon_1(&args, &res, nsm->ns_handle);
sema_v(&nsm->ns_sem);
return (stat);
}
static enum clnt_stat
nlm_nsmaddr_reg(struct nlm_nsm *nsm, char *name, int family, netobj *address)
{
struct reg1args args = { 0 };
struct reg1res res = { 0 };
enum clnt_stat stat;
args.family = family;
args.name = name;
args.address = *address;
sema_p(&nsm->ns_sem);
nlm_nsm_clnt_init(nsm->ns_addr_handle, nsm);
stat = nsmaddrproc1_reg_1(&args, &res, nsm->ns_addr_handle);
sema_v(&nsm->ns_sem);
return (stat);
}
struct nlm_vhold *
nlm_vhold_get(struct nlm_host *hostp, vnode_t *vp)
{
struct nlm_vhold *nvp, *new_nvp = NULL;
mutex_enter(&hostp->nh_lock);
nvp = nlm_vhold_find_locked(hostp, vp);
if (nvp != NULL)
goto out;
mutex_exit(&hostp->nh_lock);
new_nvp = kmem_cache_alloc(nlm_vhold_cache, KM_SLEEP);
mutex_enter(&hostp->nh_lock);
nvp = nlm_vhold_find_locked(hostp, vp);
if (nvp == NULL) {
nvp = new_nvp;
new_nvp = NULL;
TAILQ_INIT(&nvp->nv_slreqs);
nvp->nv_vp = vp;
nvp->nv_refcnt = 1;
VN_HOLD(nvp->nv_vp);
VERIFY(mod_hash_insert(hostp->nh_vholds_by_vp,
(mod_hash_key_t)vp, (mod_hash_val_t)nvp) == 0);
TAILQ_INSERT_TAIL(&hostp->nh_vholds_list, nvp, nv_link);
}
out:
mutex_exit(&hostp->nh_lock);
if (new_nvp != NULL)
kmem_cache_free(nlm_vhold_cache, new_nvp);
return (nvp);
}
void
nlm_vhold_release(struct nlm_host *hostp, struct nlm_vhold *nvp)
{
if (nvp == NULL)
return;
mutex_enter(&hostp->nh_lock);
ASSERT(nvp->nv_refcnt > 0);
nvp->nv_refcnt--;
if (nvp->nv_refcnt == 0 &&
nvp->nv_vp->v_filocks == NULL &&
nvp->nv_vp->v_shrlocks == NULL) {
nlm_vhold_destroy(hostp, nvp);
}
mutex_exit(&hostp->nh_lock);
}
static void
nlm_vhold_clean(struct nlm_vhold *nvp, int sysid)
{
cleanlocks(nvp->nv_vp, IGN_PID, sysid);
cleanshares_by_sysid(nvp->nv_vp, sysid);
}
static void
nlm_vhold_destroy(struct nlm_host *hostp, struct nlm_vhold *nvp)
{
ASSERT(MUTEX_HELD(&hostp->nh_lock));
ASSERT(nvp->nv_refcnt == 0);
ASSERT(TAILQ_EMPTY(&nvp->nv_slreqs));
VERIFY(mod_hash_remove(hostp->nh_vholds_by_vp,
(mod_hash_key_t)nvp->nv_vp,
(mod_hash_val_t)&nvp) == 0);
TAILQ_REMOVE(&hostp->nh_vholds_list, nvp, nv_link);
VN_RELE(nvp->nv_vp);
nvp->nv_vp = NULL;
kmem_cache_free(nlm_vhold_cache, nvp);
}
static bool_t
nlm_vhold_busy(struct nlm_host *hostp, struct nlm_vhold *nvp)
{
vnode_t *vp;
int sysid;
ASSERT(MUTEX_HELD(&hostp->nh_lock));
if (nvp->nv_refcnt > 0)
return (TRUE);
vp = nvp->nv_vp;
sysid = hostp->nh_sysid;
if (flk_has_remote_locks_for_sysid(vp, sysid) ||
shr_has_remote_shares(vp, sysid))
return (TRUE);
return (FALSE);
}
static int
nlm_vhold_ctor(void *datap, void *cdrarg, int kmflags)
{
struct nlm_vhold *nvp = (struct nlm_vhold *)datap;
bzero(nvp, sizeof (*nvp));
return (0);
}
static void
nlm_vhold_dtor(void *datap, void *cdrarg)
{
struct nlm_vhold *nvp = (struct nlm_vhold *)datap;
ASSERT(nvp->nv_refcnt == 0);
ASSERT(TAILQ_EMPTY(&nvp->nv_slreqs));
ASSERT(nvp->nv_vp == NULL);
}
struct nlm_vhold *
nlm_vhold_find_locked(struct nlm_host *hostp, const vnode_t *vp)
{
struct nlm_vhold *nvp = NULL;
ASSERT(MUTEX_HELD(&hostp->nh_lock));
(void) mod_hash_find(hostp->nh_vholds_by_vp,
(mod_hash_key_t)vp,
(mod_hash_val_t)&nvp);
if (nvp != NULL)
nvp->nv_refcnt++;
return (nvp);
}
static void
nlm_copy_netbuf(struct netbuf *dst, struct netbuf *src)
{
ASSERT(src->len <= src->maxlen);
dst->maxlen = src->maxlen;
dst->len = src->len;
dst->buf = kmem_zalloc(src->maxlen, KM_SLEEP);
bcopy(src->buf, dst->buf, src->len);
}
static int
nlm_host_ctor(void *datap, void *cdrarg, int kmflags)
{
struct nlm_host *hostp = (struct nlm_host *)datap;
bzero(hostp, sizeof (*hostp));
return (0);
}
static void
nlm_host_dtor(void *datap, void *cdrarg)
{
struct nlm_host *hostp = (struct nlm_host *)datap;
ASSERT(hostp->nh_refs == 0);
}
static void
nlm_host_unregister(struct nlm_globals *g, struct nlm_host *hostp)
{
ASSERT(hostp->nh_refs == 0);
ASSERT(hostp->nh_flags & NLM_NH_INIDLE);
avl_remove(&g->nlm_hosts_tree, hostp);
VERIFY(mod_hash_remove(g->nlm_hosts_hash,
(mod_hash_key_t)(uintptr_t)hostp->nh_sysid,
(mod_hash_val_t)&hostp) == 0);
TAILQ_REMOVE(&g->nlm_idle_hosts, hostp, nh_link);
hostp->nh_flags &= ~NLM_NH_INIDLE;
}
static void
nlm_host_destroy(struct nlm_host *hostp)
{
ASSERT(hostp->nh_name != NULL);
ASSERT(hostp->nh_netid != NULL);
ASSERT(TAILQ_EMPTY(&hostp->nh_vholds_list));
strfree(hostp->nh_name);
strfree(hostp->nh_netid);
kmem_free(hostp->nh_addr.buf, hostp->nh_addr.maxlen);
if (hostp->nh_laddr.buf != NULL)
kmem_free(hostp->nh_laddr.buf, hostp->nh_laddr.maxlen);
if (hostp->nh_sysid != LM_NOSYSID)
nlm_sysid_free(hostp->nh_sysid);
nlm_rpc_cache_destroy(hostp);
ASSERT(TAILQ_EMPTY(&hostp->nh_vholds_list));
mod_hash_destroy_ptrhash(hostp->nh_vholds_by_vp);
mutex_destroy(&hostp->nh_lock);
cv_destroy(&hostp->nh_rpcb_cv);
cv_destroy(&hostp->nh_recl_cv);
kmem_cache_free(nlm_hosts_cache, hostp);
}
void
nlm_host_notify_server(struct nlm_host *hostp, int32_t state)
{
struct nlm_vhold *nvp;
struct nlm_slreq *slr;
struct nlm_slreq_list slreqs2free;
TAILQ_INIT(&slreqs2free);
mutex_enter(&hostp->nh_lock);
if (state != 0)
hostp->nh_state = state;
TAILQ_FOREACH(nvp, &hostp->nh_vholds_list, nv_link) {
while ((slr = TAILQ_FIRST(&nvp->nv_slreqs)) != NULL) {
TAILQ_REMOVE(&nvp->nv_slreqs, slr, nsr_link);
TAILQ_INSERT_TAIL(&slreqs2free, slr, nsr_link);
}
nvp->nv_refcnt++;
mutex_exit(&hostp->nh_lock);
nlm_vhold_clean(nvp, hostp->nh_sysid);
mutex_enter(&hostp->nh_lock);
nvp->nv_refcnt--;
}
mutex_exit(&hostp->nh_lock);
while ((slr = TAILQ_FIRST(&slreqs2free)) != NULL) {
TAILQ_REMOVE(&slreqs2free, slr, nsr_link);
kmem_free(slr, sizeof (*slr));
}
}
void
nlm_host_notify_client(struct nlm_host *hostp, int32_t state)
{
mutex_enter(&hostp->nh_lock);
hostp->nh_state = state;
if (hostp->nh_flags & NLM_NH_RECLAIM) {
mutex_exit(&hostp->nh_lock);
return;
}
hostp->nh_flags |= NLM_NH_RECLAIM;
hostp->nh_refs++;
mutex_exit(&hostp->nh_lock);
(void) zthread_create(NULL, 0, nlm_reclaimer,
hostp, 0, minclsyspri);
}
int
nlm_host_wait_grace(struct nlm_host *hostp)
{
struct nlm_globals *g;
int error = 0;
g = zone_getspecific(nlm_zone_key, curzone);
mutex_enter(&hostp->nh_lock);
do {
int rc;
rc = cv_timedwait_sig(&hostp->nh_recl_cv,
&hostp->nh_lock, ddi_get_lbolt() +
SEC_TO_TICK(g->retrans_tmo));
if (rc == 0) {
error = EINTR;
break;
}
} while (hostp->nh_flags & NLM_NH_RECLAIM);
mutex_exit(&hostp->nh_lock);
return (error);
}
static struct nlm_host *
nlm_host_create(char *name, const char *netid,
struct knetconfig *knc, struct netbuf *naddr, struct netbuf *laddr)
{
struct nlm_host *host;
host = kmem_cache_alloc(nlm_hosts_cache, KM_SLEEP);
mutex_init(&host->nh_lock, NULL, MUTEX_DEFAULT, NULL);
cv_init(&host->nh_rpcb_cv, NULL, CV_DEFAULT, NULL);
cv_init(&host->nh_recl_cv, NULL, CV_DEFAULT, NULL);
host->nh_sysid = LM_NOSYSID;
host->nh_refs = 1;
host->nh_name = strdup(name);
host->nh_netid = strdup(netid);
host->nh_knc = *knc;
nlm_copy_netbuf(&host->nh_addr, naddr);
if (laddr != NULL) {
nlm_copy_netbuf(&host->nh_laddr, laddr);
} else {
bzero(&host->nh_laddr, sizeof (host->nh_laddr));
}
host->nh_state = 0;
host->nh_rpcb_state = NRPCB_NEED_UPDATE;
host->nh_flags = 0;
host->nh_vholds_by_vp = mod_hash_create_ptrhash("nlm vholds hash",
32, mod_hash_null_valdtor, sizeof (vnode_t));
TAILQ_INIT(&host->nh_vholds_list);
TAILQ_INIT(&host->nh_rpchc);
return (host);
}
void
nlm_host_cancel_slocks(struct nlm_globals *g, struct nlm_host *hostp)
{
struct nlm_slock *nslp;
mutex_enter(&g->lock);
TAILQ_FOREACH(nslp, &g->nlm_slocks, nsl_link) {
if (nslp->nsl_host == hostp) {
nslp->nsl_state = NLM_SL_CANCELLED;
cv_broadcast(&nslp->nsl_cond);
}
}
mutex_exit(&g->lock);
}
static void
nlm_host_gc_vholds(struct nlm_host *hostp)
{
struct nlm_vhold *nvp;
ASSERT(MUTEX_HELD(&hostp->nh_lock));
nvp = TAILQ_FIRST(&hostp->nh_vholds_list);
while (nvp != NULL) {
struct nlm_vhold *nvp_tmp;
if (nlm_vhold_busy(hostp, nvp)) {
nvp = TAILQ_NEXT(nvp, nv_link);
continue;
}
nvp_tmp = TAILQ_NEXT(nvp, nv_link);
nlm_vhold_destroy(hostp, nvp);
nvp = nvp_tmp;
}
}
static bool_t
nlm_host_has_srv_locks(struct nlm_host *hostp)
{
ASSERT(MUTEX_HELD(&hostp->nh_lock));
if (!TAILQ_EMPTY(&hostp->nh_vholds_list))
return (TRUE);
return (FALSE);
}
static bool_t
nlm_host_has_cli_locks(struct nlm_host *hostp)
{
ASSERT(MUTEX_HELD(&hostp->nh_lock));
if (flk_sysid_has_locks(hostp->nh_sysid |
LM_SYSID_CLIENT, FLK_QUERY_ACTIVE))
return (TRUE);
if (hostp->nh_shrlist != NULL)
return (TRUE);
return (FALSE);
}
static bool_t
nlm_host_has_locks(struct nlm_host *hostp)
{
if (nlm_host_has_srv_locks(hostp))
return (TRUE);
return (nlm_host_has_cli_locks(hostp));
}
static int
nlm_netbuf_addrs_cmp(struct netbuf *nb1, struct netbuf *nb2)
{
union nlm_addr {
struct sockaddr sa;
struct sockaddr_in sin;
struct sockaddr_in6 sin6;
} *na1, *na2;
int res;
na1 = (union nlm_addr *)nb1->buf;
na2 = (union nlm_addr *)nb2->buf;
if (na1->sa.sa_family < na2->sa.sa_family)
return (-1);
if (na1->sa.sa_family > na2->sa.sa_family)
return (1);
switch (na1->sa.sa_family) {
case AF_INET:
res = memcmp(&na1->sin.sin_addr, &na2->sin.sin_addr,
sizeof (na1->sin.sin_addr));
break;
case AF_INET6:
res = memcmp(&na1->sin6.sin6_addr, &na2->sin6.sin6_addr,
sizeof (na1->sin6.sin6_addr));
break;
default:
VERIFY(0);
return (0);
}
return (SIGN(res));
}
int
nlm_host_cmp(const void *p1, const void *p2)
{
struct nlm_host *h1 = (struct nlm_host *)p1;
struct nlm_host *h2 = (struct nlm_host *)p2;
int res;
res = strcmp(h1->nh_netid, h2->nh_netid);
if (res != 0)
return (SIGN(res));
res = nlm_netbuf_addrs_cmp(&h1->nh_addr, &h2->nh_addr);
return (res);
}
static struct nlm_host *
nlm_host_find_locked(struct nlm_globals *g, const char *netid,
struct netbuf *naddr, avl_index_t *wherep)
{
struct nlm_host *hostp, key;
avl_index_t pos;
ASSERT(MUTEX_HELD(&g->lock));
key.nh_netid = (char *)netid;
key.nh_addr.buf = naddr->buf;
key.nh_addr.len = naddr->len;
key.nh_addr.maxlen = naddr->maxlen;
hostp = avl_find(&g->nlm_hosts_tree, &key, &pos);
if (hostp != NULL) {
if (hostp->nh_flags & NLM_NH_INIDLE) {
TAILQ_REMOVE(&g->nlm_idle_hosts, hostp, nh_link);
hostp->nh_flags &= ~NLM_NH_INIDLE;
}
hostp->nh_refs++;
}
if (wherep != NULL)
*wherep = pos;
return (hostp);
}
struct nlm_host *
nlm_host_find(struct nlm_globals *g, const char *netid,
struct netbuf *addr)
{
struct nlm_host *hostp = NULL;
mutex_enter(&g->lock);
if (g->run_status != NLM_ST_UP)
goto out;
hostp = nlm_host_find_locked(g, netid, addr, NULL);
out:
mutex_exit(&g->lock);
return (hostp);
}
struct nlm_host *
nlm_host_findcreate(struct nlm_globals *g, char *name,
const char *netid, struct netbuf *addr, struct netbuf *laddr)
{
int err;
struct nlm_host *host, *newhost = NULL;
struct knetconfig knc;
avl_index_t where;
mutex_enter(&g->lock);
if (g->run_status != NLM_ST_UP) {
mutex_exit(&g->lock);
return (NULL);
}
host = nlm_host_find_locked(g, netid, addr, NULL);
mutex_exit(&g->lock);
if (host != NULL) {
if ((&host->nh_laddr)->len != 0 &&
(laddr == NULL || laddr->len == 0)) {
cmn_err(CE_NOTE, "nlm_host_findcreate: "
"Incoming laddr is absent but "
"host has a recorded nh_laddr.\n");
} else if ((&host->nh_laddr)->len == 0 &&
laddr != NULL && laddr->len != 0) {
cmn_err(CE_NOTE, "nlm_host_findcreate: "
"Incoming laddr is present but "
"host has no recorded nh_laddr.\n");
} else if (laddr != NULL &&
(((&host->nh_laddr)->len != laddr->len) ||
bcmp((&host->nh_laddr)->buf, laddr->buf,
(size_t)laddr->len) != 0)) {
cmn_err(CE_NOTE, "nlm_host_findcreate: received "
"laddr different from recorded nh_laddr.\n");
}
return (host);
}
err = nlm_knc_from_netid(netid, &knc);
if (err != 0)
return (NULL);
newhost = nlm_host_create(name, netid, &knc, addr, laddr);
newhost->nh_sysid = nlm_sysid_alloc();
if (newhost->nh_sysid == LM_NOSYSID)
goto out;
mutex_enter(&g->lock);
host = nlm_host_find_locked(g, netid, addr, &where);
if (host == NULL) {
host = newhost;
newhost = NULL;
avl_insert(&g->nlm_hosts_tree, host, where);
VERIFY(mod_hash_insert(g->nlm_hosts_hash,
(mod_hash_key_t)(uintptr_t)host->nh_sysid,
(mod_hash_val_t)host) == 0);
}
mutex_exit(&g->lock);
out:
if (newhost != NULL) {
newhost->nh_refs--;
nlm_host_destroy(newhost);
}
return (host);
}
struct nlm_host *
nlm_host_find_by_sysid(struct nlm_globals *g, sysid_t sysid)
{
struct nlm_host *hostp = NULL;
mutex_enter(&g->lock);
if (g->run_status != NLM_ST_UP)
goto out;
(void) mod_hash_find(g->nlm_hosts_hash,
(mod_hash_key_t)(uintptr_t)sysid,
(mod_hash_val_t)&hostp);
if (hostp == NULL)
goto out;
if (hostp->nh_flags & NLM_NH_INIDLE) {
TAILQ_REMOVE(&g->nlm_idle_hosts, hostp, nh_link);
hostp->nh_flags &= ~NLM_NH_INIDLE;
}
hostp->nh_refs++;
out:
mutex_exit(&g->lock);
return (hostp);
}
static void
nlm_host_release_locked(struct nlm_globals *g, struct nlm_host *hostp)
{
if (hostp == NULL)
return;
ASSERT(MUTEX_HELD(&g->lock));
ASSERT(hostp->nh_refs > 0);
hostp->nh_refs--;
if (hostp->nh_refs != 0)
return;
hostp->nh_idle_timeout = ddi_get_lbolt() +
SEC_TO_TICK(g->cn_idle_tmo);
ASSERT((hostp->nh_flags & NLM_NH_INIDLE) == 0);
TAILQ_INSERT_TAIL(&g->nlm_idle_hosts, hostp, nh_link);
hostp->nh_flags |= NLM_NH_INIDLE;
}
void
nlm_host_release(struct nlm_globals *g, struct nlm_host *hostp)
{
if (hostp == NULL)
return;
mutex_enter(&g->lock);
nlm_host_release_locked(g, hostp);
mutex_exit(&g->lock);
}
void
nlm_host_unmonitor(struct nlm_globals *g, struct nlm_host *host)
{
enum clnt_stat stat;
VERIFY(host->nh_refs == 0);
if (!(host->nh_flags & NLM_NH_MONITORED))
return;
host->nh_flags &= ~NLM_NH_MONITORED;
stat = nlm_nsm_unmon(&g->nlm_nsm, host->nh_name);
if (stat != RPC_SUCCESS) {
NLM_WARN("NLM: Failed to contact statd, stat=%d\n", stat);
return;
}
}
void
nlm_host_monitor(struct nlm_globals *g, struct nlm_host *host, int state)
{
int family;
netobj obj;
enum clnt_stat stat;
if (state != 0 && host->nh_state == 0) {
host->nh_state = state;
}
mutex_enter(&host->nh_lock);
if (host->nh_flags & NLM_NH_MONITORED) {
mutex_exit(&host->nh_lock);
return;
}
host->nh_flags |= NLM_NH_MONITORED;
mutex_exit(&host->nh_lock);
nlm_netbuf_to_netobj(&host->nh_addr, &family, &obj);
stat = nlm_nsmaddr_reg(&g->nlm_nsm, host->nh_name, family, &obj);
if (stat != RPC_SUCCESS) {
NLM_WARN("Failed to register address, stat=%d\n", stat);
mutex_enter(&g->lock);
host->nh_flags &= ~NLM_NH_MONITORED;
mutex_exit(&g->lock);
return;
}
stat = nlm_nsm_mon(&g->nlm_nsm, host->nh_name, host->nh_sysid);
if (stat != RPC_SUCCESS) {
NLM_WARN("Failed to contact local NSM, stat=%d\n", stat);
mutex_enter(&g->lock);
host->nh_flags &= ~NLM_NH_MONITORED;
mutex_exit(&g->lock);
return;
}
}
int
nlm_host_get_state(struct nlm_host *hostp)
{
return (hostp->nh_state);
}
struct nlm_slock *
nlm_slock_register(
struct nlm_globals *g,
struct nlm_host *host,
struct nlm4_lock *lock,
struct vnode *vp)
{
struct nlm_slock *nslp;
nslp = kmem_zalloc(sizeof (*nslp), KM_SLEEP);
cv_init(&nslp->nsl_cond, NULL, CV_DEFAULT, NULL);
nslp->nsl_lock = *lock;
nlm_copy_netobj(&nslp->nsl_fh, &nslp->nsl_lock.fh);
nslp->nsl_state = NLM_SL_BLOCKED;
nslp->nsl_host = host;
nslp->nsl_vp = vp;
mutex_enter(&g->lock);
TAILQ_INSERT_TAIL(&g->nlm_slocks, nslp, nsl_link);
mutex_exit(&g->lock);
return (nslp);
}
void
nlm_slock_unregister(struct nlm_globals *g, struct nlm_slock *nslp)
{
mutex_enter(&g->lock);
TAILQ_REMOVE(&g->nlm_slocks, nslp, nsl_link);
mutex_exit(&g->lock);
kmem_free(nslp->nsl_fh.n_bytes, nslp->nsl_fh.n_len);
cv_destroy(&nslp->nsl_cond);
kmem_free(nslp, sizeof (*nslp));
}
int
nlm_slock_wait(struct nlm_globals *g,
struct nlm_slock *nslp, uint_t timeo_secs)
{
clock_t timeo_ticks;
int cv_res, error;
cv_res = 1;
timeo_ticks = ddi_get_lbolt() + SEC_TO_TICK(timeo_secs);
mutex_enter(&g->lock);
while (nslp->nsl_state == NLM_SL_BLOCKED && cv_res > 0) {
cv_res = cv_timedwait_sig(&nslp->nsl_cond,
&g->lock, timeo_ticks);
}
if (nslp->nsl_state == NLM_SL_CANCELLED) {
error = EINTR;
goto out;
}
if (cv_res <= 0) {
error = (cv_res < 0) ? ETIMEDOUT : EINTR;
if (nslp->nsl_state == NLM_SL_GRANTED)
error = 0;
} else {
error = 0;
VERIFY(nslp->nsl_state == NLM_SL_GRANTED);
}
out:
mutex_exit(&g->lock);
return (error);
}
int
nlm_slock_grant(struct nlm_globals *g,
struct nlm_host *hostp, struct nlm4_lock *alock)
{
struct nlm_slock *nslp;
int error = ENOENT;
mutex_enter(&g->lock);
TAILQ_FOREACH(nslp, &g->nlm_slocks, nsl_link) {
if ((nslp->nsl_state != NLM_SL_BLOCKED) ||
(nslp->nsl_host != hostp))
continue;
if (alock->svid == nslp->nsl_lock.svid &&
alock->l_offset == nslp->nsl_lock.l_offset &&
alock->l_len == nslp->nsl_lock.l_len &&
alock->fh.n_len == nslp->nsl_lock.fh.n_len &&
bcmp(alock->fh.n_bytes, nslp->nsl_lock.fh.n_bytes,
nslp->nsl_lock.fh.n_len) == 0) {
nslp->nsl_state = NLM_SL_GRANTED;
cv_broadcast(&nslp->nsl_cond);
error = 0;
break;
}
}
mutex_exit(&g->lock);
return (error);
}
int
nlm_slreq_register(struct nlm_host *hostp, struct nlm_vhold *nvp,
struct flock64 *flp)
{
struct nlm_slreq *slr, *new_slr = NULL;
int ret = EEXIST;
mutex_enter(&hostp->nh_lock);
slr = nlm_slreq_find_locked(hostp, nvp, flp);
if (slr != NULL)
goto out;
mutex_exit(&hostp->nh_lock);
new_slr = kmem_zalloc(sizeof (*slr), KM_SLEEP);
bcopy(flp, &new_slr->nsr_fl, sizeof (*flp));
mutex_enter(&hostp->nh_lock);
slr = nlm_slreq_find_locked(hostp, nvp, flp);
if (slr == NULL) {
slr = new_slr;
new_slr = NULL;
ret = 0;
TAILQ_INSERT_TAIL(&nvp->nv_slreqs, slr, nsr_link);
}
out:
mutex_exit(&hostp->nh_lock);
if (new_slr != NULL)
kmem_free(new_slr, sizeof (*new_slr));
return (ret);
}
int
nlm_slreq_unregister(struct nlm_host *hostp, struct nlm_vhold *nvp,
struct flock64 *flp)
{
struct nlm_slreq *slr;
mutex_enter(&hostp->nh_lock);
slr = nlm_slreq_find_locked(hostp, nvp, flp);
if (slr == NULL) {
mutex_exit(&hostp->nh_lock);
return (ENOENT);
}
TAILQ_REMOVE(&nvp->nv_slreqs, slr, nsr_link);
mutex_exit(&hostp->nh_lock);
kmem_free(slr, sizeof (*slr));
return (0);
}
struct nlm_slreq *
nlm_slreq_find_locked(struct nlm_host *hostp, struct nlm_vhold *nvp,
struct flock64 *flp)
{
struct nlm_slreq *slr = NULL;
ASSERT(MUTEX_HELD(&hostp->nh_lock));
TAILQ_FOREACH(slr, &nvp->nv_slreqs, nsr_link) {
if (slr->nsr_fl.l_start == flp->l_start &&
slr->nsr_fl.l_len == flp->l_len &&
slr->nsr_fl.l_pid == flp->l_pid &&
slr->nsr_fl.l_type == flp->l_type)
break;
}
return (slr);
}
void
nlm_shres_track(struct nlm_host *hostp, vnode_t *vp, struct shrlock *shrp)
{
struct nlm_shres *nsp, *nsp_new;
ASSERT(shrp->s_own_len > 0);
nsp_new = nlm_shres_create_item(shrp, vp);
mutex_enter(&hostp->nh_lock);
for (nsp = hostp->nh_shrlist; nsp != NULL; nsp = nsp->ns_next)
if (nsp->ns_vp == vp && nlm_shres_equal(shrp, nsp->ns_shr))
break;
if (nsp != NULL) {
goto out;
}
nsp = nsp_new;
nsp_new = NULL;
nsp->ns_next = hostp->nh_shrlist;
hostp->nh_shrlist = nsp;
out:
mutex_exit(&hostp->nh_lock);
if (nsp_new != NULL)
nlm_shres_destroy_item(nsp_new);
}
void
nlm_shres_untrack(struct nlm_host *hostp, vnode_t *vp, struct shrlock *shrp)
{
struct nlm_shres *nsp, *nsp_prev = NULL;
mutex_enter(&hostp->nh_lock);
nsp = hostp->nh_shrlist;
while (nsp != NULL) {
if (nsp->ns_vp == vp && nlm_shres_equal(shrp, nsp->ns_shr)) {
struct nlm_shres *nsp_del;
nsp_del = nsp;
nsp = nsp->ns_next;
if (nsp_prev != NULL)
nsp_prev->ns_next = nsp;
else
hostp->nh_shrlist = nsp;
nlm_shres_destroy_item(nsp_del);
continue;
}
nsp_prev = nsp;
nsp = nsp->ns_next;
}
mutex_exit(&hostp->nh_lock);
}
struct nlm_shres *
nlm_get_active_shres(struct nlm_host *hostp)
{
struct nlm_shres *nsp, *nslist = NULL;
mutex_enter(&hostp->nh_lock);
for (nsp = hostp->nh_shrlist; nsp != NULL; nsp = nsp->ns_next) {
struct nlm_shres *nsp_new;
nsp_new = nlm_shres_create_item(nsp->ns_shr, nsp->ns_vp);
nsp_new->ns_next = nslist;
nslist = nsp_new;
}
mutex_exit(&hostp->nh_lock);
return (nslist);
}
void
nlm_free_shrlist(struct nlm_shres *nslist)
{
struct nlm_shres *nsp;
while (nslist != NULL) {
nsp = nslist;
nslist = nslist->ns_next;
nlm_shres_destroy_item(nsp);
}
}
static bool_t
nlm_shres_equal(struct shrlock *shrp1, struct shrlock *shrp2)
{
if (shrp1->s_sysid == shrp2->s_sysid &&
shrp1->s_pid == shrp2->s_pid &&
shrp1->s_own_len == shrp2->s_own_len &&
bcmp(shrp1->s_owner, shrp2->s_owner,
shrp1->s_own_len) == 0)
return (TRUE);
return (FALSE);
}
static struct nlm_shres *
nlm_shres_create_item(struct shrlock *shrp, vnode_t *vp)
{
struct nlm_shres *nsp;
nsp = kmem_alloc(sizeof (*nsp), KM_SLEEP);
nsp->ns_shr = kmem_alloc(sizeof (*shrp), KM_SLEEP);
bcopy(shrp, nsp->ns_shr, sizeof (*shrp));
nsp->ns_shr->s_owner = kmem_alloc(shrp->s_own_len, KM_SLEEP);
bcopy(shrp->s_owner, nsp->ns_shr->s_owner, shrp->s_own_len);
nsp->ns_vp = vp;
return (nsp);
}
static void
nlm_shres_destroy_item(struct nlm_shres *nsp)
{
kmem_free(nsp->ns_shr->s_owner,
nsp->ns_shr->s_own_len);
kmem_free(nsp->ns_shr, sizeof (struct shrlock));
kmem_free(nsp, sizeof (*nsp));
}
int
nlm_svc_add_ep(struct file *fp, const char *netid, struct knetconfig *knc)
{
SVCMASTERXPRT *xprt = NULL;
int error;
error = svc_tli_kcreate(fp, 0, (char *)netid, NULL, &xprt,
&nlm_sct, NULL, NLM_SVCPOOL_ID, FALSE);
if (error != 0)
return (error);
(void) nlm_knc_to_netid(knc);
return (0);
}
int
nlm_svc_starting(struct nlm_globals *g, struct file *fp,
const char *netid, struct knetconfig *knc)
{
int error;
enum clnt_stat stat;
VERIFY(g->run_status == NLM_ST_STARTING);
VERIFY(g->nlm_gc_thread == NULL);
error = nlm_nsm_init_local(&g->nlm_nsm);
if (error != 0) {
NLM_ERR("Failed to initialize NSM handler "
"(error=%d)\n", error);
g->run_status = NLM_ST_DOWN;
return (error);
}
error = EIO;
g->nlm_gc_thread = zthread_create(NULL, 0, nlm_gc,
g, 0, minclsyspri);
stat = nlm_nsm_simu_crash(&g->nlm_nsm);
if (stat != RPC_SUCCESS) {
NLM_ERR("Failed to connect to local statd "
"(rpcerr=%d)\n", stat);
goto shutdown_lm;
}
stat = nlm_nsm_stat(&g->nlm_nsm, &g->nsm_state);
if (stat != RPC_SUCCESS) {
NLM_ERR("Failed to get the status of local statd "
"(rpcerr=%d)\n", stat);
goto shutdown_lm;
}
g->grace_threshold = ddi_get_lbolt() +
SEC_TO_TICK(g->grace_period);
error = nlm_svc_add_ep(fp, netid, knc);
if (error != 0)
goto shutdown_lm;
(void) svc_pool_control(NLM_SVCPOOL_ID,
SVCPSET_SHUTDOWN_PROC, (void *)nlm_pool_shutdown);
g->run_status = NLM_ST_UP;
return (0);
shutdown_lm:
mutex_enter(&g->lock);
g->run_status = NLM_ST_STOPPING;
mutex_exit(&g->lock);
nlm_svc_stopping(g);
return (error);
}
static void
nlm_pool_shutdown(void)
{
(void) lm_shutdown();
}
void
nlm_svc_stopping(struct nlm_globals *g)
{
mutex_enter(&g->lock);
ASSERT(g->run_status == NLM_ST_STOPPING);
cv_signal(&g->nlm_gc_sched_cv);
while (g->nlm_gc_thread != NULL)
cv_wait(&g->nlm_gc_finish_cv, &g->lock);
mutex_exit(&g->lock);
while (!avl_is_empty(&g->nlm_hosts_tree)) {
struct nlm_host *hostp;
int busy_hosts = 0;
hostp = avl_first(&g->nlm_hosts_tree);
while (hostp != NULL) {
nlm_client_cancel_all(g, hostp);
nlm_host_notify_server(hostp, 0);
mutex_enter(&hostp->nh_lock);
nlm_host_gc_vholds(hostp);
if (hostp->nh_refs > 0 || nlm_host_has_locks(hostp)) {
mutex_exit(&hostp->nh_lock);
hostp = AVL_NEXT(&g->nlm_hosts_tree, hostp);
busy_hosts++;
continue;
}
mutex_exit(&hostp->nh_lock);
hostp = AVL_NEXT(&g->nlm_hosts_tree, hostp);
}
while ((hostp = TAILQ_FIRST(&g->nlm_idle_hosts)) != NULL) {
nlm_host_unregister(g, hostp);
nlm_host_destroy(hostp);
}
if (busy_hosts > 0) {
delay(MSEC_TO_TICK(500));
}
}
ASSERT(TAILQ_EMPTY(&g->nlm_slocks));
nlm_nsm_fini(&g->nlm_nsm);
g->lockd_pid = 0;
g->run_status = NLM_ST_DOWN;
}
int
nlm_vp_active(const vnode_t *vp)
{
struct nlm_globals *g;
struct nlm_host *hostp;
struct nlm_vhold *nvp;
int active = 0;
g = zone_getspecific(nlm_zone_key, curzone);
mutex_enter(&g->lock);
hostp = avl_first(&g->nlm_hosts_tree);
while (hostp != NULL) {
mutex_enter(&hostp->nh_lock);
nvp = nlm_vhold_find_locked(hostp, vp);
mutex_exit(&hostp->nh_lock);
if (nvp != NULL) {
active = 1;
break;
}
hostp = AVL_NEXT(&g->nlm_hosts_tree, hostp);
}
mutex_exit(&g->lock);
return (active);
}
void
nlm_zone_unexport(struct nlm_globals *g, struct exportinfo *exi)
{
struct nlm_host *hostp;
mutex_enter(&g->lock);
if (g->run_status != NLM_ST_UP) {
mutex_exit(&g->lock);
return;
}
hostp = avl_first(&g->nlm_hosts_tree);
while (hostp != NULL) {
struct nlm_vhold *nvp;
if (hostp->nh_flags & NLM_NH_INIDLE) {
TAILQ_REMOVE(&g->nlm_idle_hosts, hostp, nh_link);
hostp->nh_flags &= ~NLM_NH_INIDLE;
}
hostp->nh_refs++;
mutex_exit(&g->lock);
mutex_enter(&hostp->nh_lock);
TAILQ_FOREACH(nvp, &hostp->nh_vholds_list, nv_link) {
vnode_t *vp;
nvp->nv_refcnt++;
mutex_exit(&hostp->nh_lock);
vp = nvp->nv_vp;
if (!EQFSID(&exi->exi_fsid, &vp->v_vfsp->vfs_fsid))
goto next_iter;
nlm_vhold_clean(nvp, hostp->nh_sysid);
next_iter:
mutex_enter(&hostp->nh_lock);
nvp->nv_refcnt--;
}
mutex_exit(&hostp->nh_lock);
mutex_enter(&g->lock);
nlm_host_release_locked(g, hostp);
hostp = AVL_NEXT(&g->nlm_hosts_tree, hostp);
}
mutex_exit(&g->lock);
}
void
nlm_unexport(struct exportinfo *exi)
{
struct nlm_globals *g;
rw_enter(&lm_lck, RW_READER);
TAILQ_FOREACH(g, &nlm_zones_list, nlm_link) {
if (g->nlm_zoneid == exi->exi_zoneid) {
nlm_zone_unexport(g, exi);
break;
}
}
rw_exit(&lm_lck);
}
sysid_t
nlm_sysid_alloc(void)
{
sysid_t ret_sysid = LM_NOSYSID;
rw_enter(&lm_lck, RW_WRITER);
if (nlm_sysid_nidx > LM_SYSID_MAX)
nlm_sysid_nidx = LM_SYSID;
if (!BT_TEST(nlm_sysid_bmap, nlm_sysid_nidx)) {
BT_SET(nlm_sysid_bmap, nlm_sysid_nidx);
ret_sysid = nlm_sysid_nidx++;
} else {
index_t id;
id = bt_availbit(nlm_sysid_bmap, NLM_BMAP_NITEMS);
if (id > 0) {
nlm_sysid_nidx = id + 1;
ret_sysid = id;
BT_SET(nlm_sysid_bmap, id);
}
}
rw_exit(&lm_lck);
return (ret_sysid);
}
void
nlm_sysid_free(sysid_t sysid)
{
ASSERT(sysid >= LM_SYSID && sysid <= LM_SYSID_MAX);
rw_enter(&lm_lck, RW_WRITER);
ASSERT(BT_TEST(nlm_sysid_bmap, sysid));
BT_CLEAR(nlm_sysid_bmap, sysid);
rw_exit(&lm_lck);
}
bool_t
nlm_caller_is_local(SVCXPRT *transp)
{
char *netid;
struct netbuf *rtaddr;
netid = svc_getnetid(transp);
rtaddr = svc_getrpccaller(transp);
if (netid == NULL)
return (FALSE);
if (strcmp(netid, "ticlts") == 0 ||
strcmp(netid, "ticotsord") == 0)
return (TRUE);
if (strcmp(netid, "tcp") == 0 || strcmp(netid, "udp") == 0) {
struct sockaddr_in *sin = (void *)rtaddr->buf;
if (sin->sin_addr.s_addr == htonl(INADDR_LOOPBACK))
return (TRUE);
}
if (strcmp(netid, "tcp6") == 0 || strcmp(netid, "udp6") == 0) {
struct sockaddr_in6 *sin6 = (void *)rtaddr->buf;
if (IN6_IS_ADDR_LOOPBACK(&sin6->sin6_addr))
return (TRUE);
}
return (FALSE);
}
const char *
nlm_knc_to_netid(struct knetconfig *knc)
{
int i;
dev_t rdev;
struct nlm_knc *nc;
const char *netid = NULL;
rw_enter(&lm_lck, RW_READER);
for (i = 0; i < NLM_KNCS; i++) {
nc = &nlm_netconfigs[i];
if (nc->n_knc.knc_semantics == knc->knc_semantics &&
strcmp(nc->n_knc.knc_protofmly,
knc->knc_protofmly) == 0) {
netid = nc->n_netid;
rdev = nc->n_knc.knc_rdev;
break;
}
}
rw_exit(&lm_lck);
if (netid != NULL && rdev == NODEV) {
rw_enter(&lm_lck, RW_WRITER);
if (nc->n_knc.knc_rdev == NODEV)
nc->n_knc.knc_rdev = knc->knc_rdev;
rw_exit(&lm_lck);
}
return (netid);
}
int
nlm_knc_from_netid(const char *netid, struct knetconfig *knc)
{
int i, ret;
ret = ENOENT;
for (i = 0; i < NLM_KNCS; i++) {
struct nlm_knc *nknc;
nknc = &nlm_netconfigs[i];
if (strcmp(netid, nknc->n_netid) == 0 &&
nknc->n_knc.knc_rdev != NODEV) {
*knc = nknc->n_knc;
ret = 0;
break;
}
}
return (ret);
}
void
nlm_cprsuspend(void)
{
struct nlm_globals *g;
rw_enter(&lm_lck, RW_READER);
TAILQ_FOREACH(g, &nlm_zones_list, nlm_link)
nlm_suspend_zone(g);
rw_exit(&lm_lck);
}
void
nlm_cprresume(void)
{
struct nlm_globals *g;
rw_enter(&lm_lck, RW_READER);
TAILQ_FOREACH(g, &nlm_zones_list, nlm_link)
nlm_resume_zone(g);
rw_exit(&lm_lck);
}
static void
nlm_nsm_clnt_init(CLIENT *clnt, struct nlm_nsm *nsm)
{
(void) clnt_tli_kinit(clnt, &nsm->ns_knc, &nsm->ns_addr, 0,
NLM_RPC_RETRIES, zone_kcred());
}
static void
nlm_netbuf_to_netobj(struct netbuf *addr, int *family, netobj *obj)
{
struct sockaddr *sa = (struct sockaddr *)addr->buf;
*family = sa->sa_family;
switch (sa->sa_family) {
case AF_INET: {
struct sockaddr_in *sin = (struct sockaddr_in *)sa;
obj->n_len = sizeof (sin->sin_addr);
obj->n_bytes = (char *)&sin->sin_addr;
break;
}
case AF_INET6: {
struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sa;
obj->n_len = sizeof (sin6->sin6_addr);
obj->n_bytes = (char *)&sin6->sin6_addr;
break;
}
default:
VERIFY(0);
break;
}
}