xref: /illumos-gate/usr/src/uts/common/fs/nfs/nfs_dump.c (revision 6e2462f93bf3de7b08885a4677464e11be3c807b)
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