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