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]
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>
45 #include <rpc/rpc_msg.h>
47 #include <netinet/in.h>
48 #include <sys/tiuser.h>
49 #include <nfs/nfs_clnt.h>
50 #include <sys/t_kuser.h>
52 #include <sys/netconfig.h>
53 #include <sys/utsname.h>
54 #include <sys/sysmacros.h>
55 #include <sys/thread.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)
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);
92 nd_log(const char *fmt
, ...)
105 nfs_dump(vnode_t
*dumpvp
, caddr_t addr
, offset_t bn
, offset_t count
,
106 caller_context_t
*ct
)
108 static TIUSER
*tiptr
;
117 nd_log("nfs_dump: addr=%p bn=%lld count=%lld\n",
118 (void *)addr
, bn
, count
);
120 if (error
= nd_init(dumpvp
, &tiptr
))
123 for (i
= 0; i
< count
; i
+= ptod(1), addr
+= ptob(1)) {
125 error
= nd_send_data(tiptr
, addr
, (int)dbtob(bn
+ i
),
131 if (error
= nd_poll(tiptr
, retry
, &reply
))
140 error
= nd_get_reply(tiptr
, &xdrs
, call_xid
,
152 nd_init(vnode_t
*dumpvp
, TIUSER
**tiptr
)
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
) {
168 nfsdump_fhandle2
= *VTOFH(dumpvp
);
171 nfsdump_fhandle3
= *VTOFH3(dumpvp
);
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
) {
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
))\
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"));
196 nfs_perror(error
, "\nnfs_dump: cannot dump over"
197 " protocol %s: %m\n", nfsdump_cf
.knc_proto
);
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");
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
)) {
216 "\nnfs_dump: bindresvport failed: %m\n");
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");
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
;
237 stable_how stable
= FILE_SYNC
;
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");
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");
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");
272 if (nfsdump_maxcount
) {
274 * Do not extend the dump file if it is also
277 if (offset
>= nfsdump_maxcount
) {
278 cmn_err(CE_WARN
, "\tnfs_dump: end of file");
281 if (offset
+ tsize
> nfsdump_maxcount
)
282 tsize
= nfsdump_maxcount
- offset
;
284 switch (nfsdump_version
) {
287 if (!XDR_PUTINT32(xdrp
, (int32_t *)&procnum
) ||
288 !nd_auth_marshall(xdrp
) ||
289 !xdr_fhandle(xdrp
, &nfsdump_fhandle2
) ||
291 * Following four values are:
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");
306 procnum
= NFSPROC3_WRITE
;
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:
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");
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");
340 mblk_p
->b_cont
->b_wptr
+= ptob(1);
342 sudata
.addr
= nfsdump_addr
; /* structure copy */
343 sudata
.udata
.buf
= 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");
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
;
371 rudata
.addr
.maxlen
= 0;
372 rudata
.opt
.maxlen
= 0;
373 rudata
.udata
.udata_mp
= 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");
383 nfs_perror(error
, "\nnfs_dump: t_krcvudata failed: %m\n");
386 if (type
!= T_DATA
) {
387 cmn_err(CE_WARN
, "\tnfs_dump: received type %d", type
);
391 if (!rudata
.udata
.udata_mp
) {
392 cmn_err(CE_WARN
, "\tnfs_dump: null receive");
400 xdrmblk_init(xdrp
, rudata
.udata
.udata_mp
, XDR_DECODE
, 0);
402 reply_msg
.acpted_rply
.ar_verf
= _null_auth
;
403 switch (nfsdump_version
) {
405 reply_msg
.acpted_rply
.ar_results
.where
= (caddr_t
)&na
;
406 reply_msg
.acpted_rply
.ar_results
.proc
= xdr_attrstat
;
409 reply_msg
.acpted_rply
.ar_results
.where
= (caddr_t
)&wres
;
410 reply_msg
.acpted_rply
.ar_results
.proc
= xdr_WRITE3res
;
417 if (!xdr_replymsg(xdrp
, &reply_msg
)) {
419 cmn_err(CE_WARN
, "\tnfs_dump: xdr_replymsg failed");
423 if (reply_msg
.rm_xid
!= call_xid
) {
429 _seterr_reply(&reply_msg
, &rpc_err
);
431 if (rpc_err
.re_status
!= RPC_SUCCESS
) {
433 cmn_err(CE_WARN
, "\tnfs_dump: RPC error %d (%s)",
434 rpc_err
.re_status
, clnt_sperrno(rpc_err
.re_status
));
438 switch (nfsdump_version
) {
442 cmn_err(CE_WARN
, "\tnfs_dump: status %d", na
.ns_status
);
447 if (wres
.status
!= NFS3_OK
) {
449 cmn_err(CE_WARN
, "\tnfs_dump: status %d", wres
.status
);
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
));
466 freemsg(rudata
.udata
.udata_mp
);
472 nd_poll(TIUSER
*tiptr
, int retry
, int *eventp
)
474 clock_t start_bolt
= ddi_get_lbolt();
475 clock_t timout
= TIMEOUT
* (retry
+ 1);
478 nd_log("nfs_dump: calling t_kspoll\n");
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.
490 if (error
= t_kspoll(tiptr
, 0, READWAIT
, eventp
)) {
492 "\nnfs_dump: t_kspoll failed: %m\n");
498 if (retry
== RETRIES
&& !*eventp
) {
499 cmn_err(CE_WARN
, "\tnfs_dump: server not responding");
507 nd_auth_marshall(XDR
*xdrp
)
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);
518 cmn_err(CE_WARN
, "\tnfs_dump: auth_marshall failed");
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 */