#include <sys/types.h>
#include <sys/errno.h>
#include <sys/modctl.h>
#include <sys/flock.h>
#include <nfs/nfs.h>
#include <nfs/nfssys.h>
#include <nfs/lm.h>
#include <rpcsvc/nlm_prot.h>
#include "nlm_impl.h"
static struct modlmisc modlmisc = {
&mod_miscops, "lock mgr common module"
};
static struct modlinkage modlinkage = {
MODREV_1, &modlmisc, NULL
};
int lm_global_nlmid = 0;
void (*lm_set_nlm_status)(int nlm_id, flk_nlm_status_t) = NULL;
void (*lm_remove_file_locks)(int) = NULL;
zone_key_t nlm_zone_key;
void *
lm_zone_init(zoneid_t zoneid)
{
struct nlm_globals *g;
g = kmem_zalloc(sizeof (*g), KM_SLEEP);
avl_create(&g->nlm_hosts_tree, nlm_host_cmp,
sizeof (struct nlm_host),
offsetof(struct nlm_host, nh_by_addr));
g->nlm_hosts_hash = mod_hash_create_idhash("nlm_host_by_sysid",
64, mod_hash_null_valdtor);
TAILQ_INIT(&g->nlm_idle_hosts);
TAILQ_INIT(&g->nlm_slocks);
mutex_init(&g->lock, NULL, MUTEX_DEFAULT, NULL);
cv_init(&g->nlm_gc_sched_cv, NULL, CV_DEFAULT, NULL);
cv_init(&g->nlm_gc_finish_cv, NULL, CV_DEFAULT, NULL);
mutex_init(&g->clean_lock, NULL, MUTEX_DEFAULT, NULL);
g->lockd_pid = 0;
g->run_status = NLM_ST_DOWN;
g->nlm_zoneid = zoneid;
nlm_globals_register(g);
return (g);
}
void
lm_zone_fini(zoneid_t zoneid, void *data)
{
struct nlm_globals *g = data;
nlm_globals_unregister(g);
ASSERT(avl_is_empty(&g->nlm_hosts_tree));
avl_destroy(&g->nlm_hosts_tree);
mod_hash_destroy_idhash(g->nlm_hosts_hash);
ASSERT(g->nlm_gc_thread == NULL);
mutex_destroy(&g->lock);
cv_destroy(&g->nlm_gc_sched_cv);
cv_destroy(&g->nlm_gc_finish_cv);
mutex_destroy(&g->clean_lock);
kmem_free(g, sizeof (*g));
}
int
_init()
{
int retval;
rw_init(&lm_lck, NULL, RW_DEFAULT, NULL);
nlm_init();
zone_key_create(&nlm_zone_key, lm_zone_init, NULL, lm_zone_fini);
zone_key_create(&flock_zone_key, flk_zone_init, NULL, flk_zone_fini);
retval = mod_install(&modlinkage);
if (retval == 0)
return (0);
(void) zone_key_delete(flock_zone_key);
flock_zone_key = ZONE_KEY_UNINITIALIZED;
(void) zone_key_delete(nlm_zone_key);
rw_destroy(&lm_lck);
return (retval);
}
int
_fini()
{
return (EBUSY);
}
int
_info(struct modinfo *modinfop)
{
return (mod_info(&modlinkage, modinfop));
}
int
lm_svc(struct lm_svc_args *args)
{
struct knetconfig knc;
const char *netid;
struct nlm_globals *g;
struct file *fp = NULL;
int err = 0;
g = zone_getspecific(nlm_zone_key, curzone);
if (args->version != LM_SVC_CUR_VERS) {
NLM_ERR("lm_svc: Version mismatch "
"(given 0x%x, expected 0x%x)\n",
args->version, LM_SVC_CUR_VERS);
return (EINVAL);
}
bzero(&knc, sizeof (knc));
switch (args->n_proto) {
case LM_TCP:
knc.knc_semantics = NC_TPI_COTS_ORD;
knc.knc_proto = NC_TCP;
break;
case LM_UDP:
knc.knc_semantics = NC_TPI_CLTS;
knc.knc_proto = NC_UDP;
break;
default:
NLM_ERR("nlm_build_knetconfig: Unknown "
"lm_proto=0x%x\n", args->n_proto);
return (EINVAL);
}
switch (args->n_fmly) {
case LM_INET:
knc.knc_protofmly = NC_INET;
break;
case LM_INET6:
knc.knc_protofmly = NC_INET6;
break;
case LM_LOOPBACK:
knc.knc_protofmly = NC_LOOPBACK;
knc.knc_proto = NC_NOPROTO;
break;
default:
NLM_ERR("nlm_build_knetconfig: Unknown "
"lm_fmly=0x%x\n", args->n_fmly);
return (EINVAL);
}
knc.knc_rdev = args->n_rdev;
netid = nlm_knc_to_netid(&knc);
if (!netid)
return (EINVAL);
if ((fp = getf(args->fd)) == NULL)
return (EBADF);
mutex_enter(&g->lock);
if (g->run_status == NLM_ST_STOPPING) {
err = EAGAIN;
goto out;
}
if (g->run_status == NLM_ST_DOWN) {
g->run_status = NLM_ST_STARTING;
g->lockd_pid = curproc->p_pid;
g->cn_idle_tmo = args->timout;
g->grace_period = args->grace;
g->retrans_tmo = args->retransmittimeout;
if (INGLOBALZONE(curproc)) {
rfs4_grace_period = args->grace;
rfs4_lease_time = args->grace;
}
mutex_exit(&g->lock);
err = nlm_svc_starting(g, fp, netid, &knc);
mutex_enter(&g->lock);
} else {
if (g->run_status != NLM_ST_UP) {
err = ENOTACTIVE;
goto out;
}
if (g->lockd_pid != curproc->p_pid) {
err = EPERM;
goto out;
}
err = nlm_svc_add_ep(fp, netid, &knc);
}
out:
mutex_exit(&g->lock);
if (fp != NULL)
releasef(args->fd);
return (err);
}
int
lm_shutdown(void)
{
struct nlm_globals *g;
proc_t *p;
pid_t pid;
g = zone_getspecific(nlm_zone_key, curzone);
mutex_enter(&g->lock);
if (g->run_status != NLM_ST_UP) {
mutex_exit(&g->lock);
return (EBUSY);
}
g->run_status = NLM_ST_STOPPING;
pid = g->lockd_pid;
mutex_exit(&g->lock);
nlm_svc_stopping(g);
mutex_enter(&pidlock);
p = prfind(pid);
if (p != NULL)
psignal(p, SIGTERM);
mutex_exit(&pidlock);
return (0);
}
void
lm_unexport(struct exportinfo *exi)
{
nlm_unexport(exi);
}
void
lm_cprsuspend(void)
{
nlm_cprsuspend();
}
void
lm_cprresume(void)
{
nlm_cprresume();
}
void
lm_set_nlmid_flk(int *new_sysid)
{
if (lm_global_nlmid != 0)
*new_sysid |= (lm_global_nlmid << BITS_IN_SYSID);
}
void
lm_free_config(struct knetconfig *knc)
{
_NOTE(ARGUNUSED(knc));
}
int
lm_vp_active(const vnode_t *vp)
{
return (nlm_vp_active(vp));
}
struct lm_sysid *
lm_get_sysid(struct knetconfig *knc, struct netbuf *addr,
char *name, bool_t *nc_changed)
{
struct nlm_globals *g;
const char *netid;
struct nlm_host *hostp;
_NOTE(ARGUNUSED(nc_changed));
netid = nlm_knc_to_netid(knc);
if (netid == NULL)
return (NULL);
g = zone_getspecific(nlm_zone_key, curzone);
hostp = nlm_host_findcreate(g, name, netid, addr, NULL);
if (hostp == NULL)
return (NULL);
return ((struct lm_sysid *)hostp);
}
void
lm_rel_sysid(struct lm_sysid *sysid)
{
struct nlm_globals *g;
g = zone_getspecific(nlm_zone_key, curzone);
nlm_host_release(g, (struct nlm_host *)sysid);
}
sysid_t
lm_alloc_sysidt(void)
{
return (nlm_sysid_alloc());
}
void
lm_free_sysidt(sysid_t sysid)
{
nlm_sysid_free(sysid);
}
sysid_t
lm_sysidt(struct lm_sysid *lms)
{
return (((struct nlm_host *)lms)->nh_sysid);
}
int
lm_safelock(vnode_t *vp, const struct flock64 *fl, cred_t *cr)
{
return (nlm_safelock(vp, fl, cr));
}
int
lm_safemap(const vnode_t *vp)
{
return (nlm_safemap(vp));
}
int
lm_has_sleep(const vnode_t *vp)
{
return (nlm_has_sleep(vp));
}