root/usr/src/cmd/mdb/common/modules/nfs/svc.c
/*
 * This file and its contents are supplied under the terms of the
 * Common Development and Distribution License ("CDDL"), version 1.0.
 * You may only use this file in accordance with the terms of version
 * 1.0 of the CDDL.
 *
 * A full copy of the text of the CDDL should have accompanied this
 * source.  A copy of the CDDL is also available via the Internet at
 * http://www.illumos.org/license/CDDL.
 */
/*
 * Copyright 2021 Tintri by DDN, Inc. All rights reserved.
 */

#include <sys/mdb_modapi.h>
#include <mdb/mdb_ctf.h>
#include <sys/types.h>
#include <sys/zone.h>
#include <rpc/svc.h>

#include "common.h"
#include "svc.h"

/*
 * svc_pool dcmd implementation
 */

static const char *
svc_idname(uint_t id)
{
        switch (id) {
        case NFS_SVCPOOL_ID:
                return ("NFS");
        case NLM_SVCPOOL_ID:
                return ("NLM");
        case NFS_CB_SVCPOOL_ID:
                return ("NFS_CB");
        default:
                return ("");
        }
}

static void
svc_print_pool(SVCPOOL *pool, uintptr_t addr)
{
        mdb_printf("SVCPOOL = %p -> POOL ID = %s(%d)\n", addr,
            svc_idname(pool->p_id), pool->p_id);
        mdb_printf("Non detached threads    = %d\n", pool->p_threads);
        mdb_printf("Detached threads        = %d\n", pool->p_detached_threads);
        mdb_printf("Max threads             = %d\n", pool->p_maxthreads);
        mdb_printf("`redline'               = %d\n", pool->p_redline);
        mdb_printf("Reserved threads        = %d\n", pool->p_reserved_threads);
        mdb_printf("Thread lock             = %s\n",
            common_mutex(&pool->p_thread_lock));

        mdb_printf("Asleep threads          = %d\n", pool->p_asleep);
        mdb_printf("Request lock            = %s\n",
            common_mutex(&pool->p_req_lock));

        mdb_printf("Pending requests        = %d\n", pool->p_reqs);
        mdb_printf("Walking threads         = %d\n", pool->p_walkers);
        mdb_printf("Max requests from xprt  = %d\n", pool->p_max_same_xprt);
        mdb_printf("Stack size for svc_run  = %d\n", pool->p_stksize);
        mdb_printf("Creator lock            = %s\n",
            common_mutex(&pool->p_creator_lock));

        mdb_printf("No of Master xprt's     = %d\n", pool->p_lcount);
        mdb_printf("rwlock for mxprtlist    = %s\n",
            common_rwlock(&pool->p_lrwlock));
        mdb_printf("master xprt list ptr    = %p\n\n", pool->p_lhead);
}

int
svc_pool_dcmd(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
{
        SVCPOOL svcpool;
        uint_t opt_v = FALSE;
        int *pools;
        int count, i;

        if ((flags & DCMD_ADDRSPEC) == 0) {
                /* Walk through all svcpools in the global zone */
                if (mdb_walk_dcmd("svc_pool", "svc_pool", argc, argv) == -1) {
                        mdb_warn("failed to walk svcpools");
                        return (DCMD_ERR);
                }
                return (DCMD_OK);
        }

        count = mdb_getopts(argc, argv,
            'v', MDB_OPT_SETBITS, TRUE, &opt_v, NULL);
        argc -= count;
        argv += count;

        pools = mdb_alloc(argc * sizeof (*pools), UM_SLEEP | UM_GC);
        for (i = 0; i < argc; i++) {
                const char *s;

                switch (argv[i].a_type) {
                case MDB_TYPE_STRING:
                        s = argv[i].a_un.a_str;

                        if (strcmp(s, "nfs") == 0)
                                pools[i] = NFS_SVCPOOL_ID;
                        else if (strcmp(s, "nlm") == 0)
                                pools[i] = NLM_SVCPOOL_ID;
                        else if (strcmp(s, "nfs_cb") == 0)
                                pools[i] = NFS_CB_SVCPOOL_ID;
                        else
                                return (DCMD_USAGE);

                        break;

                case MDB_TYPE_IMMEDIATE:
                        pools[i] = (int)argv[i].a_un.a_val;
                        break;

                default:
                        return (DCMD_USAGE);
                }
        }

        if (mdb_vread(&svcpool, sizeof (svcpool), addr) == -1) {
                mdb_warn("failed to read svcpool");
                return (DCMD_ERR);
        }

        /*
         * Make sure the svcpool is on the list (or the list is empty).
         * If not, just return with DCMD_OK.
         */
        for (i = 0; i < argc; i++) {
                if (svcpool.p_id == pools[i]) {
                        argc = 0;
                        break;
                }
        }
        if (argc != 0)
                return (DCMD_OK);

        /* Print the svcpool */
        svc_print_pool(&svcpool, addr);
        if (opt_v && svcpool.p_lhead && (mdb_pwalk_dcmd("svc_mxprt",
            "svc_mxprt", 0, NULL, (uintptr_t)svcpool.p_lhead) == -1))
                return (DCMD_ERR);

        return (DCMD_OK);
}

void
svc_pool_help(void)
{
        mdb_printf(
            "-v       display also the master xprts for the svcpools\n"
            "poolid   either $[numeric] or verbose: nfs or nlm or nfs_cb\n"
            "\n"
            "If the poolid list is specified, only those svcpools are dumped\n"
            "whose poolid is in the list.\n");
}

/*
 * svc_mxprt dcmd implementation
 */

static void
svc_print_masterxprt(SVCMASTERXPRT *xprt)
{
        mdb_printf("svcxprt_common structure:\n");
        mdb_printf("queue ptr               = %p\n", xprt->xp_wq);
        mdb_printf("cached cred for server  = %d\n", xprt->xp_cred);
        mdb_printf("transport type          = %d\n", xprt->xp_type);
        mdb_printf("TSDU or TIDU size       = %d\n", xprt->xp_msg_size);

        mdb_printf("address                 = %s\n",
            common_netbuf_str(&xprt->xp_rtaddr));
        mdb_printf("Request queue head      = %p\n", xprt->xp_req_head);
        mdb_printf("Request queue tail      = %p\n", xprt->xp_req_tail);
        mdb_printf("Request lock address    = %s\n",
            common_mutex(&xprt->xp_req_lock));

        mdb_printf("Current no of attached threads  = %d\n",
            xprt->xp_threads);
        mdb_printf("Current no of detached threads  = %d\n",
            xprt->xp_detached_threads);
        mdb_printf("Thread count lock address       = %s\n\n",
            common_mutex(&xprt->xp_thread_lock));
}

int
svc_mxprt_dcmd(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
{
        SVCMASTERXPRT xprt;
        uint_t opt_w = FALSE;

        if (mdb_getopts(argc, argv,
            'w', MDB_OPT_SETBITS, TRUE, &opt_w, NULL) != argc)
                return (DCMD_USAGE);

        if ((flags & DCMD_ADDRSPEC) == 0) {
                mdb_printf("requires address of SVCMASTERXPRT\n");
                return (DCMD_USAGE);
        }

        if (opt_w) {
                /* Walk through all xprts */
                if (mdb_pwalk_dcmd("svc_mxprt", "svc_mxprt", 0, NULL,
                    addr) == -1) {
                        mdb_warn("failed to walk svc_mxprt");
                        return (DCMD_ERR);
                }
                return (DCMD_OK);
        }

        if (mdb_vread(&xprt, sizeof (xprt), addr) == -1) {
                mdb_warn("failed to read xprt");
                return (DCMD_ERR);
        }

        svc_print_masterxprt(&xprt);

        return (DCMD_OK);
}

void
svc_mxprt_help(void)
{
        mdb_printf(
            "-w       walks along all master xprts in the list\n"
            "\n"
            "The following two commands are equivalent:\n"
            "  ::svc_mxprt -w\n"
            "  ::walk svc_mxprt|::svc_mxprt\n");
}

/*
 * svc_pool walker implementation
 */

static int
svc_get_pool(uintptr_t zone_addr, uintptr_t *svc_addr)
{
        mdb_ctf_id_t id;
        ulong_t offset;
        uintptr_t glob_addr;

        if (zoned_get_zsd(zone_addr, "svc_zone_key", &glob_addr) != DCMD_OK) {
                mdb_warn("failed to get zoned svc");
                return (WALK_ERR);
        }

        if (mdb_ctf_lookup_by_name("struct svc_globals", &id)) {
                mdb_warn("failed to look up type %s", "struct svc_globals");
                return (WALK_ERR);
        }

        if (mdb_ctf_offsetof(id, "svc_pools", &offset)) {
                mdb_warn("failed to get %s offset", "svc_pools");
                return (WALK_ERR);
        }

        offset /= NBBY;
        if (mdb_vread(svc_addr, sizeof (*svc_addr), glob_addr + offset) == -1) {
                mdb_warn("failed to read svc_pools address");
                return (WALK_ERR);
        }

        return (WALK_NEXT);
}

int
svc_pool_walk_init(mdb_walk_state_t *wsp)
{
        /* Use global zone by default */
        if (wsp->walk_addr == 0) {
                /* wsp->walk_addr = global_zone */
                if (mdb_readvar(&wsp->walk_addr, "global_zone") == -1) {
                        mdb_warn("failed to locate global_zone");
                        return (WALK_ERR);
                }
        }

        /* put svcpool address of the zone into wsp->walk_addr */
        return (svc_get_pool(wsp->walk_addr, &wsp->walk_addr));
}

int
svc_pool_walk_step(mdb_walk_state_t *wsp)
{
        SVCPOOL pool;
        uintptr_t addr = wsp->walk_addr;

        if (addr == 0)
                return (WALK_DONE);

        if (mdb_vread(&pool, sizeof (pool), addr) == -1) {
                mdb_warn("failed to read SVCPOOL");
                return (WALK_ERR);
        }

        wsp->walk_addr = (uintptr_t)pool.p_next;
        return (wsp->walk_callback(addr, &pool, wsp->walk_cbdata));
}

/*
 * svc_mxprt walker implementation
 */

int
svc_mxprt_walk_init(mdb_walk_state_t *wsp)
{
        if (wsp->walk_addr == 0) {
                mdb_warn("global walk not supported");
                return (WALK_ERR);
        }

        wsp->walk_data = (void *)wsp->walk_addr;

        return (WALK_NEXT);
}

int
svc_mxprt_walk_step(mdb_walk_state_t *wsp)
{
        SVCMASTERXPRT xprt;
        uintptr_t addr = wsp->walk_addr;
        int status;

        if (mdb_vread(&xprt, sizeof (xprt), addr) == -1) {
                mdb_warn("can't read SVCMASTERXPRT");
                return (WALK_ERR);
        }

        wsp->walk_addr = (uintptr_t)xprt.xp_next;

        status = wsp->walk_callback(addr, &xprt, wsp->walk_cbdata);
        if (status != WALK_NEXT)
                return (status);

        return (((void *)wsp->walk_addr == wsp->walk_data) ? WALK_DONE
            : WALK_NEXT);
}