xref: /illumos-gate/usr/src/uts/common/fs/nfs/nfs_dump.c (revision 581cede61ac9c14d8d4ea452562a567189eead78)
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 2008 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 #pragma ident	"%Z%%M%	%I%	%E% SMI"
27 
28 /*
29  * Dump memory to NFS swap file after a panic.
30  * We have no timeouts, context switches, etc.
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
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
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
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
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
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 		nfs_perror(error, "\nnfs_dump: t_krcvudata failed: %m\n");
374 		return (EIO);
375 	}
376 	if (type != T_DATA) {
377 		cmn_err(CE_WARN, "\tnfs_dump:  received type %d", type);
378 		*badmsg = 1;
379 		return (0);
380 	}
381 	if (!rudata.udata.udata_mp) {
382 		cmn_err(CE_WARN, "\tnfs_dump: null receive");
383 		*badmsg = 1;
384 		return (0);
385 	}
386 
387 	/*
388 	 * Decode results.
389 	 */
390 	xdrmblk_init(xdrp, rudata.udata.udata_mp, XDR_DECODE, 0);
391 
392 	reply_msg.acpted_rply.ar_verf = _null_auth;
393 	switch (nfsdump_version) {
394 	case NFS_VERSION:
395 		reply_msg.acpted_rply.ar_results.where = (caddr_t)&na;
396 		reply_msg.acpted_rply.ar_results.proc = xdr_attrstat;
397 		break;
398 	case NFS_V3:
399 		reply_msg.acpted_rply.ar_results.where = (caddr_t)&wres;
400 		reply_msg.acpted_rply.ar_results.proc = xdr_WRITE3res;
401 		break;
402 	default:
403 		return (EIO);
404 	}
405 
406 	if (!xdr_replymsg(xdrp, &reply_msg)) {
407 		cmn_err(CE_WARN, "\tnfs_dump: xdr_replymsg failed");
408 		return (EIO);
409 	}
410 
411 	if (reply_msg.rm_xid != call_xid) {
412 		*badmsg = 1;
413 		return (0);
414 	}
415 
416 	_seterr_reply(&reply_msg, &rpc_err);
417 
418 	if (rpc_err.re_status != RPC_SUCCESS) {
419 		cmn_err(CE_WARN, "\tnfs_dump: RPC error %d (%s)",
420 		    rpc_err.re_status, clnt_sperrno(rpc_err.re_status));
421 		return (EIO);
422 	}
423 
424 	switch (nfsdump_version) {
425 	case NFS_VERSION:
426 		if (na.ns_status) {
427 			cmn_err(CE_WARN, "\tnfs_dump: status %d", na.ns_status);
428 			return (EIO);
429 		}
430 		break;
431 	case NFS_V3:
432 		if (wres.status != NFS3_OK) {
433 			cmn_err(CE_WARN, "\tnfs_dump: status %d", wres.status);
434 			return (EIO);
435 		}
436 		break;
437 	default:
438 		return (EIO);
439 	}
440 
441 	if (reply_msg.acpted_rply.ar_verf.oa_base != NULL) {
442 		/* free auth handle */
443 		xdrp->x_op = XDR_FREE;
444 		(void) xdr_opaque_auth(xdrp, &(reply_msg.acpted_rply.ar_verf));
445 	}
446 
447 	freemsg(rudata.udata.udata_mp);
448 
449 	return (0);
450 }
451 
452 static int
453 nd_poll(TIUSER *tiptr, int retry, int *eventp)
454 {
455 	clock_t		start_bolt = lbolt;
456 	clock_t		timout = TIMEOUT * (retry + 1);
457 	int		error;
458 
459 	nd_log("nfs_dump: calling t_kspoll\n");
460 
461 	*eventp = 0;
462 
463 	while (!*eventp && ((lbolt - start_bolt) < timout)) {
464 		/*
465 		 * Briefly enable interrupts before checking for a reply;
466 		 * the network transports do not yet support do_polled_io.
467 		 */
468 		int s = spl0();
469 		splx(s);
470 
471 		if (error = t_kspoll(tiptr, 0, READWAIT, eventp)) {
472 			nfs_perror(error,
473 			    "\nnfs_dump: t_kspoll failed: %m\n");
474 			return (EIO);
475 		}
476 		runqueues();
477 	}
478 
479 	if (retry == RETRIES && !*eventp) {
480 		cmn_err(CE_WARN, "\tnfs_dump: server not responding");
481 		return (EIO);
482 	}
483 
484 	return (0);
485 }
486 
487 static int
488 nd_auth_marshall(XDR *xdrp)
489 {
490 	int credsize;
491 	int32_t *ptr;
492 	int hostnamelen;
493 
494 	hostnamelen = (int)strlen(utsname.nodename);
495 	credsize = 4 + 4 + roundup(hostnamelen, 4) + 4 + 4 + 4;
496 
497 	ptr = XDR_INLINE(xdrp, 4 + 4 + credsize + 4 + 4);
498 	if (!ptr) {
499 		cmn_err(CE_WARN, "\tnfs_dump: auth_marshall failed");
500 		return (0);
501 	}
502 	/*
503 	 * We can do the fast path.
504 	 */
505 	IXDR_PUT_INT32(ptr, AUTH_UNIX);	/* cred flavor */
506 	IXDR_PUT_INT32(ptr, credsize);	/* cred len */
507 	IXDR_PUT_INT32(ptr, gethrestime_sec());
508 	IXDR_PUT_INT32(ptr, hostnamelen);
509 
510 	bcopy(utsname.nodename, ptr, hostnamelen);
511 	ptr += roundup(hostnamelen, 4) / 4;
512 
513 	IXDR_PUT_INT32(ptr, 0);		/* uid */
514 	IXDR_PUT_INT32(ptr, 0);		/* gid */
515 	IXDR_PUT_INT32(ptr, 0);		/* gid list length (empty) */
516 	IXDR_PUT_INT32(ptr, AUTH_NULL);	/* verf flavor */
517 	IXDR_PUT_INT32(ptr, 0);		/* verf len */
518 
519 	return (1);
520 }
521