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 2014 Gary Mills
  23  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  24  * Use is subject to license terms.
  25  */
  26 
  27 /*
  28  * Dump memory to NFS swap file after a panic.
  29  * We have no timeouts, context switches, etc.
  30  */
  31 
  32 #include <rpc/types.h>
  33 #include <sys/param.h>
  34 #include <sys/errno.h>
  35 #include <sys/vnode.h>
  36 #include <sys/bootconf.h>
  37 #include <nfs/nfs.h>
  38 #include <rpc/auth.h>
  39 #include <rpc/xdr.h>
  40 #include <rpc/rpc_msg.h>
  41 #include <rpc/clnt.h>
  42 #include <netinet/in.h>
  43 #include <sys/tiuser.h>
  44 #include <nfs/nfs_clnt.h>
  45 #include <sys/t_kuser.h>
  46 #include <sys/file.h>
  47 #include <sys/netconfig.h>
  48 #include <sys/utsname.h>
  49 #include <sys/sysmacros.h>
  50 #include <sys/thread.h>
  51 #include <sys/cred.h>
  52 #include <sys/strsubr.h>
  53 #include <nfs/rnode.h>
  54 #include <sys/varargs.h>
  55 #include <sys/cmn_err.h>
  56 #include <sys/systm.h>
  57 #include <sys/dumphdr.h>
  58 #include <sys/debug.h>
  59 #include <sys/sunddi.h>
  60 
  61 #define TIMEOUT         (2 * hz)
  62 #define RETRIES         (5)
  63 #define HDR_SIZE        (256)
  64 
  65 static struct knetconfig        nfsdump_cf;
  66 static struct netbuf            nfsdump_addr;
  67 static fhandle_t                nfsdump_fhandle2;
  68 static nfs_fh3                  nfsdump_fhandle3;
  69 static int                      nfsdump_maxcount;
  70 static rpcvers_t                nfsdump_version;
  71 
  72 /*
  73  * nonzero dumplog enables nd_log messages
  74  */
  75 static int      dumplog = 0;
  76 
  77 static int      nd_init(vnode_t *, TIUSER **);
  78 static int      nd_poll(TIUSER *, int, int *);
  79 static int      nd_send_data(TIUSER *, caddr_t, int, XDR *, uint32_t *);
  80 static int      nd_get_reply(TIUSER *, XDR *, uint32_t, int *);
  81 static int      nd_auth_marshall(XDR *);
  82 
  83 static void nd_log(const char *, ...) __KPRINTFLIKE(1);
  84 
  85 /*PRINTFLIKE1*/
  86 static void
  87 nd_log(const char *fmt, ...)
  88 {
  89         if (dumplog) {
  90                 va_list adx;
  91 
  92                 va_start(adx, fmt);
  93                 vprintf(fmt, adx);
  94                 va_end(adx);
  95         }
  96 }
  97 
  98 /* ARGSUSED */
  99 int
 100 nfs_dump(vnode_t *dumpvp, caddr_t addr, offset_t bn, offset_t count,
 101     caller_context_t *ct)
 102 {
 103         static TIUSER   *tiptr;
 104         XDR             xdrs;
 105         int             reply;
 106         int             badmsg;
 107         uint32_t        call_xid;
 108         int             retry = 0;
 109         int             error;
 110         int             i;
 111 
 112         nd_log("nfs_dump: addr=%p bn=%lld count=%lld\n",
 113             (void *)addr, bn, count);
 114 
 115         if (error = nd_init(dumpvp, &tiptr))
 116                 return (error);
 117 
 118         for (i = 0; i < count; i += ptod(1), addr += ptob(1)) {
 119                 do {
 120                         error = nd_send_data(tiptr, addr, (int)dbtob(bn + i),
 121                             &xdrs, &call_xid);
 122                         if (error)
 123                                 return (error);
 124 
 125                         do {
 126                                 if (error = nd_poll(tiptr, retry, &reply))
 127                                         return (error);
 128 
 129                                 if (!reply) {
 130                                         retry++;
 131                                         break;
 132                                 }
 133                                 retry = 0;
 134 
 135                                 error = nd_get_reply(tiptr, &xdrs, call_xid,
 136                                     &badmsg);
 137                                 if (error)
 138                                         return (error);
 139                         } while (badmsg);
 140                 } while (retry);
 141         }
 142 
 143         return (0);
 144 }
 145 
 146 static int
 147 nd_init(vnode_t *dumpvp, TIUSER **tiptr)
 148 {
 149         int             error;
 150 
 151         if (*tiptr)
 152                 return (0);
 153 
 154         /*
 155          * If dump info hasn't yet been initialized (because dump
 156          * device was chosen at user-level, rather than at boot time
 157          * in nfs_swapvp) fill it in now.
 158          */
 159         if (nfsdump_maxcount == 0) {
 160                 nfsdump_version = VTOMI(dumpvp)->mi_vers;
 161                 switch (nfsdump_version) {
 162                 case NFS_VERSION:
 163                         nfsdump_fhandle2 = *VTOFH(dumpvp);
 164                         break;
 165                 case NFS_V3:
 166                         nfsdump_fhandle3 = *VTOFH3(dumpvp);
 167                         break;
 168                 default:
 169                         return (EIO);
 170                 }
 171                 nfsdump_maxcount = (int)dumpvp_size;
 172                 nfsdump_addr = VTOMI(dumpvp)->mi_curr_serv->sv_addr;
 173                 nfsdump_cf = *(VTOMI(dumpvp)->mi_curr_serv->sv_knconf);
 174                 if (nfsdump_cf.knc_semantics != NC_TPI_CLTS) {
 175                         int v6 = 1;
 176                         nd_log("nfs_dump: not connectionless!\n");
 177                         if ((strcmp(nfsdump_cf.knc_protofmly, NC_INET) == 0) ||
 178                             ((v6 = strcmp(nfsdump_cf.knc_protofmly, NC_INET6))\
 179                             == 0)) {
 180                                 major_t clone_maj;
 181 
 182                                 nfsdump_cf.knc_proto = NC_UDP;
 183                                 nfsdump_cf.knc_semantics = NC_TPI_CLTS;
 184                                 nd_log("nfs_dump: grabbing UDP major number\n");
 185                                 clone_maj = ddi_name_to_major("clone");
 186                                 nd_log("nfs_dump: making UDP device\n");
 187                                 nfsdump_cf.knc_rdev = makedevice(clone_maj,
 188                                     ddi_name_to_major(v6?"udp":"udp6"));
 189                         } else {
 190                                 error = EIO;
 191                                 nfs_perror(error, "\nnfs_dump: cannot dump over"
 192                                     " protocol %s: %m\n", nfsdump_cf.knc_proto);
 193                                 return (error);
 194                         }
 195                 }
 196         }
 197 
 198         nd_log("nfs_dump: calling t_kopen\n");
 199 
 200         if (error = t_kopen(NULL, nfsdump_cf.knc_rdev,
 201             FREAD|FWRITE|FNDELAY, tiptr, CRED())) {
 202                 nfs_perror(error, "\nnfs_dump: t_kopen failed: %m\n");
 203                 return (EIO);
 204         }
 205 
 206         if ((strcmp(nfsdump_cf.knc_protofmly, NC_INET) == 0) ||
 207             (strcmp(nfsdump_cf.knc_protofmly, NC_INET6) == 0)) {
 208                 nd_log("nfs_dump: calling bindresvport\n");
 209                 if (error = bindresvport(*tiptr, NULL, NULL, FALSE)) {
 210                         nfs_perror(error,
 211                             "\nnfs_dump: bindresvport failed: %m\n");
 212                         return (EIO);
 213                 }
 214         } else {
 215                 nd_log("nfs_dump: calling t_kbind\n");
 216                 if ((error = t_kbind(*tiptr, NULL, NULL)) != 0) {
 217                         nfs_perror(error, "\nnfs_dump: t_kbind failed: %m\n");
 218                         return (EIO);
 219                 }
 220         }
 221         return (0);
 222 }
 223 
 224 static int
 225 nd_send_data(TIUSER *tiptr, caddr_t addr, int offset, XDR *xdrp, uint32_t *xidp)
 226 {
 227         static struct rpc_msg           call_msg;
 228         static uchar_t                  header[HDR_SIZE];
 229         static struct t_kunitdata       sudata;
 230         static uchar_t                  *dumpbuf;
 231         int                             procnum;
 232         stable_how                      stable = FILE_SYNC;
 233         mblk_t                          *mblk_p;
 234         int                             error;
 235         int                             tsize = ptob(1);
 236         uint64                          offset3;
 237 
 238         if (!dumpbuf) {
 239                 call_msg.rm_direction = CALL;
 240                 call_msg.rm_call.cb_rpcvers = RPC_MSG_VERSION;
 241                 call_msg.rm_call.cb_prog = NFS_PROGRAM;
 242                 call_msg.rm_call.cb_vers = nfsdump_version;
 243 
 244                 if (!(dumpbuf = kmem_alloc(ptob(1), KM_NOSLEEP))) {
 245                 cmn_err(CE_WARN, "\tnfs_dump: cannot allocate dump buffer");
 246                         return (ENOMEM);
 247                 }
 248         }
 249 
 250         nd_log("nfs_dump: calling esballoc for header\n");
 251 
 252         if (!(mblk_p = esballoc(header, HDR_SIZE, BPRI_HI, &frnop))) {
 253                 cmn_err(CE_WARN, "\tnfs_dump: out of mblks");
 254                 return (ENOBUFS);
 255         }
 256 
 257         xdrmem_create(xdrp, (caddr_t)header, HDR_SIZE, XDR_ENCODE);
 258 
 259         call_msg.rm_xid = alloc_xid();
 260         *xidp = call_msg.rm_xid;
 261 
 262         if (!xdr_callhdr(xdrp, &call_msg)) {
 263                 cmn_err(CE_WARN, "\tnfs_dump: cannot serialize header");
 264                 return (EIO);
 265         }
 266 
 267         if (nfsdump_maxcount) {
 268                 /*
 269                  * Do not extend the dump file if it is also
 270                  * the swap file.
 271                  */
 272                 if (offset >= nfsdump_maxcount) {
 273                         cmn_err(CE_WARN, "\tnfs_dump: end of file");
 274                         return (EIO);
 275                 }
 276                 if (offset + tsize > nfsdump_maxcount)
 277                         tsize = nfsdump_maxcount - offset;
 278         }
 279         switch (nfsdump_version) {
 280         case NFS_VERSION:
 281                 procnum = RFS_WRITE;
 282                 if (!XDR_PUTINT32(xdrp, (int32_t *)&procnum) ||
 283                     !nd_auth_marshall(xdrp) ||
 284                     !xdr_fhandle(xdrp, &nfsdump_fhandle2) ||
 285                         /*
 286                          *  Following four values are:
 287                          *      beginoffset
 288                          *      offset
 289                          *      length
 290                          *      bytes array length
 291                          */
 292                     !XDR_PUTINT32(xdrp, (int32_t *)&offset) ||
 293                     !XDR_PUTINT32(xdrp, (int32_t *)&offset) ||
 294                     !XDR_PUTINT32(xdrp, (int32_t *)&tsize) ||
 295                     !XDR_PUTINT32(xdrp, (int32_t *)&tsize)) {
 296                         cmn_err(CE_WARN, "\tnfs_dump: serialization failed");
 297                         return (EIO);
 298                 }
 299                 break;
 300         case NFS_V3:
 301                 procnum = NFSPROC3_WRITE;
 302                 offset3 = offset;
 303                 if (!XDR_PUTINT32(xdrp, (int32_t *)&procnum) ||
 304                     !nd_auth_marshall(xdrp) ||
 305                     !xdr_nfs_fh3(xdrp, &nfsdump_fhandle3) ||
 306                         /*
 307                          *  Following four values are:
 308                          *      offset
 309                          *      count
 310                          *      stable
 311                          *      bytes array length
 312                          */
 313                     !xdr_u_longlong_t(xdrp, &offset3) ||
 314                     !XDR_PUTINT32(xdrp, (int32_t *)&tsize) ||
 315                     !XDR_PUTINT32(xdrp, (int32_t *)&stable) ||
 316                     !XDR_PUTINT32(xdrp, (int32_t *)&tsize)) {
 317                         cmn_err(CE_WARN, "\tnfs_dump: serialization failed");
 318                         return (EIO);
 319                 }
 320                 break;
 321         default:
 322                 return (EIO);
 323         }
 324 
 325         bcopy(addr, (caddr_t)dumpbuf, tsize);
 326 
 327         mblk_p->b_wptr += (int)XDR_GETPOS(xdrp);
 328 
 329         mblk_p->b_cont = esballoc((uchar_t *)dumpbuf, ptob(1), BPRI_HI, &frnop);
 330 
 331         if (!mblk_p->b_cont) {
 332                 cmn_err(CE_WARN, "\tnfs_dump: out of mblks");
 333                 return (ENOBUFS);
 334         }
 335         mblk_p->b_cont->b_wptr += ptob(1);
 336 
 337         sudata.addr = nfsdump_addr;             /* structure copy */
 338         sudata.udata.buf = (char *)NULL;
 339         sudata.udata.maxlen = 0;
 340         sudata.udata.len = 1;                   /* needed for t_ksndudata */
 341         sudata.udata.udata_mp = mblk_p;
 342 
 343         nd_log("nfs_dump: calling t_ksndudata\n");
 344 
 345         if (error = t_ksndudata(tiptr, &sudata, (frtn_t *)NULL)) {
 346                 nfs_perror(error, "\nnfs_dump: t_ksndudata failed: %m\n");
 347                 return (error);
 348         }
 349         return (0);
 350 }
 351 
 352 static int
 353 nd_get_reply(TIUSER *tiptr, XDR *xdrp, uint32_t call_xid, int *badmsg)
 354 {
 355         static struct rpc_msg           reply_msg;
 356         static struct rpc_err           rpc_err;
 357         static struct nfsattrstat       na;
 358         static struct WRITE3res         wres;
 359         static struct t_kunitdata       rudata;
 360         int                             uderr;
 361         int                             type;
 362         int                             error;
 363 
 364         *badmsg = 0;
 365 
 366         rudata.addr.maxlen = 0;
 367         rudata.opt.maxlen = 0;
 368         rudata.udata.udata_mp = (mblk_t *)NULL;
 369 
 370         nd_log("nfs_dump: calling t_krcvudata\n");
 371 
 372         if (error = t_krcvudata(tiptr, &rudata, &type, &uderr)) {
 373                 if (error == EBADMSG) {
 374                         cmn_err(CE_WARN, "\tnfs_dump:  received EBADMSG");
 375                         *badmsg = 1;
 376                         return (0);
 377                 }
 378                 nfs_perror(error, "\nnfs_dump: t_krcvudata failed: %m\n");
 379                 return (EIO);
 380         }
 381         if (type != T_DATA) {
 382                 cmn_err(CE_WARN, "\tnfs_dump:  received type %d", type);
 383                 *badmsg = 1;
 384                 return (0);
 385         }
 386         if (!rudata.udata.udata_mp) {
 387                 cmn_err(CE_WARN, "\tnfs_dump: null receive");
 388                 *badmsg = 1;
 389                 return (0);
 390         }
 391 
 392         /*
 393          * Decode results.
 394          */
 395         xdrmblk_init(xdrp, rudata.udata.udata_mp, XDR_DECODE, 0);
 396 
 397         reply_msg.acpted_rply.ar_verf = _null_auth;
 398         switch (nfsdump_version) {
 399         case NFS_VERSION:
 400                 reply_msg.acpted_rply.ar_results.where = (caddr_t)&na;
 401                 reply_msg.acpted_rply.ar_results.proc = xdr_attrstat;
 402                 break;
 403         case NFS_V3:
 404                 reply_msg.acpted_rply.ar_results.where = (caddr_t)&wres;
 405                 reply_msg.acpted_rply.ar_results.proc = xdr_WRITE3res;
 406                 break;
 407         default:
 408                 return (EIO);
 409         }
 410 
 411         if (!xdr_replymsg(xdrp, &reply_msg)) {
 412                 cmn_err(CE_WARN, "\tnfs_dump: xdr_replymsg failed");
 413                 return (EIO);
 414         }
 415 
 416         if (reply_msg.rm_xid != call_xid) {
 417                 *badmsg = 1;
 418                 return (0);
 419         }
 420 
 421         _seterr_reply(&reply_msg, &rpc_err);
 422 
 423         if (rpc_err.re_status != RPC_SUCCESS) {
 424                 cmn_err(CE_WARN, "\tnfs_dump: RPC error %d (%s)",
 425                     rpc_err.re_status, clnt_sperrno(rpc_err.re_status));
 426                 return (EIO);
 427         }
 428 
 429         switch (nfsdump_version) {
 430         case NFS_VERSION:
 431                 if (na.ns_status) {
 432                         cmn_err(CE_WARN, "\tnfs_dump: status %d", na.ns_status);
 433                         return (EIO);
 434                 }
 435                 break;
 436         case NFS_V3:
 437                 if (wres.status != NFS3_OK) {
 438                         cmn_err(CE_WARN, "\tnfs_dump: status %d", wres.status);
 439                         return (EIO);
 440                 }
 441                 break;
 442         default:
 443                 return (EIO);
 444         }
 445 
 446         if (reply_msg.acpted_rply.ar_verf.oa_base != NULL) {
 447                 /* free auth handle */
 448                 xdrp->x_op = XDR_FREE;
 449                 (void) xdr_opaque_auth(xdrp, &(reply_msg.acpted_rply.ar_verf));
 450         }
 451 
 452         freemsg(rudata.udata.udata_mp);
 453 
 454         return (0);
 455 }
 456 
 457 static int
 458 nd_poll(TIUSER *tiptr, int retry, int *eventp)
 459 {
 460         clock_t         start_bolt = ddi_get_lbolt();
 461         clock_t         timout = TIMEOUT * (retry + 1);
 462         int             error;
 463 
 464         nd_log("nfs_dump: calling t_kspoll\n");
 465 
 466         *eventp = 0;
 467 
 468         while (!*eventp && ((ddi_get_lbolt() - start_bolt) < timout)) {
 469                 /*
 470                  * Briefly enable interrupts before checking for a reply;
 471                  * the network transports do not yet support do_polled_io.
 472                  */
 473                 int s = spl0();
 474                 splx(s);
 475 
 476                 if (error = t_kspoll(tiptr, 0, READWAIT, eventp)) {
 477                         nfs_perror(error,
 478                             "\nnfs_dump: t_kspoll failed: %m\n");
 479                         return (EIO);
 480                 }
 481                 runqueues();
 482         }
 483 
 484         if (retry == RETRIES && !*eventp) {
 485                 cmn_err(CE_WARN, "\tnfs_dump: server not responding");
 486                 return (EIO);
 487         }
 488 
 489         return (0);
 490 }
 491 
 492 static int
 493 nd_auth_marshall(XDR *xdrp)
 494 {
 495         int credsize;
 496         int32_t *ptr;
 497         int hostnamelen;
 498 
 499         hostnamelen = (int)strlen(utsname.nodename);
 500         credsize = 4 + 4 + roundup(hostnamelen, 4) + 4 + 4 + 4;
 501 
 502         ptr = XDR_INLINE(xdrp, 4 + 4 + credsize + 4 + 4);
 503         if (!ptr) {
 504                 cmn_err(CE_WARN, "\tnfs_dump: auth_marshall failed");
 505                 return (0);
 506         }
 507         /*
 508          * We can do the fast path.
 509          */
 510         IXDR_PUT_INT32(ptr, AUTH_UNIX); /* cred flavor */
 511         IXDR_PUT_INT32(ptr, credsize);  /* cred len */
 512         IXDR_PUT_INT32(ptr, gethrestime_sec());
 513         IXDR_PUT_INT32(ptr, hostnamelen);
 514 
 515         bcopy(utsname.nodename, ptr, hostnamelen);
 516         ptr += roundup(hostnamelen, 4) / 4;
 517 
 518         IXDR_PUT_INT32(ptr, 0);         /* uid */
 519         IXDR_PUT_INT32(ptr, 0);         /* gid */
 520         IXDR_PUT_INT32(ptr, 0);         /* gid list length (empty) */
 521         IXDR_PUT_INT32(ptr, AUTH_NULL); /* verf flavor */
 522         IXDR_PUT_INT32(ptr, 0);         /* verf len */
 523 
 524         return (1);
 525 }