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 }