/* * 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 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. * * Copyright 2019 Joyent, Inc. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define FT(var, typ) (*((typ *)(&(var)))) static int dump_states(uintptr_t array_vaddr, int verbose, struct i_ddi_soft_state *sp); static int i_vhci_states(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv, struct i_ddi_soft_state *sp); static int vhci_states(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv); static int mdiclient(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv); static int vhciguid(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv); static int vhcilun(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv); static int i_vhcilun(uintptr_t addr, uint_t display_single_guid, char *guid); /* Utils */ static int get_mdbstr(uintptr_t addr, char *name); static void dump_mutex(kmutex_t m, char *name); static void dump_condvar(kcondvar_t c, char *name); static void dump_string(uintptr_t addr, char *name); static void dump_flags(unsigned long long flags, char **strings); static void dump_state_str(char *name, uintptr_t addr, char **strings); static int mpxio_walk_cb(uintptr_t addr, const void *data, void *cbdata); static const mdb_dcmd_t dcmds[] = { { "vhci_states", "[ -v ]", "dump all the vhci state pointers", vhci_states }, { "vhciguid", NULL, "list all clients or given a guid, list one client", vhciguid }, { NULL } }; static const mdb_modinfo_t modinfo = { MDB_API_VERSION, dcmds, NULL }; static char *client_lb_str[] = { "NONE", "RR", "LBA", NULL }; static char *mdi_client_states[] = { NULL, "OPTIMAL", "DEGRADED", "FAILED", NULL }; static char *client_flags[] = { "MDI_CLIENT_FLAGS_OFFLINE", "MDI_CLIENT_FLAGS_SUSPEND", "MDI_CLIENT_FLAGS_POWER_DOWN", "MDI_CLIENT_FLAGS_DETACH", "MDI_CLIENT_FLAGS_FAILOVER", "MDI_CLIENT_FLAGS_REPORT_DEV", "MDI_CLIENT_FLAGS_PATH_FREE_IN_PROGRESS", "MDI_CLIENT_FLAGS_ASYNC_FREE", "MDI_CLIENT_FLAGS_DEV_NOT_SUPPORTED", NULL }; static char *vhci_conf_flags[] = { "VHCI_CONF_FLAGS_AUTO_FAILBACK", NULL }; static char *svlun_flags[] = { "VLUN_TASK_D_ALIVE_FLG", "VLUN_RESERVE_ACTIVE_FLG", "VLUN_QUIESCED_FLG", NULL }; static char mdipathinfo_cb_str[] = "::print struct mdi_pathinfo"; const mdb_modinfo_t * _mdb_init(void) { return (&modinfo); } /* * mdiclient() * * Dump mdi_client_t info and list all paths. */ /* ARGSUSED */ static int mdiclient(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) { struct mdi_client value; if (!(flags & DCMD_ADDRSPEC)) { mdb_warn("mdiclient: requires an address"); return (DCMD_ERR); } if (mdb_vread(&value, sizeof (struct mdi_client), addr) != sizeof (struct mdi_client)) { mdb_warn("mdiclient: Failed read on %l#r\n", addr); return (DCMD_ERR); } mdb_printf("----------------- mdi_client @ %#lr ----------\n", addr); dump_string((uintptr_t)value.ct_guid, "GUID (ct_guid)"); dump_string((uintptr_t)value.ct_drvname, "Driver Name (ct_drvname)"); dump_state_str("Load Balance (ct_lb)", value.ct_lb, client_lb_str); mdb_printf("\n"); mdb_printf("ct_hnext: %26l#r::print struct mdi_client\n", value.ct_hnext); mdb_printf("ct_hprev: %26l#r::print struct mdi_client\n", value.ct_hprev); mdb_printf("ct_dip: %28l#r::print struct dev_info\n", value.ct_dip); mdb_printf("ct_vhci: %27l#r::print struct mdi_vhci\n", value.ct_vhci); mdb_printf("ct_cprivate: %23l#r\n", value.ct_cprivate); mdb_printf("\nct_path_head: %22l#r::print struct mdi_pathinfo\n", value.ct_path_head); mdb_printf("ct_path_tail: %22l#r::print struct mdi_pathinfo\n", value.ct_path_tail); mdb_printf("ct_path_last: %22l#r::print struct mdi_pathfinfo\n", value.ct_path_last); mdb_printf("ct_path_count: %21d\n", value.ct_path_count); mdb_printf("List of paths:\n"); mdb_pwalk("mdipi_client_list", (mdb_walk_cb_t)mpxio_walk_cb, mdipathinfo_cb_str, (uintptr_t)value.ct_path_head); mdb_printf("\n"); dump_state_str("Client State (ct_state)", value.ct_state, mdi_client_states); dump_mutex(value.ct_mutex, "per-client mutex (ct_mutex):"); mdb_printf("ct_flags: %26d\n", value.ct_flags); if (value.ct_flags) { dump_flags((unsigned long long)value.ct_flags, client_flags); } mdb_printf("ct_unstable: %23d\n", value.ct_unstable); dump_condvar(value.ct_unstable_cv, "ct_unstable_cv"); dump_condvar(value.ct_failover_cv, "ct_failover_cv"); mdb_printf("\n"); mdb_printf("ct_failover_flags TEMP_VAR: %8d\n", value.ct_failover_flags); mdb_printf("ct_failover_status UNUSED: %9d\n", value.ct_failover_status); return (DCMD_OK); } /* * vhcilun() * * Get client info given a guid. */ /* ARGSUSED */ static int vhcilun(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) { if (!(flags & DCMD_ADDRSPEC)) { mdb_warn("sv_lun: requires an address"); return (DCMD_ERR); } return (i_vhcilun(addr, 0 /* display_single_guid */, 0)); } /* * vhciguid() * * List all the clients. */ /* ARGSUSED */ static int vhciguid(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) { struct i_ddi_soft_state ss; int i; mdi_vhci_t *mdi_vhci_value; mdi_client_t *mdi_client_value; struct client_hash *ct_hash_val; struct client_hash *ct_hash_table_val; int len = strlen(MDI_HCI_CLASS_SCSI); int mdi_vhci_len = sizeof (*mdi_vhci_value); int mdi_client_len = sizeof (*mdi_client_value); int ct_hash_len = sizeof (*ct_hash_val); int ct_hash_count = 0; char *class; int found = 0; uintptr_t buf; uintptr_t temp; if (flags & DCMD_ADDRSPEC) mdb_warn("This command doesn't use an address\n"); if (i_vhci_states(0, 0, 0, 0, &ss) != DCMD_OK) return (DCMD_ERR); if (mdb_readvar(&buf, "mdi_vhci_head") == -1) { mdb_warn("mdi driver variable mdi_vhci_head not found.\n"); mdb_warn("Is the driver loaded ?\n"); return (DCMD_ERR); } mdb_printf("----------------- mdi_vhci_head @ %#lr ----------\n", buf); mdi_vhci_value = (mdi_vhci_t *)mdb_alloc(mdi_vhci_len, UM_SLEEP|UM_GC); if (mdb_vread(mdi_vhci_value, mdi_vhci_len, buf) != mdi_vhci_len) { mdb_warn("vhciguid: Failed read on %l#r\n", mdi_vhci_value); mdb_free(mdi_vhci_value, mdi_vhci_len); return (DCMD_ERR); } temp = (uintptr_t)mdi_vhci_value->vh_class; class = (char *)mdb_alloc(len, UM_SLEEP|UM_GC); if (mdb_vread(class, strlen(MDI_HCI_CLASS_SCSI), temp) != strlen(MDI_HCI_CLASS_SCSI)) { mdb_warn("vhciguid: Failed read of class %l#r\n", mdi_vhci_value); mdb_free(mdi_vhci_value, mdi_vhci_len); mdb_free(class, len); return (DCMD_ERR); } class[len] = 0; mdb_printf("----------------- class @ %s----------\n", class); while (class) { if (strcmp(class, MDI_HCI_CLASS_SCSI) == 0) { found = 1; break; } if (mdi_vhci_value->vh_next == NULL) { break; } temp = (uintptr_t)mdi_vhci_value->vh_next; if (mdb_vread(mdi_vhci_value, mdi_vhci_len, temp) != mdi_vhci_len) { mdb_warn("vhciguid: Failed read on vh->next %l#r\n", mdi_vhci_value); break; } temp = (uintptr_t)mdi_vhci_value->vh_class; if (mdb_vread(class, strlen(MDI_HCI_CLASS_SCSI), temp) != strlen(MDI_HCI_CLASS_SCSI)) { mdb_warn("vhciguid: Failed read on vh->next %l#r\n", mdi_vhci_value); break; } class[len] = 0; } if (found == 0) { mdb_warn("vhciguid: No scsi_vhci class found"); mdb_free(mdi_vhci_value, mdi_vhci_len); mdb_free(class, len); return (DCMD_ERR); } mdb_printf("----- Number of devices found %d ----------\n", mdi_vhci_value->vh_client_count); for (i = 0; i < CLIENT_HASH_TABLE_SIZE; i++) { ct_hash_table_val = &mdi_vhci_value->vh_client_table[i]; if (ct_hash_table_val == NULL) continue; /* Read client_hash structure */ ct_hash_val = (struct client_hash *)mdb_alloc(ct_hash_len, UM_SLEEP|UM_GC); temp = (uintptr_t)ct_hash_table_val; if (mdb_vread(ct_hash_val, ct_hash_len, temp) != ct_hash_len) { mdb_warn("Failed read on hash %l#r\n", ct_hash_val); break; } mdb_printf("----hash[%d] %l#r: devices mapped = %d --\n", i, ct_hash_table_val, ct_hash_val->ct_hash_count); if (ct_hash_val->ct_hash_count == 0) { continue; } ct_hash_count = ct_hash_val->ct_hash_count; /* Read mdi_client structures */ mdi_client_value = (mdi_client_t *)mdb_alloc(mdi_client_len, UM_SLEEP|UM_GC); temp = (uintptr_t)ct_hash_val->ct_hash_head; if (mdb_vread(mdi_client_value, mdi_client_len, temp) != mdi_client_len) { mdb_warn("Failed read on client %l#r\n", mdi_client_value); break; } mdb_printf("mdi_client %l#r %l#r ------\n", mdi_client_value, mdi_client_value->ct_vprivate); vhcilun((uintptr_t)mdi_client_value->ct_vprivate, DCMD_ADDRSPEC, 0, 0); while (--ct_hash_count) { temp = (uintptr_t)mdi_client_value->ct_hnext; if (mdb_vread(mdi_client_value, mdi_client_len, temp) != mdi_client_len) { mdb_warn("Failed read on client %l#r\n", mdi_client_value); break; } vhcilun((uintptr_t)mdi_client_value->ct_vprivate, DCMD_ADDRSPEC, 0, 0); } } mdb_printf("----------done----------\n"); return (DCMD_OK); } /* * Print the flag name by comparing flags to the mask variable. */ static void dump_flags(unsigned long long flags, char **strings) { int i, linel = 8, first = 1; unsigned long long mask = 1; for (i = 0; i < 64; i++) { if (strings[i] == NULL) break; if (flags & mask) { if (!first) { mdb_printf(" | "); } else { first = 0; } /* make output pretty */ linel += strlen(strings[i]) + 3; if (linel > 80) { mdb_printf("\n\t"); linel = strlen(strings[i]) + 1 + 8; } mdb_printf("%s", strings[i]); } mask <<= 1; } mdb_printf("\n"); } /* ARGSUSED */ static int vhci_states(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) { return (i_vhci_states(addr, flags, argc, argv, NULL)); } /* * dump_states() * * Print the state information for vhci_states(). */ static int dump_states(uintptr_t array_vaddr, int verbose, struct i_ddi_soft_state *sp) { int i; int array_size; struct i_ddi_soft_state *ss; struct scsi_vhci vhci; if (sp == NULL) { ss = (struct i_ddi_soft_state *)mdb_alloc(sizeof (*ss), UM_SLEEP|UM_GC); } else { ss = sp; } if (mdb_vread(ss, sizeof (*ss), array_vaddr) != sizeof (*ss)) { mdb_warn("Cannot read softstate struct (Invalid pointer?).\n"); return (DCMD_ERR); } array_size = ss->n_items * (sizeof (void *)); array_vaddr = (uintptr_t)ss->array; ss->array = mdb_alloc(array_size, UM_SLEEP|UM_GC); if (mdb_vread(ss->array, array_size, array_vaddr) != array_size) { mdb_warn("Corrupted softstate struct.\n"); return (DCMD_ERR); } if (sp != NULL) return (DCMD_OK); if (verbose) { /* * ss->size is of type size_t which is 4 bytes and 8 bytes * on 32-bit and 64-bit systems respectively. */ #ifdef _LP64 mdb_printf("Softstate size is %lld(0x%llx) bytes.\n\n", ss->size, ss->size); #else mdb_printf("Softstate size is %ld(0x%lx) bytes.\n\n", ss->size, ss->size); #endif mdb_printf("state pointer\t\t\t\t\tinstance\n"); mdb_printf("=============\t\t\t\t\t========\n"); } for (i = 0; i < ss->n_items; i++) { if (ss->array[i] == 0) continue; if (mdb_vread(&vhci, sizeof (vhci), (uintptr_t)ss->array[i]) != sizeof (vhci)) { mdb_warn("Corrupted softstate struct.\n"); return (DCMD_ERR); } if (verbose) { mdb_printf("%l#r::print struct scsi_vhci\t\t %d\n", ss->array[i], i); mdb_printf("\nvhci_conf_flags: %d\n", vhci.vhci_conf_flags); if (vhci.vhci_conf_flags) { mdb_printf("\t"); dump_flags((unsigned long long) vhci.vhci_conf_flags, vhci_conf_flags); } } else { mdb_printf("%l#r\n", ss->array[i]); } } return (DCMD_OK); } static int get_mdbstr(uintptr_t addr, char *string_val) { if (mdb_readstr(string_val, MAXNAMELEN, addr) == -1) { mdb_warn("Error Reading String from %l#r\n", addr); return (1); } return (0); } static void dump_state_str(char *name, uintptr_t addr, char **strings) { mdb_printf("%s: %s (%l#r)\n", name, strings[(unsigned long)addr], addr); } /* VHCI UTILS */ /* * i_vhci_states() * * Internal routine for vhci_states() to check for -v arg and then * print state info. */ /* ARGSUSED */ static int i_vhci_states(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv, struct i_ddi_soft_state *sp) { uintptr_t adr; int verbose = 0; if (mdb_readvar(&adr, "vhci_softstate") == -1) { mdb_warn("vhci driver variable vhci_softstate not found.\n"); mdb_warn("Is the driver loaded ?\n"); return (DCMD_ERR); } if (sp == NULL) { if (mdb_getopts(argc, argv, 'v', MDB_OPT_SETBITS, TRUE, &verbose, NULL) != argc) { return (DCMD_USAGE); } } return (dump_states(adr, verbose, sp)); } /* * i_vhcilun() * * Internal routine for vhciguid() to print client info. */ static int i_vhcilun(uintptr_t addr, uint_t display_single_guid, char *guid) { scsi_vhci_lun_t value; struct dev_info dev_info_value; char string_val[MAXNAMELEN]; int found = 0; struct mdi_client ct_value; uintptr_t temp_addr; do { if (mdb_vread(&value, sizeof (scsi_vhci_lun_t), addr) != sizeof (scsi_vhci_lun_t)) { mdb_warn("sv_lun: Failed read on %l#r", addr); return (DCMD_ERR); } temp_addr = addr; addr = (uintptr_t)value.svl_hash_next; if (!get_mdbstr((uintptr_t)value.svl_lun_wwn, string_val)) { if (display_single_guid) { if (strcmp(string_val, guid) == 0) { found = 1; } else continue; } } mdb_printf("%t%l#r::print struct scsi_vhci_lun", temp_addr); if (mdb_vread(&dev_info_value, sizeof (struct dev_info), (uintptr_t)value.svl_dip) != sizeof (struct dev_info)) { mdb_warn("svl_dip: Failed read on %l#r", value.svl_dip); return (DCMD_ERR); } mdb_printf("\n%tGUID: %s\n", string_val); if (value.svl_active_pclass != NULL) { if (!get_mdbstr((uintptr_t)value.svl_active_pclass, string_val)) { mdb_printf("%tActv_cl: %s", string_val); } } else { mdb_printf(" No active pclass"); } if (display_single_guid) { mdb_printf(" (%l#r)", value.svl_active_pclass); } mdb_printf("\n%t%l#r::print struct mdi_client", dev_info_value.devi_mdi_client); if (value.svl_flags) { mdb_printf("\t"); dump_flags((unsigned long long)value.svl_flags, svlun_flags); } else { mdb_printf("\n"); } if (found) { mdiclient((uintptr_t)dev_info_value.devi_mdi_client, DCMD_ADDRSPEC, 0, 0); } else { if (mdb_vread(&ct_value, sizeof (struct mdi_client), (uintptr_t)dev_info_value.devi_mdi_client) != sizeof (struct mdi_client)) { mdb_warn("mdiclient: Failed read on %l#r", dev_info_value.devi_mdi_client); return (DCMD_ERR); } if (ct_value.ct_flags) { mdb_printf("\t"); dump_flags((unsigned long long) ct_value.ct_flags, client_flags); } mdb_printf("%t"); dump_state_str("LB", ct_value.ct_lb, client_lb_str); mdb_printf("\n"); } } while (addr && !found); return (DCMD_OK); } static void dump_mutex(kmutex_t m, char *name) { mdb_printf("%s is%s held\n", name, FT(m, uint64_t) == 0 ? " not" : ""); } static void dump_condvar(kcondvar_t c, char *name) { mdb_printf("Threads sleeping on %s = %d\n", name, (int)FT(c, ushort_t)); } static void dump_string(uintptr_t addr, char *name) { char string_val[MAXNAMELEN]; if (get_mdbstr(addr, string_val)) { return; } mdb_printf("%s: %s (%l#r)\n", name, string_val, addr); } /* ARGSUSED */ static int mpxio_walk_cb(uintptr_t addr, const void *data, void *cbdata) { mdb_printf("%t%l#r%s\n", addr, (char *)cbdata); return (0); }