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