/* * 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 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #include #include #include #include #include #include #include #include #include #include #include #include #include "cmd_options.h" static int stmf_ilport_walk_i(mdb_walk_state_t *wsp) { if (wsp->walk_addr == NULL) { struct stmf_state state; if (mdb_readsym(&state, sizeof (struct stmf_state), "stmf_state") == -1) { mdb_warn("failed to read stmf_state"); return (WALK_ERR); } wsp->walk_addr = (uintptr_t)state.stmf_ilportlist; } wsp->walk_data = mdb_alloc(sizeof (stmf_i_local_port_t), UM_SLEEP); return (WALK_NEXT); } static int stmf_ilport_walk_s(mdb_walk_state_t *wsp) { int status = WALK_NEXT; if (wsp->walk_addr == NULL) return (WALK_DONE); if (mdb_vread(wsp->walk_data, sizeof (struct stmf_i_local_port), wsp->walk_addr) == -1) { mdb_warn("failed to read stmf_i_local_port_t at %p", wsp->walk_addr); return (WALK_ERR); } if (wsp->walk_callback) status = wsp->walk_callback(wsp->walk_addr, wsp->walk_data, wsp->walk_cbdata); wsp->walk_addr = (uintptr_t) (((struct stmf_i_local_port *)wsp->walk_data)->ilport_next); return (status); } static void stmf_ilport_walk_f(mdb_walk_state_t *wsp) { mdb_free(wsp->walk_data, sizeof (struct stmf_i_local_port)); } static int dump_ilport(struct stmf_i_local_port *ilportp, int verbose) { if (ilportp == NULL) return (DCMD_OK); mdb_printf("%p\n", ilportp); if (verbose) { /* here assume the alias is maximumly 1024 bytes */ char alias[255]; struct stmf_local_port lport; struct stmf_i_local_port ilport; if (mdb_vread(&ilport, sizeof (ilport), (uintptr_t)ilportp) == -1) { mdb_warn("failed to read stmf_i_local_port at %p", ilportp); return (DCMD_ERR); } memset(alias, 0, sizeof (alias)); if (mdb_vread(&lport, sizeof (lport), (uintptr_t)ilport.ilport_lport) == -1) { mdb_warn("failed to read stmf_local_port at %p", ilport.ilport_lport); return (DCMD_ERR); } if (lport.lport_alias && mdb_vread(alias, sizeof (alias), (uintptr_t)lport.lport_alias) == -1) { mdb_warn("failed to read memory at %p", lport.lport_alias); return (DCMD_ERR); } mdb_printf(" lport: %p\n", ilport.ilport_lport); if (lport.lport_alias) mdb_printf(" port alias: %s\n", alias); mdb_printf(" port provider: %p\n", lport.lport_pp); } return (DCMD_OK); } /*ARGSUSED*/ static int stmf_ilports(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) { int i; int verbose = 0; mdb_walk_state_t ws = {NULL, }; for (i = 0; i < argc; i++) { char *ptr = (char *)argv[i].a_un.a_str; if (ptr[0] == '-') ptr++; while (*ptr) { if (*ptr == 'v') verbose = 1; ptr++; } } if (stmf_ilport_walk_i(&ws) == WALK_ERR) return (DCMD_ERR); dump_ilport((stmf_i_local_port_t *)ws.walk_addr, verbose); while (stmf_ilport_walk_s(&ws) == WALK_NEXT) dump_ilport((stmf_i_local_port_t *)ws.walk_addr, verbose); stmf_ilport_walk_f(&ws); return (DCMD_OK); } struct stmf_i_local_port * next_stmf_port(mdb_walk_state_t *wsp) { if (wsp->walk_addr == NULL) { if (stmf_ilport_walk_i(wsp) == WALK_ERR) { stmf_ilport_walk_f(wsp); return (NULL); } if (wsp->walk_addr == NULL) stmf_ilport_walk_f(wsp); return ((struct stmf_i_local_port *)wsp->walk_addr); } if (stmf_ilport_walk_s(wsp) == WALK_ERR) { stmf_ilport_walk_f(wsp); return (NULL); } if (wsp->walk_addr == NULL) stmf_ilport_walk_f(wsp); return ((struct stmf_i_local_port *)wsp->walk_addr); } /*ARGSUSED*/ static int stmf_iss(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) { struct stmf_i_local_port iport; struct stmf_i_scsi_session *issp; struct stmf_i_scsi_session iss; int i; int verbose = 0; for (i = 0; i < argc; i++) { char *ptr = (char *)argv[i].a_un.a_str; if (ptr[0] == '-') ptr++; while (*ptr) { if (*ptr == 'v') verbose = 1; ptr++; } } if (addr == NULL) { mdb_warn("address of stmf_i_local_port should be specified\n"); return (DCMD_ERR); } /* * Input should be stmf_i_local_port_t. */ if (mdb_vread(&iport, sizeof (struct stmf_i_local_port), addr) != sizeof (struct stmf_i_local_port)) { mdb_warn("Unable to read in stmf_i_local_port at %p\n", addr); return (DCMD_ERR); } issp = iport.ilport_ss_list; while (issp) { if (mdb_vread(&iss, sizeof (iss), (uintptr_t)issp) == -1) { mdb_warn("failed to read stmf_i_scsi_session_t at %p", issp); return (DCMD_ERR); } mdb_printf("%p\n", issp); if (verbose) { mdb_printf(" scsi session: %p\n", iss.iss_ss); } issp = iss.iss_next; } return (DCMD_OK); } /*ARGSUSED*/ static int stmf_ilus(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) { struct stmf_state state; struct stmf_i_lu ilu; struct stmf_i_lu *ilup; int i; int verbose = 0; for (i = 0; i < argc; i++) { char *ptr = (char *)argv[i].a_un.a_str; if (ptr[0] == '-') ptr++; while (*ptr) { if (*ptr == 'v') verbose = 1; ptr++; } } if (mdb_readsym(&state, sizeof (struct stmf_state), "stmf_state") == -1) { mdb_warn("failed to read stmf_state"); return (DCMD_ERR); } ilup = state.stmf_ilulist; while (ilup) { if (mdb_vread(&ilu, sizeof (struct stmf_i_lu), (uintptr_t)ilup) == -1) { mdb_warn("failed to read stmf_i_lu_t at %p", ilup); return (DCMD_ERR); } mdb_printf("%p\n", ilup); if (verbose) { mdb_printf(" lu: %p\n", ilu.ilu_lu); /* XXX lu_alias? what is its size? */ } ilup = ilu.ilu_next; } return (DCMD_OK); } /*ARGSUSED*/ static int stmf_i_lu_providers(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) { struct stmf_state state; struct stmf_i_lu_provider ilp; struct stmf_i_lu_provider *ilpp; int i; int verbose = 0; for (i = 0; i < argc; i++) { char *ptr = (char *)argv[i].a_un.a_str; if (ptr[0] == '-') ptr++; while (*ptr) { if (*ptr == 'v') verbose = 1; ptr++; } } if (mdb_readsym(&state, sizeof (struct stmf_state), "stmf_state") == -1) { mdb_warn("failed to read stmf_state"); return (DCMD_ERR); } ilpp = state.stmf_ilplist; while (ilpp) { if (mdb_vread(&ilp, sizeof (stmf_i_lu_provider_t), (uintptr_t)ilpp) == -1) { mdb_warn("failed to read stmf_i_lu_provider_t at %p", ilpp); return (DCMD_ERR); } mdb_printf("%p\n", ilpp); if (verbose) { mdb_printf(" lu provider: %p\n", ilp.ilp_lp); } ilpp = ilp.ilp_next; } return (DCMD_OK); } /*ARGSUSED*/ static int stmf_i_port_providers(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) { struct stmf_state state; struct stmf_i_port_provider ipp; struct stmf_i_port_provider *ippp; int i; int verbose = 0; for (i = 0; i < argc; i++) { char *ptr = (char *)argv[i].a_un.a_str; if (ptr[0] == '-') ptr++; while (*ptr) { if (*ptr == 'v') verbose = 1; ptr++; } } if (mdb_readsym(&state, sizeof (struct stmf_state), "stmf_state") == -1) { mdb_warn("failed to read stmf_state"); return (DCMD_ERR); } ippp = state.stmf_ipplist; while (ippp) { if (mdb_vread(&ipp, sizeof (stmf_i_port_provider_t), (uintptr_t)ippp) == -1) { mdb_warn("failed to read stmf_i_port_provider_t at %p", ippp); return (DCMD_ERR); } mdb_printf("%p\n", ippp); if (verbose) { mdb_printf(" port provider: %p\n", ipp.ipp_pp); } ippp = ipp.ipp_next; } return (DCMD_OK); } int string2wwn(const char *s, uint8_t wwn[8]); static uint16_t port_max_logins; static int rp_index; /* * Cervert stmf_i_local_port to fct_i_local_port */ /*ARGSUSED*/ static struct fct_i_local_port * __ilport2iport(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) { struct stmf_i_local_port iport; struct stmf_local_port lport; struct fct_local_port fport; if (!(flags & DCMD_ADDRSPEC)) { mdb_warn("stmf_i_local_port address should be specified"); return (NULL); } /* * Input should be stmf_i_local_port_t. */ if (mdb_vread(&iport, sizeof (struct stmf_i_local_port), addr) != sizeof (struct stmf_i_local_port)) { mdb_warn("Unable to read in stmf_i_local_port\n"); return (NULL); } if (mdb_vread(&lport, sizeof (stmf_local_port_t), (uintptr_t)iport.ilport_lport) != sizeof (stmf_local_port_t)) { mdb_warn("Unable to read in stmf_local_port\n"); return (NULL); } if (mdb_vread(&fport, sizeof (fct_local_port_t), (uintptr_t)lport.lport_port_private) != sizeof (fct_local_port_t)) { mdb_warn("Unable to read in fct_local_port\n"); return (NULL); } return (fport.port_fct_private); } static int ilport2iport(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) { struct fct_i_local_port *iportp; int i; int verbose = 0; for (i = 0; i < argc; i++) { char *ptr = (char *)argv[i].a_un.a_str; if (ptr[0] == '-') ptr++; while (*ptr) { if (*ptr == 'v') verbose = 1; ptr++; } } iportp = __ilport2iport(addr, flags, argc, argv); if (iportp) { mdb_printf("%p\n", iportp); if (verbose) { struct fct_i_local_port iport; /* is the alias always 16 bytes in size ? */ char alias[16]; memset(alias, 0, sizeof (alias)); if (mdb_vread(&iport, sizeof (fct_i_local_port_t), (uintptr_t)iportp) != sizeof (fct_i_local_port_t)) { mdb_warn("Unable to read in fct_i_local_port" "at %p\n", iportp); return (DCMD_ERR); } if (iport.iport_alias && mdb_vread(alias, sizeof (alias), (uintptr_t)iport.iport_alias) != sizeof (alias)) { mdb_warn("Unable to read in memory at %p", iport.iport_alias); return (DCMD_ERR); } mdb_printf(" port: %p\n", iport.iport_port); if (iport.iport_alias) mdb_printf(" alias: %s\n", alias); } } return (DCMD_OK); } /* * by wwn, we can only find one local port */ static struct stmf_i_local_port * find_lport_by_wwn(uint8_t wwn[8]) { struct stmf_i_local_port *siport; struct fct_i_local_port *fiport; struct fct_i_local_port iport; struct fct_local_port fport; mdb_walk_state_t ws = {NULL, }; while ((siport = next_stmf_port(&ws)) != NULL) { fiport = __ilport2iport((uintptr_t)siport, DCMD_ADDRSPEC, 0, NULL); if (fiport == NULL) return (NULL); if (mdb_vread(&iport, sizeof (fct_i_local_port_t), (uintptr_t)fiport) != sizeof (fct_i_local_port_t)) { mdb_warn("Unable to read in fct_i_local_port\n"); return (NULL); } if (mdb_vread(&fport, sizeof (fct_local_port_t), (uintptr_t)iport.iport_port) != sizeof (fct_local_port_t)) { mdb_warn("Unable to read in fct_local_port\n"); return (NULL); } #if 0 mdb_printf("pwwn=%02x%02x%02x%02x%02x%02x%02x%02x\n", fport.port_pwwn[0], fport.port_pwwn[1], fport.port_pwwn[2], fport.port_pwwn[3], fport.port_pwwn[4], fport.port_pwwn[5], fport.port_pwwn[6], fport.port_pwwn[7]); #endif if (memcmp(fport.port_pwwn, wwn, 8) == 0) { return (siport); } } return (NULL); } /*ARGSUSED*/ static int stmf_find_ilport(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) { struct find_options *options; struct stmf_i_local_port *siport; options = parse_options(argc, argv); /* need to free options manually ? */ if (options == NULL || ! options->lpname_defined) { mdb_printf("lpname= " "should be specified\n"); return (DCMD_OK); } if ((siport = find_lport_by_wwn(options->lpname)) != NULL) mdb_printf("%p\n", siport); return (DCMD_OK); } static int fct_irp_walk_i(mdb_walk_state_t *wsp) { struct fct_local_port port; struct fct_i_local_port iport; if (wsp->walk_addr == NULL) { mdb_warn("Can not perform global walk"); return (WALK_ERR); } /* * Input should be fct_i_local_port_t. */ if (mdb_vread(&iport, sizeof (struct fct_i_local_port), wsp->walk_addr) != sizeof (struct fct_i_local_port)) { mdb_warn("Unable to read in fct_i_local_port\n"); return (WALK_ERR); } if (mdb_vread(&port, sizeof (struct fct_local_port), (uintptr_t)iport.iport_port) != sizeof (struct fct_local_port)) { mdb_warn("Unable to read in fct_local_port\n"); return (WALK_ERR); } port_max_logins = port.port_max_logins; rp_index = 0; wsp->walk_addr = (uintptr_t)iport.iport_rp_slots; return (WALK_NEXT); } static int fct_irp_walk_s(mdb_walk_state_t *wsp) { int status = WALK_NEXT; fct_i_remote_port_t *rp; if (wsp->walk_addr == NULL) return (WALK_DONE); if (rp_index++ >= port_max_logins) return (WALK_DONE); if (mdb_vread(&rp, sizeof (fct_i_remote_port_t *), wsp->walk_addr) == -1) { mdb_warn("failed to read address of fct_i_remote_port_t at %p", wsp->walk_addr); return (WALK_DONE); } if (rp != NULL && wsp->walk_callback != NULL) status = wsp->walk_callback((uintptr_t)rp, rp, wsp->walk_cbdata); wsp->walk_addr = (uintptr_t) &(((fct_i_remote_port_t **)wsp->walk_addr)[1]); return (status); } static void fct_irp_walk_f(mdb_walk_state_t *wsp) { wsp->walk_addr = NULL; } /* * to set remote_port */ /*ARGSUSED*/ static int walk_fct_irp_cb(uintptr_t p, const void * arg, void *cbdata) { *((uintptr_t *)cbdata) = p; return (WALK_NEXT); } static int fct_irps(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) { static uint64_t cbdata = 0; mdb_walk_state_t ws = {walk_fct_irp_cb, &cbdata, addr}; fct_i_remote_port_t *irpp; int i; int verbose = 0; for (i = 0; i < argc; i++) { char *ptr = (char *)argv[i].a_un.a_str; if (ptr[0] == '-') ptr++; while (*ptr) { if (*ptr == 'v') verbose = 1; ptr++; } } if (!(flags & DCMD_ADDRSPEC)) { mdb_warn("fct_i_local_port_t address should be specified"); return (DCMD_ERR); } fct_irp_walk_i(&ws); while (fct_irp_walk_s(&ws) == WALK_NEXT) { irpp = *((fct_i_remote_port_t **)ws.walk_cbdata); if (irpp) { *((fct_i_remote_port_t **)ws.walk_cbdata) = NULL; mdb_printf("%p\n", irpp); if (verbose) { fct_i_remote_port_t irp; if (mdb_vread(&irp, sizeof (irp), (uintptr_t)irpp) != sizeof (irp)) { mdb_warn("Unable to read in " "fct_i_remote_port at %p\n", irpp); return (DCMD_ERR); } mdb_printf(" remote port: %p\n", irp.irp_rp); mdb_printf(" port id: %x\n", irp.irp_portid); } } } fct_irp_walk_f(&ws); return (DCMD_OK); } static uintptr_t cur_iport_for_irp_loop = NULL; static fct_i_remote_port_t * next_rport(struct fct_i_local_port *iport) { static uint64_t cbdata = 0; static mdb_walk_state_t ws = {walk_fct_irp_cb, &cbdata}; int ret; fct_i_remote_port_t *irp; if (ws.walk_addr == NULL || cur_iport_for_irp_loop != (uintptr_t)iport) { *((fct_i_remote_port_t **)ws.walk_cbdata) = NULL; cur_iport_for_irp_loop = (uintptr_t)iport; ws.walk_addr = (uintptr_t)iport; if (fct_irp_walk_i(&ws) == WALK_ERR) { fct_irp_walk_f(&ws); return (NULL); } if (ws.walk_addr == NULL) { fct_irp_walk_f(&ws); return (NULL); } } while ((ret = fct_irp_walk_s(&ws)) == WALK_NEXT) { if (*((fct_i_remote_port_t **)ws.walk_cbdata) != 0) { irp = *((fct_i_remote_port_t **)ws.walk_cbdata); *((fct_i_remote_port_t **)ws.walk_cbdata) = NULL; return (irp); } } fct_irp_walk_f(&ws); /* * If it is WALK_DONE, there may be one remote port there */ if (ret == WALK_DONE) { irp = *((fct_i_remote_port_t **)ws.walk_cbdata); *((fct_i_remote_port_t **)ws.walk_cbdata) = NULL; return (irp); } return (NULL); } static struct stmf_i_local_port * irp_to_ilport(struct fct_i_remote_port *irpp) { struct fct_i_remote_port irp; struct fct_remote_port rp; struct fct_local_port port; struct stmf_local_port lport; if (mdb_vread(&irp, sizeof (struct fct_i_remote_port), (uintptr_t)irpp) != sizeof (struct fct_i_remote_port)) { mdb_warn("Unable to read in fct_i_remote_port\n"); return (NULL); } if (mdb_vread(&rp, sizeof (struct fct_remote_port), (uintptr_t)irp.irp_rp) != sizeof (struct fct_remote_port)) { mdb_warn("Unable to read in fct_remote_port\n"); return (NULL); } if (mdb_vread(&port, sizeof (struct fct_local_port), (uintptr_t)rp.rp_port) != sizeof (struct fct_local_port)) { mdb_warn("Unable to read in fct_local_port\n"); return (NULL); } if (mdb_vread(&lport, sizeof (struct stmf_local_port), (uintptr_t)port.port_lport) != sizeof (struct stmf_local_port)) { mdb_warn("Unable to read in stmf_local_port\n"); return (NULL); } return (lport.lport_stmf_private); } /* * by wwn, we may find more than one remote port, so we need to know its * corresponding local port */ static struct fct_i_remote_port * find_irp_by_wwn(struct stmf_i_local_port *siport, uint8_t wwn[8]) { struct fct_i_local_port *fiport; fct_i_remote_port_t *irpp; struct fct_i_remote_port irp; struct fct_remote_port rp; fct_i_remote_port_t *ret = NULL; fiport = __ilport2iport((uintptr_t)siport, DCMD_ADDRSPEC, 0, NULL); if (fiport == NULL) return (NULL); while ((irpp = next_rport(fiport)) != NULL) { if (mdb_vread(&irp, sizeof (struct fct_i_remote_port), (uintptr_t)irpp) != sizeof (struct fct_i_remote_port)) { mdb_warn("Unable to read in fct_i_remote_port\n"); break; } if (mdb_vread(&rp, sizeof (struct fct_remote_port), (uintptr_t)irp.irp_rp) != sizeof (struct fct_remote_port)) { mdb_warn("Unable to read in fct_remote_port\n"); break; } if (memcmp(rp.rp_pwwn, wwn, 8) == 0) { ret = irpp; break; } } cur_iport_for_irp_loop = NULL; return (ret); } /*ARGSUSED*/ static int stmf_find_fct_irp(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) { struct stmf_i_local_port *siport; struct find_options *options; fct_i_remote_port_t *irpp; mdb_walk_state_t ws = {NULL, }; options = parse_options(argc, argv); /* need to free options manually ? */ if (options == NULL || (options->rpname_defined == 0 && options->rp_defined == 0)) { mdb_printf("rpname= or rp=<3000586778734>" " should be specified\n"); return (DCMD_OK); } if (options->rpname_defined && options->rp_defined) { mdb_printf("rpname= or rp=<3000586778734>" " should be specified, but not both\n"); return (DCMD_OK); } if (options->rp_defined) { siport = irp_to_ilport(options->rp); if (siport != NULL) mdb_printf("stmf_i_local_port=%p," " fct_i_remote_port=%p\n", siport, options->rp); return (DCMD_OK); } /* if options->rpname_defined */ while ((siport = next_stmf_port(&ws)) != NULL) { if ((irpp = find_irp_by_wwn(siport, options->rpname)) != NULL) mdb_printf("stmf_i_local_port=%p, " "fct_i_remote_port=%p\n", siport, irpp); } return (DCMD_OK); } typedef void (*cmd_filter_t) (struct fct_i_cmd *, struct find_options *, void *); /*ARGSUSED*/ static void print_tasks(struct fct_i_cmd *icmdp, struct find_options *options, void *arg) { struct fct_i_cmd icmd; struct fct_cmd cmd; if (mdb_vread(&icmd, sizeof (struct fct_i_cmd), (uintptr_t)icmdp) != sizeof (struct fct_i_cmd)) { mdb_warn("Unable to read in fct_i_cmd\n"); return; } if (mdb_vread(&cmd, sizeof (struct fct_cmd), (uintptr_t)icmd.icmd_cmd) != sizeof (struct fct_cmd)) { mdb_warn("Unable to read in fct_cmd\n"); return; } if (cmd.cmd_type == FCT_CMD_FCP_XCHG) { struct scsi_task task; int colon_printed = 0; if (mdb_vread(&task, sizeof (struct scsi_task), (uintptr_t)cmd.cmd_specific) != sizeof (struct scsi_task)) { mdb_warn("Unable to read in scsi_task\n"); return; } mdb_printf("%p", cmd.cmd_specific); if (options->show_task_flags) { mdb_printf(":"); colon_printed = 1; mdb_printf(" task_flags=%x", task.task_flags); } if (options->show_lport) { if (colon_printed == 0) { mdb_printf(":"); colon_printed = 1; } mdb_printf(" lport=%p", task.task_lport); } mdb_printf("\n"); } } static void print_tasks_on_rp(struct fct_i_cmd *icmdp, struct find_options *options, void *arg) { struct fct_i_cmd icmd; struct fct_cmd cmd; fct_i_remote_port_t irp; if (mdb_vread(&icmd, sizeof (struct fct_i_cmd), (uintptr_t)icmdp) != sizeof (struct fct_i_cmd)) { mdb_warn("Unable to read in fct_i_cmd\n"); return; } if (mdb_vread(&cmd, sizeof (struct fct_cmd), (uintptr_t)icmd.icmd_cmd) != sizeof (struct fct_cmd)) { mdb_warn("Unable to read in fct_cmd\n"); return; } /* arg is a pointer to fct_i_remote_port */ if (mdb_vread(&irp, sizeof (struct fct_i_remote_port), (uintptr_t)arg) != sizeof (struct fct_i_remote_port)) { mdb_warn("Unable to read in fct_i_remote_port\n"); return; } if (cmd.cmd_type == FCT_CMD_FCP_XCHG && cmd.cmd_rp == irp.irp_rp) { struct scsi_task task; int colon_printed = 0; if (mdb_vread(&task, sizeof (struct scsi_task), (uintptr_t)cmd.cmd_specific) != sizeof (struct scsi_task)) { mdb_warn("Unable to read in scsi_task\n"); return; } mdb_printf("%p", cmd.cmd_specific); if (options->show_task_flags) { mdb_printf(":"); colon_printed = 1; mdb_printf(" task_flags=%x", task.task_flags); } if (options->show_lport) { if (colon_printed == 0) { mdb_printf(":"); colon_printed = 1; } mdb_printf(" lport=%p", task.task_lport); } mdb_printf("\n"); } } /*ARGSUSED*/ static void print_all_cmds(struct fct_i_cmd *icmd, struct find_options *options, void *arg) { mdb_printf("%p\n", icmd); } /* * find outstanding cmds (fct_i_cmd) on local port */ static int outstanding_cmds_on_lport(struct stmf_i_local_port *siport, cmd_filter_t filter, struct find_options *options, void *arg) { struct fct_i_local_port *iportp; struct fct_i_local_port iport; struct fct_local_port port; struct fct_cmd_slot *slotp; struct fct_cmd_slot slot; int i; iportp = __ilport2iport((uintptr_t)siport, DCMD_ADDRSPEC, 0, NULL); if (iportp == NULL) return (DCMD_ERR); if (mdb_vread(&iport, sizeof (struct fct_i_local_port), (uintptr_t)iportp) != sizeof (struct fct_i_local_port)) { mdb_warn("Unable to read in fct_i_local_port\n"); return (DCMD_ERR); } if (mdb_vread(&port, sizeof (struct fct_local_port), (uintptr_t)iport.iport_port) != sizeof (struct fct_local_port)) { mdb_warn("Unable to read in fct_local_port\n"); return (DCMD_ERR); } slotp = iport.iport_cmd_slots; for (i = 0; i < port.port_max_xchges; i++) { if (mdb_vread(&slot, sizeof (struct fct_cmd_slot), (uintptr_t)slotp) != sizeof (struct fct_cmd_slot)) { mdb_warn("Unable to read in fct_cmd_slot\n"); return (DCMD_ERR); } if (slot.slot_cmd != NULL) { if (filter == NULL) mdb_printf("%p\n", slot.slot_cmd); else filter(slot.slot_cmd, options, arg); } slotp ++; } return (DCMD_OK); } /*ARGSUSED*/ static int stmf_find_tasks(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) { struct find_options *options; struct stmf_i_local_port *siport; options = parse_options(argc, argv); if (options == NULL || (options->lpname_defined == 0 && options->rpname_defined == 0)) { mdb_printf("lpname= or rpname=" " should be specified\n"); return (DCMD_OK); } if (options->lpname_defined) { siport = find_lport_by_wwn(options->lpname); if (siport == NULL) return (DCMD_ERR); outstanding_cmds_on_lport(siport, print_tasks, options, NULL); return (DCMD_OK); } if (options->rpname_defined) { mdb_walk_state_t ws = {NULL, }; fct_i_remote_port_t *irpp; while ((siport = next_stmf_port(&ws)) != NULL) { if ((irpp = find_irp_by_wwn(siport, options->rpname)) != NULL) { outstanding_cmds_on_lport(siport, print_tasks_on_rp, options, irpp); } } } return (DCMD_OK); } /*ARGSUSED*/ static int fct_find_cmds(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) { struct find_options *options; struct stmf_i_local_port *siport; options = parse_options(argc, argv); if (options == NULL || options->lpname_defined == 0) { mdb_printf("lpname= should be specified\n"); return (DCMD_OK); } siport = find_lport_by_wwn(options->lpname); if (siport == NULL) return (DCMD_ERR); outstanding_cmds_on_lport(siport, print_all_cmds, options, NULL); return (DCMD_OK); } static int fct_icmds(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) { struct fct_i_local_port iport; struct fct_i_cmd icmd; struct fct_i_cmd *icmdp; int i; int verbose = 0; for (i = 0; i < argc; i++) { char *ptr = (char *)argv[i].a_un.a_str; if (ptr[0] == '-') ptr++; while (*ptr) { if (*ptr == 'v') verbose = 1; ptr++; } } if (!(flags & DCMD_ADDRSPEC)) { mdb_warn("fct_i_local_port_t address should be specified"); return (DCMD_ERR); } if (mdb_vread(&iport, sizeof (struct fct_i_local_port), addr) != sizeof (struct fct_i_local_port)) { mdb_warn("Unable to read in fct_i_local_port at %p\n", addr); return (DCMD_ERR); } icmdp = iport.iport_cached_cmdlist; while (icmdp) { if (mdb_vread(&icmd, sizeof (struct fct_i_cmd), (uintptr_t)icmdp) == -1) { mdb_warn("failed to read fct_i_cmd at %p", icmdp); return (DCMD_ERR); } mdb_printf("%p\n", icmdp); if (verbose) { mdb_printf(" fct cmd: %p\n", icmd.icmd_cmd); } icmdp = icmd.icmd_next; } return (DCMD_OK); } /* * Walker to list the addresses of all the active STMF scsi tasks (scsi_task_t), * given a stmf_worker address * * To list all the active STMF scsi tasks, use * "::walk stmf_worker |::walk stmf_scsi_task" * To list the active tasks of a particular worker, use * ::walk stmf_scsi_task */ static int stmf_scsi_task_walk_init(mdb_walk_state_t *wsp) { stmf_worker_t worker; /* * Input should be a stmf_worker, so read it to get the * worker_task_head to get the start of the task list */ if (wsp->walk_addr == NULL) { mdb_warn("::walk stmf_scsi_task\n"); return (WALK_ERR); } if (mdb_vread(&worker, sizeof (stmf_worker_t), wsp->walk_addr) != sizeof (stmf_worker_t)) { mdb_warn("failed to read in the task address\n"); return (WALK_ERR); } wsp->walk_addr = (uintptr_t)(worker.worker_task_head); wsp->walk_data = mdb_alloc(sizeof (scsi_task_t), UM_SLEEP); return (WALK_NEXT); } static int stmf_scsi_task_walk_step(mdb_walk_state_t *wsp) { stmf_i_scsi_task_t itask; int status; if (wsp->walk_addr == NULL) { return (WALK_DONE); } /* Save the stmf_i_scsi_task for use later to get the next entry */ if (mdb_vread(&itask, sizeof (stmf_i_scsi_task_t), wsp->walk_addr) != sizeof (stmf_i_scsi_task_t)) { mdb_warn("failed to read stmf_i_scsi_task at %p", wsp->walk_addr); return (WALK_DONE); } wsp->walk_addr = (uintptr_t)itask.itask_task; if (mdb_vread(wsp->walk_data, sizeof (scsi_task_t), wsp->walk_addr) != sizeof (scsi_task_t)) { mdb_warn("failed to read scsi_task_t at %p", wsp->walk_addr); return (DCMD_ERR); } status = wsp->walk_callback(wsp->walk_addr, wsp->walk_data, wsp->walk_cbdata); wsp->walk_addr = (uintptr_t)(itask.itask_worker_next); return (status); } static void stmf_scsi_task_walk_fini(mdb_walk_state_t *wsp) { mdb_free(wsp->walk_data, sizeof (scsi_task_t)); } /*ARGSUSED*/ static int stmf_scsi_task(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) { stmf_worker_t worker; stmf_i_scsi_task_t itask; scsi_task_t *task_addr, task; /* * A stmf_worker address is given to the left of ::stmf_scsi_task * i.e. display the scsi_task for the given worker */ if (!(flags & DCMD_ADDRSPEC)) { if (mdb_walk_dcmd("stmf_worker", "stmf_scsi_task", argc, argv) == -1) { mdb_warn("Failed to walk the stmf_scsi_task entries"); return (DCMD_ERR); } return (DCMD_OK); } if (DCMD_HDRSPEC(flags) && (!(flags & DCMD_PIPE_OUT))) { mdb_printf("%%-19s %-10s %-19s%\n", "scsi_task_t", "Flags", "LPort"); } if (mdb_vread(&worker, sizeof (stmf_worker_t), addr) != sizeof (stmf_worker_t)) { mdb_warn("failed to read in the worker address"); return (DCMD_ERR); } /* Read the scsi_task */ if (worker.worker_task_head == NULL) { return (DCMD_OK); } if (mdb_vread(&itask, sizeof (stmf_i_scsi_task_t), (uintptr_t)worker.worker_task_head) == -1) { mdb_warn("failed to read stmf_i_scsi_task_t at %p", worker.worker_task_head); return (DCMD_ERR); } task_addr = itask.itask_task; if (mdb_vread(&task, sizeof (scsi_task_t), (uintptr_t)task_addr) != sizeof (scsi_task_t)) { mdb_warn("failed to read scsi_task_t at %p", task_addr); return (DCMD_ERR); } if ((flags & DCMD_PIPE_OUT)) { mdb_printf("%p\n", task_addr); } else { /* pretty print */ mdb_printf("%-19p %-10x %-19p\n", task_addr, task.task_flags, task.task_lport); } return (DCMD_OK); } /* * Walker to list the addresses of all the stmf_worker in the queue */ typedef struct stmf_worker_walk_data { int worker_current; int worker_count; } stmf_worker_walk_data_t; /* stmf_workers_state definition from stmf.c (static) */ enum { STMF_WORKERS_DISABLED = 0, STMF_WORKERS_ENABLING, STMF_WORKERS_ENABLED } stmf_workers_state; /* * Initialize the stmf_worker_t walker by either using the given starting * address, or reading the value of the kernel's global stmf_workers pointer. */ /*ARGSUSED*/ static int stmf_worker_walk_init(mdb_walk_state_t *wsp) { int worker_state; int nworkers; stmf_worker_t *worker; stmf_worker_walk_data_t *walk_data; if (mdb_readvar(&worker_state, "stmf_workers_state") == -1) { mdb_warn("failed to read stmf_workers_state"); return (WALK_ERR); } if (worker_state != STMF_WORKERS_ENABLED) { mdb_warn("stmf_workers_state not initialized"); return (WALK_ERR); } /* * Look up the stmf_nworkers_accepting_cmds to * determine number of entries in the worker queue */ if (mdb_readvar(&nworkers, "stmf_nworkers_accepting_cmds") == -1) { mdb_warn("failed to read stmf_nworkers_accepting_cmds"); return (WALK_ERR); } if (mdb_readvar(&worker, "stmf_workers") == -1) { mdb_warn("failed to read stmf_workers"); return (WALK_ERR); } walk_data = mdb_alloc(sizeof (stmf_worker_walk_data_t), UM_SLEEP); walk_data->worker_current = 0; walk_data->worker_count = nworkers; wsp->walk_addr = (uintptr_t)worker; wsp->walk_data = walk_data; return (WALK_NEXT); } static int stmf_worker_walk_step(mdb_walk_state_t *wsp) { stmf_worker_walk_data_t *walk_data = wsp->walk_data; int status; if (wsp->walk_addr == NULL) { return (WALK_DONE); } if (walk_data->worker_current >= walk_data->worker_count) { return (WALK_DONE); } status = wsp->walk_callback(wsp->walk_addr, wsp->walk_data, wsp->walk_cbdata); walk_data->worker_current++; wsp->walk_addr += sizeof (stmf_worker_t); return (status); } static void stmf_worker_walk_fini(mdb_walk_state_t *wsp) { mdb_free(wsp->walk_data, sizeof (stmf_worker_walk_data_t)); } int stmf_worker(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) { stmf_worker_t worker; if (!(flags & DCMD_ADDRSPEC)) { if (mdb_walk_dcmd("stmf_worker", "stmf_worker", argc, argv) == -1) { mdb_warn("Failed to walk the stmf_worker entries"); return (DCMD_ERR); } return (DCMD_OK); } if (mdb_vread(&worker, sizeof (stmf_worker_t), addr) != sizeof (stmf_worker_t)) { mdb_warn("failed to read stmf_worker at %p", addr); return (DCMD_ERR); } if (flags & DCMD_PIPE_OUT) { mdb_printf("%-19p\n", addr); } else { /* pretty print */ if (DCMD_HDRSPEC(flags)) { mdb_printf("%%-19s %-10s %-10s %-10s%\n", "stmf_worker_t", "State", "Ref_Count", "Tasks"); } mdb_printf("%-19p %-10s %-10d %-5d%\n", addr, (worker.worker_flags == STMF_WORKER_STARTED) ? "STARTED" : (worker.worker_flags == STMF_WORKER_ACTIVE) ? "ACTIVE" : "TERMINATED", worker.worker_ref_count, worker.worker_queue_depth); } return (DCMD_OK); } struct find_options * parse_options(int argc, const mdb_arg_t *argv) { int i; struct find_options *options; int len; char *ptr; int ret; if (argc == 0) return (NULL); options = mdb_zalloc(sizeof (struct find_options), UM_SLEEP); for (i = 0; i < argc; i++) { switch (argv[i].a_type) { case MDB_TYPE_STRING: break; case MDB_TYPE_IMMEDIATE: case MDB_TYPE_CHAR: mdb_printf("unknown type\n"); } if ((ptr = strchr(argv[i].a_un.a_str, '=')) == NULL) { mdb_printf("invalid argument: %s\n", argv[i].a_un.a_str); goto out; } len = ptr - argv[i].a_un.a_str; ptr++; /* point to value now */ if (len == strlen("lpname") && strncmp(argv[i].a_un.a_str, "lpname", len) == 0) { if (strstr(ptr, "wwn.") == ptr) ptr += 4; ret = string2wwn(ptr, options->lpname); if (ret == -1) goto out; #if 0 mdb_printf("wwn=%02x%02x%02x%02x%02x%02x%02x%02x\n", wwn[0], wwn[1], wwn[2], wwn[3], wwn[4], wwn[5], wwn[6], wwn[7]); #endif options->lpname_defined = 1; } else if (len == strlen("rp") && strncmp(argv[i].a_un.a_str, "rp", len) == 0) { options->rp_defined = 1; options->rp = (void *)(unsigned long)mdb_strtoull(ptr); } else if (len == strlen("rpname") && strncmp(argv[i].a_un.a_str, "rpname", len) == 0) { if (strstr(ptr, "wwn.") == ptr) ptr += 4; ret = string2wwn(ptr, options->rpname); if (ret == -1) goto out; options->rpname_defined = 1; } else if (len == strlen("show") && strncmp(argv[i].a_un.a_str, "show", len) == 0) { char *s; int l; for (;;) { s = strchr(ptr, ','); if (s) l = s - ptr; else l = strlen(ptr); if (l == strlen("task_flags") && strncmp(ptr, "task_flags", l) == 0) options->show_task_flags = 1; else if (l == strlen("lport") && strncmp(ptr, "lport", l) == 0) options->show_lport = 1; else { mdb_printf("unknown shower: %s\n", ptr); goto out; } if (s == NULL) break; ptr = s + 1; } } else { mdb_printf("unknown argument: %s\n", argv[i].a_un.a_str); goto out; } } return (options); out: mdb_free(options, sizeof (struct find_options)); return (NULL); } int string2wwn(const char *s, uint8_t wwn[8]) { int i; char tmp[17]; char *p; if (strlen(s) > 16) { mdb_printf("invalid wwn %s\n", s); return (-1); } strcpy(tmp, s); p = tmp + strlen(tmp) - 2; memset(wwn, 0, 8); /* figure out wwn from the tail to beginning */ for (i = 7; i >= 0 && p >= tmp; i--, p -= 2) { wwn[i] = mdb_strtoull(p); *p = 0; } return (0); } void fct_find_cmds_help(void) { mdb_printf( "Find all cached fct_i_cmd_t for a local port. If a local port \n" "name is specified, find all pending cmds for it and print the \n" "address. Example:\n" " fct_find_cmds lpname=\n"); } void stmf_find_ilport_help(void) { mdb_printf( "Find the fct_i_local_port if local port name is " "specified. Example:\n" " stmf_find_ilport lpname=\n"); } void stmf_find_fct_irp_help(void) { mdb_printf( "If a remote port name or stmf_i_remote_port_t address is\n" "specified, loop through all local ports, to which this remote \n" "port has logged in, print address for stmf_i_local_port_t and \n" "stmf_i_remote_port. Example:\n" " stmf_find_fct_irp rpname=\n" " stmf_find_fct_irp rp=<3000586778734>\n"); } void stmf_find_tasks_help(void) { mdb_printf( "Find all pending scsi_task_t for a given local port and/or\n" "remote port. Various different fields for each task are printed\n" "depending on what is requested. Example:\n" " stmf_find_tasks rpname=\n" " stmf_find_tasks lpname= " "show=task_flags,lport\n"); } void stmf_scsi_task_help(void) { mdb_printf( "List all active scsi_task_t on a given stmf_worker_t. Example\n" " addr::stmf_scsi_task\n"); } static const mdb_dcmd_t dcmds[] = { { "stmf_ilports", "[-v]", "Print a list of stmf_i_local_port", stmf_ilports }, { "ilport2iport", "?[-v]", "Convert stmf_i_local_port to corresponding fct_i_local_port", ilport2iport }, { "stmf_iss", "?[-v]", "List all active sessions for a given local port", stmf_iss }, { "stmf_ilus", "[-v]", "Print a list of stmf_i_lu", stmf_ilus }, { "stmf_i_lu_providers", "[-v]", "Print a list of stmf_i_lu_provider", stmf_i_lu_providers }, { "stmf_i_port_providers", "[-v]", "Print a list of stmf_i_port_provider", stmf_i_port_providers }, { "fct_irps", "?[-v]", "Print all fct_i_remote_port for a given fct_i_local_port", fct_irps }, { "fct_icmds", "?[-v]", "Print all cached fct_i_cmd_t on fct_i_local_port", fct_icmds }, { "fct_find_cmds", "lpname", "Find all fct_i_cmd_t for a given local port", fct_find_cmds, fct_find_cmds_help}, { "stmf_find_ilport", "lpname", "Find local port information based on its wwn", stmf_find_ilport, stmf_find_ilport_help}, { "stmf_find_fct_irp", "rpname|rp", "Print fct remote port information based on its wwn", stmf_find_fct_irp, stmf_find_fct_irp_help}, { "stmf_find_tasks", "lpname|rpname [show]", "Find all pending task for a local port or remote port", stmf_find_tasks, stmf_find_tasks_help}, { "stmf_worker", "?", "List all the stmf_worker entries", stmf_worker}, { "stmf_scsi_task", ":", "List all the active STMF SCSI tasks per worker", stmf_scsi_task, stmf_scsi_task_help}, { NULL } }; static const mdb_walker_t walkers[] = { { "stmf_worker", "Walk STMF worker queue", stmf_worker_walk_init, stmf_worker_walk_step, stmf_worker_walk_fini}, { "stmf_scsi_task", "Walk active STMF SCSI tasks per worker", stmf_scsi_task_walk_init, stmf_scsi_task_walk_step, stmf_scsi_task_walk_fini }, { NULL } }; static const mdb_modinfo_t modinfo = { MDB_API_VERSION, dcmds, walkers }; const mdb_modinfo_t * _mdb_init(void) { return (&modinfo); }