5907 xdrmblk_getpos() is unreliable
[illumos-gate.git] / usr / src / uts / common / fs / nfs / nfs_dump.c
blobafa50a712426fde3d41fc35a4a298c91709a583f
1 /*
2 * CDDL HEADER START
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.
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.
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]
19 * CDDL HEADER END
23 * Copyright 2015 Nexenta Systems, Inc. All rights reserved.
27 * Copyright 2014 Gary Mills
28 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
29 * Use is subject to license terms.
33 * Dump memory to NFS swap file after a panic.
34 * We have no timeouts, context switches, etc.
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>
66 #define TIMEOUT (2 * hz)
67 #define RETRIES (5)
68 #define HDR_SIZE (256)
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;
78 * nonzero dumplog enables nd_log messages
80 static int dumplog = 0;
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 *);
88 static void nd_log(const char *, ...) __KPRINTFLIKE(1);
90 /*PRINTFLIKE1*/
91 static void
92 nd_log(const char *fmt, ...)
94 if (dumplog) {
95 va_list adx;
97 va_start(adx, fmt);
98 vprintf(fmt, adx);
99 va_end(adx);
103 /* ARGSUSED */
105 nfs_dump(vnode_t *dumpvp, caddr_t addr, offset_t bn, offset_t count,
106 caller_context_t *ct)
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;
117 nd_log("nfs_dump: addr=%p bn=%lld count=%lld\n",
118 (void *)addr, bn, count);
120 if (error = nd_init(dumpvp, &tiptr))
121 return (error);
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);
130 do {
131 if (error = nd_poll(tiptr, retry, &reply))
132 return (error);
134 if (!reply) {
135 retry++;
136 break;
138 retry = 0;
140 error = nd_get_reply(tiptr, &xdrs, call_xid,
141 &badmsg);
142 if (error)
143 return (error);
144 } while (badmsg);
145 } while (retry);
148 return (0);
151 static int
152 nd_init(vnode_t *dumpvp, TIUSER **tiptr)
154 int error;
156 if (*tiptr)
157 return (0);
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.
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);
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;
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);
203 nd_log("nfs_dump: calling t_kopen\n");
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);
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);
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);
226 return (0);
229 static int
230 nd_send_data(TIUSER *tiptr, caddr_t addr, int offset, XDR *xdrp, uint32_t *xidp)
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;
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;
249 if (!(dumpbuf = kmem_alloc(ptob(1), KM_NOSLEEP))) {
250 cmn_err(CE_WARN, "\tnfs_dump: cannot allocate dump buffer");
251 return (ENOMEM);
255 nd_log("nfs_dump: calling esballoc for header\n");
257 if (!(mblk_p = esballoc(header, HDR_SIZE, BPRI_HI, &frnop))) {
258 cmn_err(CE_WARN, "\tnfs_dump: out of mblks");
259 return (ENOBUFS);
262 xdrmem_create(xdrp, (caddr_t)header, HDR_SIZE, XDR_ENCODE);
264 call_msg.rm_xid = alloc_xid();
265 *xidp = call_msg.rm_xid;
267 if (!xdr_callhdr(xdrp, &call_msg)) {
268 cmn_err(CE_WARN, "\tnfs_dump: cannot serialize header");
269 return (EIO);
272 if (nfsdump_maxcount) {
274 * Do not extend the dump file if it is also
275 * the swap file.
277 if (offset >= nfsdump_maxcount) {
278 cmn_err(CE_WARN, "\tnfs_dump: end of file");
279 return (EIO);
281 if (offset + tsize > nfsdump_maxcount)
282 tsize = nfsdump_maxcount - offset;
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) ||
291 * Following four values are:
292 * beginoffset
293 * offset
294 * length
295 * bytes array length
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);
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) ||
312 * Following four values are:
313 * offset
314 * count
315 * stable
316 * bytes array length
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);
325 break;
326 default:
327 return (EIO);
330 bcopy(addr, (caddr_t)dumpbuf, tsize);
332 mblk_p->b_wptr += (int)XDR_GETPOS(xdrp);
334 mblk_p->b_cont = esballoc((uchar_t *)dumpbuf, ptob(1), BPRI_HI, &frnop);
336 if (!mblk_p->b_cont) {
337 cmn_err(CE_WARN, "\tnfs_dump: out of mblks");
338 return (ENOBUFS);
340 mblk_p->b_cont->b_wptr += ptob(1);
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;
348 nd_log("nfs_dump: calling t_ksndudata\n");
350 if (error = t_ksndudata(tiptr, &sudata, (frtn_t *)NULL)) {
351 nfs_perror(error, "\nnfs_dump: t_ksndudata failed: %m\n");
352 return (error);
354 return (0);
357 static int
358 nd_get_reply(TIUSER *tiptr, XDR *xdrp, uint32_t call_xid, int *badmsg)
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;
369 *badmsg = 0;
371 rudata.addr.maxlen = 0;
372 rudata.opt.maxlen = 0;
373 rudata.udata.udata_mp = (mblk_t *)NULL;
375 nd_log("nfs_dump: calling t_krcvudata\n");
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);
383 nfs_perror(error, "\nnfs_dump: t_krcvudata failed: %m\n");
384 return (EIO);
386 if (type != T_DATA) {
387 cmn_err(CE_WARN, "\tnfs_dump: received type %d", type);
388 *badmsg = 1;
389 return (0);
391 if (!rudata.udata.udata_mp) {
392 cmn_err(CE_WARN, "\tnfs_dump: null receive");
393 *badmsg = 1;
394 return (0);
398 * Decode results.
400 xdrmblk_init(xdrp, rudata.udata.udata_mp, XDR_DECODE, 0);
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);
417 if (!xdr_replymsg(xdrp, &reply_msg)) {
418 XDR_DESTROY(xdrp);
419 cmn_err(CE_WARN, "\tnfs_dump: xdr_replymsg failed");
420 return (EIO);
423 if (reply_msg.rm_xid != call_xid) {
424 XDR_DESTROY(xdrp);
425 *badmsg = 1;
426 return (0);
429 _seterr_reply(&reply_msg, &rpc_err);
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);
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);
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);
452 break;
453 default:
454 XDR_DESTROY(xdrp);
455 return (EIO);
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));
464 XDR_DESTROY(xdrp);
466 freemsg(rudata.udata.udata_mp);
468 return (0);
471 static int
472 nd_poll(TIUSER *tiptr, int retry, int *eventp)
474 clock_t start_bolt = ddi_get_lbolt();
475 clock_t timout = TIMEOUT * (retry + 1);
476 int error;
478 nd_log("nfs_dump: calling t_kspoll\n");
480 *eventp = 0;
482 while (!*eventp && ((ddi_get_lbolt() - start_bolt) < timout)) {
484 * Briefly enable interrupts before checking for a reply;
485 * the network transports do not yet support do_polled_io.
487 int s = spl0();
488 splx(s);
490 if (error = t_kspoll(tiptr, 0, READWAIT, eventp)) {
491 nfs_perror(error,
492 "\nnfs_dump: t_kspoll failed: %m\n");
493 return (EIO);
495 runqueues();
498 if (retry == RETRIES && !*eventp) {
499 cmn_err(CE_WARN, "\tnfs_dump: server not responding");
500 return (EIO);
503 return (0);
506 static int
507 nd_auth_marshall(XDR *xdrp)
509 int credsize;
510 int32_t *ptr;
511 int hostnamelen;
513 hostnamelen = (int)strlen(utsname.nodename);
514 credsize = 4 + 4 + roundup(hostnamelen, 4) + 4 + 4 + 4;
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);
522 * We can do the fast path.
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);
529 bcopy(utsname.nodename, ptr, hostnamelen);
530 ptr += roundup(hostnamelen, 4) / 4;
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 */
538 return (1);