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
nd_log(const char * fmt,...)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
nfs_dump(vnode_t * dumpvp,caddr_t addr,offset_t bn,offset_t count,caller_context_t * ct)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
nd_init(vnode_t * dumpvp,TIUSER ** tiptr)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
nd_send_data(TIUSER * tiptr,caddr_t addr,int offset,XDR * xdrp,uint32_t * xidp)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
nd_get_reply(TIUSER * tiptr,XDR * xdrp,uint32_t call_xid,int * badmsg)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
nd_poll(TIUSER * tiptr,int retry,int * eventp)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
nd_auth_marshall(XDR * xdrp)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 }
526