/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved. */ #include #include #include #include #include #include #include #include #include "crypto_cmds.h" static void prt_an_state(int state) { switch (state) { case REQ_ALLOCATED: mdb_printf("REQ_ALLOCATED "); break; case REQ_WAITING: mdb_printf("REQ_WAITING "); break; case REQ_INPROGRESS: mdb_printf("REQ_INPROGRESS "); break; case REQ_DONE: mdb_printf("REQ_DONE "); break; case REQ_CANCELED: mdb_printf("REQ_CANCELED "); break; default: mdb_printf("? %d ?? ", state); break; } } static const mdb_bitmask_t call_flags[] = { { "CRYPTO_ALWAYS_QUEUE", CRYPTO_ALWAYS_QUEUE, CRYPTO_ALWAYS_QUEUE }, { "CRYPTO_NOTIFY_OPDONE", CRYPTO_NOTIFY_OPDONE, CRYPTO_NOTIFY_OPDONE }, { "CRYPTO_SKIP_REQID", CRYPTO_SKIP_REQID, CRYPTO_SKIP_REQID }, { NULL, 0, 0 } }; /*ARGSUSED*/ static int kcf_areq_node_simple(kcf_areq_node_t *areqn) { mdb_printf("\nan_type: "); if (areqn->an_type != CRYPTO_ASYNCH) mdb_printf("%-8d ", areqn->an_type); else mdb_printf("CRYPTO_ASYNCH"); mdb_printf("\nan_state: "); prt_an_state(areqn->an_state); mdb_printf("\nan_context: %-16p\t", areqn->an_context); mdb_printf("an_is_my_turn: %s\t ", areqn->an_is_my_turn == B_FALSE ? "B_FALSE" : "B_TRUE"); mdb_printf("\ncr_reqid: %lx\n", areqn->an_reqarg.cr_reqid); return (DCMD_OK); } /* * Verbose print of kcf_areq_node_t */ static int v_kcf_areq_node(kcf_areq_node_t *areqn) { /* contents only -- the address is printed elsewhere */ /* First column */ mdb_printf("\n%16s: ", "an_type"); if (areqn->an_type != CRYPTO_ASYNCH) mdb_printf("%-8d ", areqn->an_type); else mdb_printf("CRYPTO_ASYNCH"); /* Second column */ mdb_printf("\t\t%16s: %p\n", "an_lock", areqn->an_lock); /* First column */ mdb_printf("%16s: ", "an_state"); prt_an_state(areqn->an_state); /* Second column */ mdb_printf("%14s: next 4 items\n", "an_reqarg"); /* First column again */ mdb_printf("%16s: '%16b'", "cr_flag", areqn->an_reqarg.cr_flag, call_flags); /* Second column */ mdb_printf("\t%16s: %p\n", "cr_callback_func", areqn->an_reqarg.cr_callback_func); /* First column again */ mdb_printf("%16s: %-16p", "cr_callback_arg", areqn->an_reqarg.cr_callback_arg); /* Second column */ mdb_printf("\t%16s: %lx\n", "cr_reqid", (ulong_t)areqn->an_reqarg.cr_reqid); /* First column again */ mdb_printf("%16s: %d", "an_params.rp_opgrp", areqn->an_params.rp_opgrp); /* Second column */ mdb_printf("\t%16s: %d\n", "an_params.rp_optype", areqn->an_params.rp_optype); /* First column again */ mdb_printf("%16s: %-16p", "an_context", areqn->an_context); /* Second column */ mdb_printf("\t%16s: %p\n", "an_ctxchain_next", areqn->an_ctxchain_next); /* First column again */ mdb_printf("%16s: %s", "an_is_my_turn", areqn->an_is_my_turn == B_FALSE ? "B_FALSE" : "B_TRUE"); /* Second column */ mdb_printf("\t\t%16s: %s\n", "an_isdual", areqn->an_isdual == B_FALSE ? "B_FALSE" : "B_TRUE"); /* First column again */ mdb_printf("%16s: %p", "an_next", areqn->an_next); /* Second column */ mdb_printf("\t\t%16s: %p\n", "an_prev", areqn->an_prev); /* First column again */ mdb_printf("%16s: %p", "an_provider", areqn->an_provider); /* Second column */ mdb_printf("\t\t%16s: %p\n", "an_idnext", areqn->an_idnext); /* First column again */ mdb_printf("%16s: %p", "an_idprev", areqn->an_idprev); /* Second column */ mdb_printf("\t\t%16s: %hx\n", "an_done", areqn->an_done); /* First column again */ mdb_printf("%16s: %d\n", "an_refcnt", areqn->an_refcnt); return (DCMD_OK); } /*ARGSUSED*/ int kcf_areq_node(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) { kcf_areq_node_t areqn; uint_t opt_v = FALSE; if (mdb_getopts(argc, argv, 'v', MDB_OPT_SETBITS, TRUE, &opt_v, NULL) != argc) return (DCMD_USAGE); /* * read even if we're looping, because the cbdata design does not * apply to mdb_pwalk_dcmd */ if (mdb_vread(&areqn, sizeof (kcf_areq_node_t), addr) == -1) { mdb_warn("cannot read %p", addr); return (DCMD_ERR); } if (opt_v) /* verbose */ return (v_kcf_areq_node(&areqn)); else return (kcf_areq_node_simple(&areqn)); } /*ARGSUSED*/ int kcf_global_swq(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) { kcf_global_swq_t swq; kcf_global_swq_t *ptr; if (!(flags & DCMD_ADDRSPEC)) { if (mdb_readsym(&ptr, sizeof (uintptr_t), "gswq") == -1) { mdb_warn("cannot read gswq"); return (DCMD_ERR); } } else ptr = (kcf_global_swq_t *)addr; if (mdb_vread(&swq, sizeof (kcf_global_swq_t), (uintptr_t)ptr) == -1) { mdb_warn("cannot read %p", ptr); return (DCMD_ERR); } mdb_printf("gs_lock (mutex):\t%p\n", swq.gs_lock); mdb_printf("gs_cv:\t%hx\n", swq.gs_cv._opaque); mdb_printf("gs_njobs:\t%u\n", swq.gs_njobs); mdb_printf("gs_maxjobs:\t%u\n", swq.gs_maxjobs); mdb_printf("gs_first:\t%p\n", swq.gs_first); mdb_printf("gs_last:\t%p\n", swq.gs_last); return (mdb_pwalk_dcmd("an_next", "kcf_areq_node", argc, argv, (uintptr_t)swq.gs_first)); } static int areq_walk_init_common(mdb_walk_state_t *wsp, boolean_t use_first) { kcf_global_swq_t gswq_copy; uintptr_t gswq_ptr; if (mdb_readsym(&gswq_ptr, sizeof (gswq_ptr), "gswq") == -1) { mdb_warn("failed to read 'gswq'"); return (WALK_ERR); } if (mdb_vread(&gswq_copy, sizeof (gswq_copy), gswq_ptr) == -1) { mdb_warn("cannot read %p", gswq_ptr); return (WALK_ERR); } if ((wsp->walk_addr = (use_first ? (uintptr_t)gswq_copy.gs_first : (uintptr_t)gswq_copy.gs_last)) == 0) { mdb_printf("Global swq is empty\n"); return (WALK_DONE); } wsp->walk_data = mdb_alloc(sizeof (kcf_areq_node_t), UM_SLEEP); return (WALK_NEXT); } int areq_first_walk_init(mdb_walk_state_t *wsp) { return (areq_walk_init_common(wsp, B_TRUE)); } int areq_last_walk_init(mdb_walk_state_t *wsp) { return (areq_walk_init_common(wsp, B_FALSE)); } typedef enum idwalk_type { IDNEXT, /* an_idnext */ IDPREV, /* an_idprev */ CTXCHAIN /* an_ctxchain_next */ } idwalk_type_t; static int an_id_walk_init(mdb_walk_state_t *wsp, idwalk_type_t type) { kcf_areq_node_t *adn; if (wsp->walk_addr == 0) { mdb_warn("must give kcf_areq_node address\n"); return (WALK_ERR); } adn = wsp->walk_data = mdb_alloc(sizeof (kcf_areq_node_t), UM_SLEEP); if (mdb_vread(adn, sizeof (kcf_areq_node_t), wsp->walk_addr) == -1) { mdb_warn("cannot read %p", wsp->walk_addr); return (WALK_ERR); } switch (type) { case IDNEXT: wsp->walk_addr = (uintptr_t)adn->an_idnext; break; case IDPREV: wsp->walk_addr = (uintptr_t)adn->an_idprev; break; case CTXCHAIN: wsp->walk_addr = (uintptr_t)adn->an_ctxchain_next; break; default: mdb_warn("Bad structure member in walk_init\n"); return (WALK_ERR); } return (WALK_NEXT); } int an_idnext_walk_init(mdb_walk_state_t *wsp) { return (an_id_walk_init(wsp, IDNEXT)); } int an_idprev_walk_init(mdb_walk_state_t *wsp) { return (an_id_walk_init(wsp, IDPREV)); } int an_ctxchain_walk_init(mdb_walk_state_t *wsp) { return (an_id_walk_init(wsp, CTXCHAIN)); } /* * At each step, read a kcf_areq_node_t into our private storage, then invoke * the callback function. We terminate when we reach a NULL type pointer. */ static int an_id_walk_step(mdb_walk_state_t *wsp, idwalk_type_t type) { int status; kcf_areq_node_t *ptr; if (wsp->walk_addr == 0) /* then we're done */ return (WALK_DONE); ptr = wsp->walk_data; if (mdb_vread(wsp->walk_data, sizeof (kcf_areq_node_t), wsp->walk_addr) == -1) { mdb_warn("cannot read %p", wsp->walk_addr); return (WALK_ERR); } status = wsp->walk_callback(wsp->walk_addr, wsp->walk_data, wsp->walk_cbdata); switch (type) { case IDNEXT: if ((wsp->walk_addr = (uintptr_t)ptr->an_idnext) == 0) return (WALK_DONE); break; case IDPREV: if ((wsp->walk_addr = (uintptr_t)ptr->an_idprev) == 0) return (WALK_DONE); break; case CTXCHAIN: if ((wsp->walk_addr = (uintptr_t)ptr->an_ctxchain_next) == 0) return (WALK_DONE); break; default: mdb_warn("Bad structure member in walk_step\n"); return (WALK_ERR); } return (status); } int an_idnext_walk_step(mdb_walk_state_t *wsp) { return (an_id_walk_step(wsp, IDNEXT)); } int an_idprev_walk_step(mdb_walk_state_t *wsp) { return (an_id_walk_step(wsp, IDPREV)); } int an_ctxchain_walk_step(mdb_walk_state_t *wsp) { return (an_id_walk_step(wsp, CTXCHAIN)); } /* * The walker's fini function is invoked at the end of each walk. Since we * dynamically allocated a kcf_areq_node_t in areq_walk_init, * we must free it now. */ void areq_walk_fini(mdb_walk_state_t *wsp) { #ifdef DEBUG mdb_printf("...end of kcf_areq_node walk\n"); #endif mdb_free(wsp->walk_data, sizeof (kcf_areq_node_t)); } /* * At each step, read a kcf_areq_node_t into our private storage, then invoke * the callback function. We terminate when we reach a NULL an_next pointer * or a NULL an_prev pointer. use_next flag indicates which one to check. */ static int an_walk_step_common(mdb_walk_state_t *wsp, boolean_t use_next) { int status; kcf_areq_node_t *ptr; ptr = (kcf_areq_node_t *)wsp->walk_data; if (mdb_vread(wsp->walk_data, sizeof (kcf_areq_node_t), wsp->walk_addr) == -1) { mdb_warn("failed to read kcf_areq_node at %p", wsp->walk_addr); return (WALK_DONE); } status = wsp->walk_callback(wsp->walk_addr, wsp->walk_data, wsp->walk_cbdata); if ((wsp->walk_addr = (use_next ? (uintptr_t)ptr->an_next : (uintptr_t)ptr->an_prev)) == 0) return (WALK_DONE); return (status); } int an_next_walk_step(mdb_walk_state_t *wsp) { return (an_walk_step_common(wsp, B_TRUE)); } int an_prev_walk_step(mdb_walk_state_t *wsp) { return (an_walk_step_common(wsp, B_FALSE)); } /* * Walker data for reqid_table walking */ typedef struct reqid_data { kcf_reqid_table_t rd_table; kcf_reqid_table_t *rd_tbl_ptrs[REQID_TABLES]; int rd_cur_index; } reqid_data_t; typedef struct reqid_cb_data { crypto_req_id_t cb_reqid; int verbose; int found; } reqid_cb_data_t; extern int crypto_pr_reqid(uintptr_t, reqid_data_t *, reqid_cb_data_t *); int reqid_table_walk_init(mdb_walk_state_t *wsp) { reqid_data_t *wdata; reqid_cb_data_t *cbdata; wsp->walk_callback = (mdb_walk_cb_t)crypto_pr_reqid; wsp->walk_data = mdb_alloc(sizeof (reqid_data_t), UM_SLEEP); /* see if the walker was called from the command line or mdb_pwalk */ if (wsp->walk_cbdata == NULL) { /* command line */ if ((wsp->walk_cbdata = mdb_zalloc(sizeof (reqid_cb_data_t), UM_SLEEP)) == NULL) { mdb_warn("couldn't get cb memory for " "reqid_table_walker"); return (WALK_ERR); } /* initialize for a simple walk, as opposed to a reqid search */ cbdata = wsp->walk_cbdata; cbdata->verbose = TRUE; cbdata->cb_reqid = 0; } wdata = (reqid_data_t *)wsp->walk_data; if (mdb_readsym(wdata->rd_tbl_ptrs, sizeof (wdata->rd_tbl_ptrs), "kcf_reqid_table") == -1) { mdb_warn("failed to read 'kcf_reqid_table'"); return (WALK_ERR); } wdata->rd_cur_index = 0; wsp->walk_addr = (uintptr_t)wdata->rd_tbl_ptrs[wdata->rd_cur_index]; return (WALK_NEXT); } /* * At each step, read a kcf_reqid_table_t into our private storage, then invoke * the callback function. We terminate when we reach a */ int reqid_table_walk_step(mdb_walk_state_t *wsp) { int status; reqid_data_t *wdata; wdata = wsp->walk_data; wsp->walk_addr = (uintptr_t)wdata->rd_tbl_ptrs[wdata->rd_cur_index]; #ifdef DEBUG mdb_printf( "DEBUG: kcf_reqid_table at %p, sizeof kcf_reqid_table_t = %d\n", wsp->walk_addr, sizeof (kcf_reqid_table_t)); #endif status = wsp->walk_callback(wsp->walk_addr, wsp->walk_data, wsp->walk_cbdata); /* get ready for next call */ wdata->rd_cur_index++; if (wdata->rd_cur_index >= REQID_TABLES) return (WALK_DONE); return (status); } /* * The walker's fini function is invoked at the end of each walk. Since we * dynamically allocated a reqid_data_t in areq_walk_init, * we must free it now. */ void reqid_table_walk_fini(mdb_walk_state_t *wsp) { #ifdef DEBUG mdb_printf("...end of kcf_reqid walk\n"); #endif mdb_free(wsp->walk_data, sizeof (reqid_data_t)); } /* * If there's an argument beyond -v, then we're looking for a specific * reqid, otherwise, print any non-null kcf_areq things we run across. */ int crypto_pr_reqid(uintptr_t addr, reqid_data_t *data, reqid_cb_data_t *cbdata) { kcf_areq_node_t node; int i; int needhdr = TRUE; if (addr == 0) { mdb_printf("kcf_reqid_table[%d] = NULL\n", data->rd_cur_index); return (WALK_NEXT); } if (mdb_vread(&(data->rd_table), sizeof (kcf_reqid_table_t), addr) == -1) { mdb_warn("failed to read kcf_reqid_table at %p", addr); return (WALK_ERR); } /* Loop over all rt_idhash's */ for (i = 0; i < REQID_BUCKETS; i++) { uint_t number_in_chain = 0; uintptr_t node_addr; /* follow the an_idnext chains for each bucket */ do { /* read kcf_areq_node */ if (number_in_chain == 0) node_addr = (uintptr_t)data->rd_table.rt_idhash[i]; else /*LINTED*/ node_addr = (uintptr_t)node.an_idnext; #ifdef DEBUG mdb_printf("DEBUG: node_addr = %p\n", node_addr); #endif if (node_addr == 0) break; /* skip */ if (mdb_vread(&node, sizeof (kcf_areq_node_t), node_addr) == -1) { if (cbdata->verbose == TRUE) mdb_printf( "cannot read rt_idhash %d an_idnext %d\n", i, number_in_chain); break; } /* see if we want to print it */ if ((cbdata->cb_reqid == 0) || (node.an_reqarg.cr_reqid == cbdata->cb_reqid)) { cbdata->found = TRUE; /* printed if false || reqid */ /* is this the first rd_idhash found for this table? */ if (needhdr == TRUE) { /* print both indices in bold */ mdb_printf("%kcf_reqid_table[%lu] at %p:%\n", data->rd_cur_index, addr); mdb_printf("\trt_lock: %p\trt_curid: %llx\n", data->rd_table.rt_lock, data->rd_table.rt_curid); needhdr = FALSE; } /* print kcf_areq_node */ if (number_in_chain < 1) mdb_printf( " %rt_idhash[%lu%]% = %%p:%\n", i, node_addr); else mdb_printf( " rt_idhash[%lu%]" " an_idnext %d = %%p:%\n", i, number_in_chain, node_addr); mdb_inc_indent(8); /* if we're looking for one and only one reqid */ /* do it REALLY verbose */ if ((node.an_reqarg.cr_reqid == cbdata->cb_reqid) && (cbdata->cb_reqid != 0)) v_kcf_areq_node(&node); else if (cbdata->verbose == TRUE) /* * verbose for this walker means non-verbose for * the kcf_areq_node details */ kcf_areq_node_simple(&node); mdb_dec_indent(8); } /* if we only wanted one reqid, quit now */ if (node.an_reqarg.cr_reqid == cbdata->cb_reqid) { return (WALK_DONE); } number_in_chain++; } while (node.an_idnext != NULL); /* follow chain in same bucket */ } /* for each REQID_BUCKETS */ if ((needhdr == TRUE) && (cbdata->cb_reqid == 0)) { mdb_printf("%kcf_reqid_table[%lu]: %p\n", data->rd_cur_index, addr); } return (WALK_NEXT); } /*ARGSUSED*/ int crypto_find_reqid(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) { const mdb_arg_t *argp = NULL; reqid_cb_data_t cbdata; int i, status; cbdata.cb_reqid = 0L; cbdata.verbose = FALSE; cbdata.found = FALSE; if (flags & DCMD_ADDRSPEC) { mdb_printf("use addr ::kcf_reqid_table\n"); return (DCMD_USAGE); } if ((i = mdb_getopts(argc, argv, 'v', MDB_OPT_SETBITS, TRUE, &cbdata.verbose, NULL)) != argc) { if (argc - i > 1) return (DCMD_USAGE); } if (argc > i) argp = &argv[i]; if ((argp != NULL)) { if (argp->a_type == MDB_TYPE_IMMEDIATE) cbdata.cb_reqid = argp->a_un.a_val; else cbdata.cb_reqid = (crypto_req_id_t) mdb_strtoull(argp->a_un.a_str); } status = mdb_pwalk("kcf_reqid_table", (mdb_walk_cb_t)crypto_pr_reqid, &cbdata, addr); if ((cbdata.cb_reqid != 0L) && (cbdata.found == FALSE)) mdb_printf("ID 0x%lx not found\n", cbdata.cb_reqid); #ifdef DEBUG else mdb_printf("DEBUG: cbdata.db_reqid = %lx, cbdata.found = %d\n", cbdata.cb_reqid, cbdata.found); #endif return (status); } int kcf_reqid_table_dcmd(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) { reqid_data_t wdata; reqid_cb_data_t cbdata; if (!(flags & DCMD_ADDRSPEC)) return (DCMD_USAGE); memset(&wdata, 0, sizeof (wdata)); memset(&cbdata, 0, sizeof (cbdata)); if ((mdb_getopts(argc, argv, 'v', MDB_OPT_SETBITS, TRUE, &cbdata.verbose, NULL)) != argc) { return (DCMD_USAGE); } crypto_pr_reqid(addr, &wdata, &cbdata); return (DCMD_OK); }