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