1 /*
   2  * CDDL HEADER START
   3  *
   4  * The contents of this file are subject to the terms of the
   5  * Common Development and Distribution License (the "License").
   6  * You may not use this file except in compliance with the License.
   7  *
   8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
   9  * or http://www.opensolaris.org/os/licensing.
  10  * See the License for the specific language governing permissions
  11  * and limitations under the License.
  12  *
  13  * When distributing Covered Code, include this CDDL HEADER in each
  14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  15  * If applicable, add the following below this CDDL HEADER, with the
  16  * fields enclosed by brackets "[]" replaced with your own identifying
  17  * information: Portions Copyright [yyyy] [name of copyright owner]
  18  *
  19  * CDDL HEADER END
  20  */
  21 /*
  22  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
  23  * Use is subject to license terms.
  24  */
  25 
  26 #include <sys/types.h>
  27 #include <sys/mdb_modapi.h>
  28 #include <sys/ddi.h>
  29 #include <sys/sunddi.h>
  30 #include <sys/sunldi.h>
  31 
  32 #include <sys/nsctl/nsctl.h>
  33 #include <sys/unistat/spcs_s.h>
  34 #include <sys/unistat/spcs_s_k.h>
  35 
  36 #include <sys/nsctl/sv.h>
  37 #include <sys/nsctl/sv_impl.h>
  38 
  39 #include <sys/nsctl/nsvers.h>
  40 
  41 /*
  42  * Walker for an array of sv_dev_t structures.
  43  * A global walk is assumed to start at sv_devs.
  44  */
  45 
  46 struct sv_dev_winfo {
  47         uintptr_t start;
  48         uintptr_t end;
  49 };
  50 
  51 
  52 static int
  53 sv_dev_winit(mdb_walk_state_t *wsp)
  54 {
  55         struct sv_dev_winfo *winfo;
  56         sv_dev_t *sv_devs;
  57         int sv_max_devices;
  58 
  59         winfo = mdb_zalloc(sizeof (struct sv_dev_winfo), UM_SLEEP);
  60 
  61         if (mdb_readvar(&sv_devs, "sv_devs") == -1) {
  62                 mdb_warn("failed to read 'sv_devs'");
  63                 mdb_free(winfo,  sizeof (struct sv_dev_winfo));
  64                 return (WALK_ERR);
  65         }
  66 
  67         if (mdb_readvar(&sv_max_devices, "sv_max_devices") == -1) {
  68                 mdb_warn("failed to read 'sv_max_devices'");
  69                 mdb_free(winfo, sizeof (struct sv_dev_winfo));
  70                 return (WALK_ERR);
  71         }
  72 
  73         winfo->start = (uintptr_t)sv_devs;
  74         winfo->end = (uintptr_t)(sv_devs + sv_max_devices);
  75 
  76         if (wsp->walk_addr == NULL)
  77                 wsp->walk_addr = winfo->start;
  78 
  79         wsp->walk_data = winfo;
  80         return (WALK_NEXT);
  81 }
  82 
  83 
  84 static int
  85 sv_dev_wstep(mdb_walk_state_t *wsp)
  86 {
  87         struct sv_dev_winfo *winfo = wsp->walk_data;
  88         int status;
  89 
  90         if (wsp->walk_addr == NULL)
  91                 return (WALK_DONE);
  92 
  93         if (wsp->walk_addr >= winfo->end)
  94                 return (WALK_DONE);
  95 
  96         status = wsp->walk_callback(wsp->walk_addr, wsp->walk_data,
  97             wsp->walk_cbdata);
  98 
  99         wsp->walk_addr += sizeof (sv_dev_t);
 100         return (status);
 101 }
 102 
 103 
 104 static void
 105 sv_dev_wfini(mdb_walk_state_t *wsp)
 106 {
 107         mdb_free(wsp->walk_data, sizeof (struct sv_dev_winfo));
 108 }
 109 
 110 
 111 /*
 112  * Walker for an sv hash chain.
 113  * Global walks are disallowed.
 114  */
 115 
 116 static int
 117 sv_hash_winit(mdb_walk_state_t *wsp)
 118 {
 119         if (wsp->walk_addr == NULL)
 120                 return (WALK_ERR);
 121 
 122         wsp->walk_data = mdb_zalloc(sizeof (sv_dev_t), UM_SLEEP);
 123 
 124         return (WALK_NEXT);
 125 }
 126 
 127 
 128 static int
 129 sv_hash_wstep(mdb_walk_state_t *wsp)
 130 {
 131         int status;
 132 
 133         if (wsp->walk_addr == NULL)
 134                 return (WALK_DONE);
 135 
 136         if (mdb_vread(wsp->walk_data,
 137             sizeof (sv_dev_t), wsp->walk_addr) == -1) {
 138                 mdb_warn("failed to read sv_dev at %p", wsp->walk_addr);
 139                 return (WALK_DONE);
 140         }
 141 
 142         status = wsp->walk_callback(wsp->walk_addr, wsp->walk_data,
 143             wsp->walk_cbdata);
 144 
 145         wsp->walk_addr = (uintptr_t)(((sv_dev_t *)wsp->walk_data)->sv_hash);
 146         return (status);
 147 }
 148 
 149 
 150 static void
 151 sv_hash_wfini(mdb_walk_state_t *wsp)
 152 {
 153         mdb_free(wsp->walk_data, sizeof (sv_dev_t));
 154 }
 155 
 156 
 157 /*
 158  * Walker for an array of sv_maj_t structures.
 159  * A global walk is assumed to start at sv_majors.
 160  */
 161 
 162 sv_maj_t *sv_majors[SV_MAJOR_HASH_CNT + 1] = {0};
 163 
 164 static int
 165 sv_maj_winit(mdb_walk_state_t *wsp)
 166 {
 167         if (wsp->walk_addr == NULL) {
 168                 if (mdb_readvar(&sv_majors, "sv_majors") == -1) {
 169                         mdb_warn("failed to read 'sv_majors'");
 170                         return (WALK_ERR);
 171                 }
 172         } else {
 173                 sv_majors[0] = (sv_maj_t *)wsp->walk_addr;
 174         }
 175 
 176         wsp->walk_addr = (uintptr_t)&sv_majors[0];
 177         wsp->walk_data = mdb_zalloc(sizeof (sv_maj_t), UM_SLEEP);
 178 
 179         return (WALK_NEXT);
 180 }
 181 
 182 
 183 static int
 184 sv_maj_wstep(mdb_walk_state_t *wsp)
 185 {
 186         uintptr_t addr;
 187         int status = DCMD_OK;
 188 
 189         if (wsp->walk_addr == NULL)
 190                 return (WALK_DONE);
 191 
 192         if (wsp->walk_addr >= (uintptr_t)&sv_majors[SV_MAJOR_HASH_CNT])
 193                 return (WALK_DONE);
 194 
 195         for (addr = *(uintptr_t *)wsp->walk_addr; addr;
 196                 addr = (uintptr_t)(((sv_maj_t *)wsp->walk_data)->sm_next)) {
 197 
 198                 if (mdb_vread(wsp->walk_data, sizeof (sv_maj_t), addr)
 199                                                         != sizeof (sv_maj_t)) {
 200                         mdb_warn("failed to read sv_maj at %p", addr);
 201                         status = DCMD_ERR;
 202                         break;
 203                 }
 204 
 205                 status = wsp->walk_callback(addr, wsp->walk_data,
 206                                                 wsp->walk_cbdata);
 207                 if (status != DCMD_OK)
 208                         break;
 209         }
 210 
 211         wsp->walk_addr += sizeof (sv_maj_t *);
 212         return (status);
 213 }
 214 
 215 
 216 static void
 217 sv_maj_wfini(mdb_walk_state_t *wsp)
 218 {
 219         mdb_free(wsp->walk_data, sizeof (sv_maj_t));
 220 }
 221 
 222 
 223 /*
 224  * Walker for an sv gclient chain.
 225  * A global walk is assumed to start at sv_gclients.
 226  */
 227 
 228 static int
 229 sv_gclient_winit(mdb_walk_state_t *wsp)
 230 {
 231         if (wsp->walk_addr == NULL &&
 232             mdb_readvar(&wsp->walk_addr, "sv_gclients") == -1) {
 233                 mdb_warn("unable to read 'sv_gclients'");
 234                 return (WALK_ERR);
 235         }
 236 
 237         wsp->walk_data = mdb_zalloc(sizeof (sv_gclient_t), UM_SLEEP);
 238 
 239         return (WALK_NEXT);
 240 }
 241 
 242 
 243 static int
 244 sv_gclient_wstep(mdb_walk_state_t *wsp)
 245 {
 246         int status;
 247 
 248         if (wsp->walk_addr == NULL)
 249                 return (WALK_DONE);
 250 
 251         if (mdb_vread(wsp->walk_data,
 252             sizeof (sv_gclient_t), wsp->walk_addr) == -1) {
 253                 mdb_warn("failed to read sv_gclient at %p", wsp->walk_addr);
 254                 return (WALK_DONE);
 255         }
 256 
 257         status = wsp->walk_callback(wsp->walk_addr, wsp->walk_data,
 258             wsp->walk_cbdata);
 259 
 260         wsp->walk_addr = (uintptr_t)(((sv_gclient_t *)wsp->walk_data)->sg_next);
 261         return (status);
 262 }
 263 
 264 
 265 static void
 266 sv_gclient_wfini(mdb_walk_state_t *wsp)
 267 {
 268         mdb_free(wsp->walk_data, sizeof (sv_gclient_t));
 269 }
 270 
 271 
 272 /*
 273  * Display a single sv_glcient_t structure.
 274  * If called with no address, performs a global walk of all sv_gclients.
 275  */
 276 static int
 277 sv_gclient(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
 278 {
 279         sv_gclient_t sg;
 280         char name[64];
 281 
 282         if (argc != 0)
 283                 return (DCMD_USAGE);
 284 
 285         if (!(flags & DCMD_ADDRSPEC)) {
 286                 /*
 287                  * paranoid mode on: qualify walker name with module name
 288                  * using '`' syntax.
 289                  */
 290                 if (mdb_walk_dcmd("sv`sv_gclient",
 291                     "sv`sv_gclient", argc, argv) == -1) {
 292                         mdb_warn("failed to walk 'sv_gclient'");
 293                         return (DCMD_ERR);
 294                 }
 295                 return (DCMD_OK);
 296         }
 297 
 298         if (mdb_vread(&sg, sizeof (sg), addr) != sizeof (sg)) {
 299                 mdb_warn("failed to read sv_gclient at %p", addr);
 300                 return (DCMD_ERR);
 301         }
 302 
 303         if (DCMD_HDRSPEC(flags)) {
 304                 mdb_printf("%-?s  %8T%-?s  %8T%-16s  %8T%s\n",
 305                     "ADDR", "NEXT", "ID", "NAME");
 306         }
 307 
 308         if (mdb_readstr(name, sizeof (name), (uintptr_t)sg.sg_name) == -1) {
 309                 mdb_warn("failed to read sv_gclient name at %p", addr);
 310                 return (DCMD_ERR);
 311         }
 312 
 313         mdb_printf("%p  %8T%p  %8T%llx  %8T%s",
 314             addr, sg.sg_next, sg.sg_id, name);
 315 
 316         return (DCMD_OK);
 317 }
 318 
 319 
 320 /*
 321  * Display a single sv_maj_t structure.
 322  * If called with no address, performs a global walk of all sv_majs.
 323  * -a : all (i.e. display all devices, even if disabled
 324  * -v : verbose
 325  */
 326 static int
 327 sv_maj(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
 328 {
 329         sv_maj_t *maj;
 330         int a_opt, v_opt;
 331         int i;
 332 
 333         a_opt = v_opt = FALSE;
 334 
 335         if (mdb_getopts(argc, argv,
 336             'a', MDB_OPT_SETBITS, TRUE, &a_opt,
 337             'v', MDB_OPT_SETBITS, TRUE, &v_opt) != argc)
 338                 return (DCMD_USAGE);
 339 
 340         if (!(flags & DCMD_ADDRSPEC)) {
 341                 /*
 342                  * paranoid mode on: qualify walker name with module name
 343                  * using '`' syntax.
 344                  */
 345                 if (mdb_walk_dcmd("sv`sv_maj", "sv`sv_maj", argc, argv) == -1) {
 346                         mdb_warn("failed to walk 'sv_maj'");
 347                         return (DCMD_ERR);
 348                 }
 349                 return (DCMD_OK);
 350         }
 351 
 352         if (DCMD_HDRSPEC(flags)) {
 353                 mdb_printf("%-?s  %8T%s\n", "ADDR", "INUSE");
 354         }
 355 
 356         maj = mdb_zalloc(sizeof (*maj), UM_GC);
 357         if (mdb_vread(maj, sizeof (*maj), addr) != sizeof (*maj)) {
 358                 mdb_warn("failed to read sv_maj at %p", addr);
 359                 return (DCMD_ERR);
 360         }
 361 
 362         if (!a_opt && maj->sm_inuse == 0)
 363                 return (DCMD_OK);
 364 
 365         mdb_printf("%?p  %8T%d\n", addr, maj->sm_inuse);
 366 
 367         if (!v_opt)
 368                 return (DCMD_OK);
 369 
 370         /*
 371          * verbose - print the rest of the structure as well.
 372          */
 373 
 374         mdb_inc_indent(4);
 375         mdb_printf("\n");
 376 
 377         mdb_printf("dev_ops: %a (%p)\n", maj->sm_dev_ops, maj->sm_dev_ops);
 378         mdb_printf("flag: %08x %8Tsequence: %d %8Tmajor: %d\n",
 379                 maj->sm_flag, maj->sm_seq, maj->sm_major);
 380 
 381         mdb_printf("function pointers:\n");
 382         mdb_inc_indent(4);
 383         mdb_printf("%-20a%-20a%\n%-20a%-20a%\n%-20a%-20a%\n%-20a%-20a%\n",
 384                 maj->sm_open, maj->sm_close,
 385                 maj->sm_read, maj->sm_write,
 386                 maj->sm_aread, maj->sm_awrite,
 387                 maj->sm_strategy, maj->sm_ioctl);
 388         mdb_dec_indent(4);
 389 
 390 
 391         mdb_printf("hash chain:\n");
 392         mdb_inc_indent(4);
 393         for (i = 0; i < SV_MINOR_HASH_CNT; i++) {
 394                 mdb_printf("%?p", maj->sm_hash[i]);
 395                 mdb_printf(((i % 4) == 3) ? "\n" : " %8T");
 396         }
 397         mdb_printf("\n\n");
 398         mdb_dec_indent(4);
 399         mdb_dec_indent(4);
 400         return (DCMD_OK);
 401 }
 402 
 403 
 404 /*
 405  * Display a sv_dev_t hash chain.
 406  * Requires an address.
 407  * Same options as sv_dev().
 408  */
 409 static int
 410 sv_hash(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
 411 {
 412         if (!(flags & DCMD_ADDRSPEC))
 413                 return (DCMD_USAGE);
 414 
 415         /*
 416          * paranoid mode on: qualify walker name with module name
 417          * using '`' syntax.
 418          */
 419         if (mdb_pwalk_dcmd("sv`sv_hash", "sv`sv_dev", argc, argv, addr) == -1) {
 420                 mdb_warn("failed to walk sv_dev hash chain");
 421                 return (DCMD_ERR);
 422         }
 423 
 424         return (DCMD_OK);
 425 }
 426 
 427 
 428 /*
 429  * Display a single sv_dev_t structure.
 430  * If called with no address, performs a global walk of all sv_devs.
 431  * -a : all (i.e. display all devices, even if disabled
 432  * -v : verbose
 433  */
 434 
 435 const mdb_bitmask_t sv_flag_bits[] = {
 436         { "NSC_DEVICE", NSC_DEVICE, NSC_DEVICE },
 437         { "NSC_CACHE", NSC_CACHE, NSC_CACHE },
 438         { NULL, 0, 0 }
 439 };
 440 
 441 static int
 442 sv_dev(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
 443 {
 444         sv_dev_t *svp;
 445         int a_opt, v_opt;
 446         int dev_t_chars;
 447 
 448         a_opt = v_opt = FALSE;
 449         dev_t_chars = sizeof (dev_t) * 2;       /* # chars to display dev_t */
 450 
 451         if (mdb_getopts(argc, argv,
 452             'a', MDB_OPT_SETBITS, TRUE, &a_opt,
 453             'v', MDB_OPT_SETBITS, TRUE, &v_opt) != argc)
 454                 return (DCMD_USAGE);
 455 
 456         svp = mdb_zalloc(sizeof (*svp), UM_GC);
 457 
 458         if (!(flags & DCMD_ADDRSPEC)) {
 459                 /*
 460                  * paranoid mode on: qualify walker name with module name
 461                  * using '`' syntax.
 462                  */
 463                 if (mdb_walk_dcmd("sv`sv_dev", "sv`sv_dev", argc, argv) == -1) {
 464                         mdb_warn("failed to walk 'sv_dev'");
 465                         return (DCMD_ERR);
 466                 }
 467                 return (DCMD_OK);
 468         }
 469 
 470         if (DCMD_HDRSPEC(flags)) {
 471                 mdb_printf("%-?s  %8T%-*s  %8T%s\n", "ADDR",
 472                     dev_t_chars, "DEV", "STATE");
 473         }
 474 
 475         if (mdb_vread(svp, sizeof (*svp), addr) != sizeof (*svp)) {
 476                 mdb_warn("failed to read sv_dev at %p", addr);
 477                 return (DCMD_ERR);
 478         }
 479 
 480         if (!a_opt && svp->sv_state == SV_DISABLE)
 481                 return (DCMD_OK);
 482 
 483         mdb_printf("%?p  %8T%0*lx  %8T", addr, dev_t_chars, svp->sv_dev);
 484 
 485         if (svp->sv_state == SV_DISABLE)
 486                 mdb_printf("disabled");
 487         else if (svp->sv_state == SV_PENDING)
 488                 mdb_printf("pending");
 489         else if (svp->sv_state == SV_ENABLE)
 490                 mdb_printf("enabled");
 491 
 492         mdb_printf("\n");
 493 
 494         if (!v_opt)
 495                 return (DCMD_OK);
 496 
 497         /*
 498          * verbose - print the rest of the structure as well.
 499          */
 500 
 501         mdb_inc_indent(4);
 502         mdb_printf("\n");
 503 
 504         mdb_printf("hash chain: 0x%p  %8Tlock: 0x%p  %8Tolock: 0x%p\n",
 505             svp->sv_hash,
 506             addr + OFFSETOF(sv_dev_t, sv_lock),
 507             addr + OFFSETOF(sv_dev_t, sv_olock));
 508 
 509         mdb_printf("fd: 0x%p  %8T\n", svp->sv_fd);
 510 
 511         mdb_printf("maxfbas: %d  %8Tnblocks: %d  %8Tstate: %d\n",
 512             svp->sv_maxfbas, svp->sv_nblocks, svp->sv_state);
 513 
 514         mdb_printf("gclients: 0x%llx  %8Tgkernel: 0x%llx\n",
 515             svp->sv_gclients, svp->sv_gkernel);
 516 
 517         mdb_printf("openlcnt: %d  %8Ttimestamp: 0x%lx\n",
 518             svp->sv_openlcnt, svp->sv_timestamp);
 519 
 520         mdb_printf("flags: 0x%08x <%b>\n",
 521             svp->sv_flag, svp->sv_flag, sv_flag_bits);
 522 
 523         mdb_printf("lh: 0x%p  %8Tpending: 0x%p\n",
 524             svp->sv_lh, svp->sv_pending);
 525 
 526         mdb_dec_indent(4);
 527         return (DCMD_OK);
 528 }
 529 
 530 
 531 /*
 532  * Display general sv module information.
 533  */
 534 
 535 #define sv_get_print(kvar, str, fmt, val)               \
 536         if (mdb_readvar(&(val), #kvar) == -1) {             \
 537                 mdb_dec_indent(4);                      \
 538                 mdb_warn("unable to read '" #kvar "'"); \
 539                 return (DCMD_ERR);                      \
 540         }                                               \
 541         mdb_printf("%-20s" fmt "\n", str ":", val)
 542 
 543 /* ARGSUSED */
 544 static int
 545 sv(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
 546 {
 547         clock_t clock;
 548         int maj, min, mic, baseline, i;
 549 
 550         if (argc != 0)
 551                 return (DCMD_USAGE);
 552 
 553         if (mdb_readvar(&maj, "sv_major_rev") == -1) {
 554                 mdb_warn("unable to read 'sv_major_rev'");
 555                 return (DCMD_ERR);
 556         }
 557 
 558         if (mdb_readvar(&min, "sv_minor_rev") == -1) {
 559                 mdb_warn("unable to read 'sv_minor_rev'");
 560                 return (DCMD_ERR);
 561         }
 562 
 563         if (mdb_readvar(&mic, "sv_micro_rev") == -1) {
 564                 mdb_warn("unable to read 'sv_micro_rev'");
 565                 return (DCMD_ERR);
 566         }
 567 
 568         if (mdb_readvar(&baseline, "sv_baseline_rev") == -1) {
 569                 mdb_warn("unable to read 'sv_baseline_rev'");
 570                 return (DCMD_ERR);
 571         }
 572 
 573         mdb_printf("SV module version: kernel %d.%d.%d.%d; mdb %d.%d.%d.%d\n",
 574             maj, min, mic, baseline,
 575             ISS_VERSION_MAJ, ISS_VERSION_MIN, ISS_VERSION_MIC, ISS_VERSION_NUM);
 576         mdb_inc_indent(4);
 577 
 578         sv_get_print(sv_config_time, "last config time", "0x%lx", clock);
 579         sv_get_print(sv_stats_on, "stats on", "%d", i);
 580         sv_get_print(sv_debug, "debug", "%d", i);
 581         sv_get_print(sv_max_devices, "max sv devices", "%d", i);
 582 
 583         mdb_dec_indent(4);
 584         return (DCMD_OK);
 585 }
 586 
 587 
 588 /*
 589  * MDB module linkage information:
 590  */
 591 
 592 static const mdb_dcmd_t dcmds[] = {
 593         { "sv", NULL, "display sv module info", sv },
 594         { "sv_dev", "?[-av]", "list sv_dev structure", sv_dev },
 595         { "sv_gclient", "?", "list sv_gclient structure", sv_gclient },
 596         { "sv_hash", ":[-av]", "display sv_dev hash chain", sv_hash },
 597         { "sv_maj", "?[-av]", "list sv_maj structure", sv_maj },
 598         { NULL }
 599 };
 600 
 601 
 602 static const mdb_walker_t walkers[] = {
 603         { "sv_dev", "walk array of sv_dev structures",
 604             sv_dev_winit, sv_dev_wstep, sv_dev_wfini },
 605         { "sv_gclient", "walk sb_gclient chain",
 606             sv_gclient_winit, sv_gclient_wstep, sv_gclient_wfini },
 607         { "sv_hash", "walk sv_dev hash chain",
 608             sv_hash_winit, sv_hash_wstep, sv_hash_wfini },
 609         { "sv_maj", "walk array of sv_maj structures",
 610             sv_maj_winit, sv_maj_wstep, sv_maj_wfini },
 611         { NULL }
 612 };
 613 
 614 
 615 static const mdb_modinfo_t modinfo = {
 616         MDB_API_VERSION, dcmds, walkers
 617 };
 618 
 619 
 620 const mdb_modinfo_t *
 621 _mdb_init(void)
 622 {
 623         return (&modinfo);
 624 }