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