xref: /onnv-gate/usr/src/uts/common/cpr/cpr_misc.c (revision 0:68f95e015346)
1*0Sstevel@tonic-gate /*
2*0Sstevel@tonic-gate  * CDDL HEADER START
3*0Sstevel@tonic-gate  *
4*0Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
5*0Sstevel@tonic-gate  * Common Development and Distribution License, Version 1.0 only
6*0Sstevel@tonic-gate  * (the "License").  You may not use this file except in compliance
7*0Sstevel@tonic-gate  * with the License.
8*0Sstevel@tonic-gate  *
9*0Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10*0Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
11*0Sstevel@tonic-gate  * See the License for the specific language governing permissions
12*0Sstevel@tonic-gate  * and limitations under the License.
13*0Sstevel@tonic-gate  *
14*0Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
15*0Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16*0Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
17*0Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
18*0Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
19*0Sstevel@tonic-gate  *
20*0Sstevel@tonic-gate  * CDDL HEADER END
21*0Sstevel@tonic-gate  */
22*0Sstevel@tonic-gate /*
23*0Sstevel@tonic-gate  * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
24*0Sstevel@tonic-gate  * Use is subject to license terms.
25*0Sstevel@tonic-gate  */
26*0Sstevel@tonic-gate 
27*0Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
28*0Sstevel@tonic-gate 
29*0Sstevel@tonic-gate #include <sys/types.h>
30*0Sstevel@tonic-gate #include <sys/errno.h>
31*0Sstevel@tonic-gate #include <sys/cpuvar.h>
32*0Sstevel@tonic-gate #include <sys/vfs.h>
33*0Sstevel@tonic-gate #include <sys/vnode.h>
34*0Sstevel@tonic-gate #include <sys/pathname.h>
35*0Sstevel@tonic-gate #include <sys/callb.h>
36*0Sstevel@tonic-gate #include <sys/fs/ufs_inode.h>
37*0Sstevel@tonic-gate #include <vm/anon.h>
38*0Sstevel@tonic-gate #include <sys/fs/swapnode.h>	/* for swapfs_minfree */
39*0Sstevel@tonic-gate #include <sys/kmem.h>
40*0Sstevel@tonic-gate #include <sys/cpr.h>
41*0Sstevel@tonic-gate #include <sys/conf.h>
42*0Sstevel@tonic-gate 
43*0Sstevel@tonic-gate /*
44*0Sstevel@tonic-gate  * CPR miscellaneous support routines
45*0Sstevel@tonic-gate  */
46*0Sstevel@tonic-gate #define	cpr_open(path, mode,  vpp)	(vn_open(path, UIO_SYSSPACE, \
47*0Sstevel@tonic-gate 		mode, 0600, vpp, CRCREAT, 0))
48*0Sstevel@tonic-gate #define	cpr_rdwr(rw, vp, basep, cnt)	(vn_rdwr(rw, vp,  (caddr_t)(basep), \
49*0Sstevel@tonic-gate 		cnt, 0LL, UIO_SYSSPACE, 0, (rlim64_t)MAXOFF_T, CRED(), \
50*0Sstevel@tonic-gate 		(ssize_t *)NULL))
51*0Sstevel@tonic-gate 
52*0Sstevel@tonic-gate extern void clkset(time_t);
53*0Sstevel@tonic-gate extern cpu_t *i_cpr_bootcpu(void);
54*0Sstevel@tonic-gate extern caddr_t i_cpr_map_setup(void);
55*0Sstevel@tonic-gate extern void i_cpr_free_memory_resources(void);
56*0Sstevel@tonic-gate 
57*0Sstevel@tonic-gate extern kmutex_t cpr_slock;
58*0Sstevel@tonic-gate extern size_t cpr_buf_size;
59*0Sstevel@tonic-gate extern char *cpr_buf;
60*0Sstevel@tonic-gate extern size_t cpr_pagedata_size;
61*0Sstevel@tonic-gate extern char *cpr_pagedata;
62*0Sstevel@tonic-gate extern int cpr_bufs_allocated;
63*0Sstevel@tonic-gate extern int cpr_bitmaps_allocated;
64*0Sstevel@tonic-gate 
65*0Sstevel@tonic-gate static struct cprconfig cprconfig;
66*0Sstevel@tonic-gate static int cprconfig_loaded = 0;
67*0Sstevel@tonic-gate static int cpr_statefile_ok(vnode_t *, int);
68*0Sstevel@tonic-gate static int cpr_p_online(cpu_t *, int);
69*0Sstevel@tonic-gate static void cpr_save_mp_state(void);
70*0Sstevel@tonic-gate int cpr_is_ufs(struct vfs *);
71*0Sstevel@tonic-gate 
72*0Sstevel@tonic-gate char cpr_default_path[] = CPR_DEFAULT;
73*0Sstevel@tonic-gate 
74*0Sstevel@tonic-gate #define	COMPRESS_PERCENT 40	/* approx compression ratio in percent */
75*0Sstevel@tonic-gate #define	SIZE_RATE	115	/* increase size by 15% */
76*0Sstevel@tonic-gate #define	INTEGRAL	100	/* for integer math */
77*0Sstevel@tonic-gate 
78*0Sstevel@tonic-gate 
79*0Sstevel@tonic-gate /*
80*0Sstevel@tonic-gate  * cmn_err() followed by a 1/4 second delay; this gives the
81*0Sstevel@tonic-gate  * logging service a chance to flush messages and helps avoid
82*0Sstevel@tonic-gate  * intermixing output from prom_printf().
83*0Sstevel@tonic-gate  */
84*0Sstevel@tonic-gate /*PRINTFLIKE2*/
85*0Sstevel@tonic-gate void
86*0Sstevel@tonic-gate cpr_err(int ce, const char *fmt, ...)
87*0Sstevel@tonic-gate {
88*0Sstevel@tonic-gate 	va_list adx;
89*0Sstevel@tonic-gate 
90*0Sstevel@tonic-gate 	va_start(adx, fmt);
91*0Sstevel@tonic-gate 	vcmn_err(ce, fmt, adx);
92*0Sstevel@tonic-gate 	va_end(adx);
93*0Sstevel@tonic-gate 	drv_usecwait(MICROSEC >> 2);
94*0Sstevel@tonic-gate }
95*0Sstevel@tonic-gate 
96*0Sstevel@tonic-gate 
97*0Sstevel@tonic-gate int
98*0Sstevel@tonic-gate cpr_init(int fcn)
99*0Sstevel@tonic-gate {
100*0Sstevel@tonic-gate 	/*
101*0Sstevel@tonic-gate 	 * Allow only one suspend/resume process.
102*0Sstevel@tonic-gate 	 */
103*0Sstevel@tonic-gate 	if (mutex_tryenter(&cpr_slock) == 0)
104*0Sstevel@tonic-gate 		return (EBUSY);
105*0Sstevel@tonic-gate 
106*0Sstevel@tonic-gate 	CPR->c_flags = 0;
107*0Sstevel@tonic-gate 	CPR->c_substate = 0;
108*0Sstevel@tonic-gate 	CPR->c_cprboot_magic = 0;
109*0Sstevel@tonic-gate 	CPR->c_alloc_cnt = 0;
110*0Sstevel@tonic-gate 
111*0Sstevel@tonic-gate 	CPR->c_fcn = fcn;
112*0Sstevel@tonic-gate 	if (fcn == AD_CPR_REUSABLE)
113*0Sstevel@tonic-gate 		CPR->c_flags |= C_REUSABLE;
114*0Sstevel@tonic-gate 	else
115*0Sstevel@tonic-gate 		CPR->c_flags |= C_SUSPENDING;
116*0Sstevel@tonic-gate 	if (fcn != AD_CPR_NOCOMPRESS && fcn != AD_CPR_TESTNOZ)
117*0Sstevel@tonic-gate 		CPR->c_flags |= C_COMPRESSING;
118*0Sstevel@tonic-gate 	/*
119*0Sstevel@tonic-gate 	 * reserve CPR_MAXCONTIG virtual pages for cpr_dump()
120*0Sstevel@tonic-gate 	 */
121*0Sstevel@tonic-gate 	CPR->c_mapping_area = i_cpr_map_setup();
122*0Sstevel@tonic-gate 	if (CPR->c_mapping_area == 0) {		/* no space in kernelmap */
123*0Sstevel@tonic-gate 		cpr_err(CE_CONT, "Unable to alloc from kernelmap.\n");
124*0Sstevel@tonic-gate 		mutex_exit(&cpr_slock);
125*0Sstevel@tonic-gate 		return (EAGAIN);
126*0Sstevel@tonic-gate 	}
127*0Sstevel@tonic-gate 	DEBUG3(cpr_err(CE_CONT, "Reserved virtual range from 0x%p for writing "
128*0Sstevel@tonic-gate 	    "kas\n", (void *)CPR->c_mapping_area));
129*0Sstevel@tonic-gate 
130*0Sstevel@tonic-gate 	return (0);
131*0Sstevel@tonic-gate }
132*0Sstevel@tonic-gate 
133*0Sstevel@tonic-gate /*
134*0Sstevel@tonic-gate  * This routine releases any resources used during the checkpoint.
135*0Sstevel@tonic-gate  */
136*0Sstevel@tonic-gate void
137*0Sstevel@tonic-gate cpr_done(void)
138*0Sstevel@tonic-gate {
139*0Sstevel@tonic-gate 	cpr_stat_cleanup();
140*0Sstevel@tonic-gate 	i_cpr_bitmap_cleanup();
141*0Sstevel@tonic-gate 
142*0Sstevel@tonic-gate 	/*
143*0Sstevel@tonic-gate 	 * Free pages used by cpr buffers.
144*0Sstevel@tonic-gate 	 */
145*0Sstevel@tonic-gate 	if (cpr_buf) {
146*0Sstevel@tonic-gate 		kmem_free(cpr_buf, cpr_buf_size);
147*0Sstevel@tonic-gate 		cpr_buf = NULL;
148*0Sstevel@tonic-gate 	}
149*0Sstevel@tonic-gate 	if (cpr_pagedata) {
150*0Sstevel@tonic-gate 		kmem_free(cpr_pagedata, cpr_pagedata_size);
151*0Sstevel@tonic-gate 		cpr_pagedata = NULL;
152*0Sstevel@tonic-gate 	}
153*0Sstevel@tonic-gate 
154*0Sstevel@tonic-gate 	i_cpr_free_memory_resources();
155*0Sstevel@tonic-gate 	mutex_exit(&cpr_slock);
156*0Sstevel@tonic-gate 	cpr_err(CE_CONT, "System has been resumed.\n");
157*0Sstevel@tonic-gate }
158*0Sstevel@tonic-gate 
159*0Sstevel@tonic-gate 
160*0Sstevel@tonic-gate /*
161*0Sstevel@tonic-gate  * reads config data into cprconfig
162*0Sstevel@tonic-gate  */
163*0Sstevel@tonic-gate static int
164*0Sstevel@tonic-gate cpr_get_config(void)
165*0Sstevel@tonic-gate {
166*0Sstevel@tonic-gate 	static char config_path[] = CPR_CONFIG;
167*0Sstevel@tonic-gate 	struct cprconfig *cf = &cprconfig;
168*0Sstevel@tonic-gate 	struct vnode *vp;
169*0Sstevel@tonic-gate 	char *fmt;
170*0Sstevel@tonic-gate 	int err;
171*0Sstevel@tonic-gate 
172*0Sstevel@tonic-gate 	if (cprconfig_loaded)
173*0Sstevel@tonic-gate 		return (0);
174*0Sstevel@tonic-gate 
175*0Sstevel@tonic-gate 	fmt = "cannot %s config file \"%s\", error %d\n";
176*0Sstevel@tonic-gate 	if (err = vn_open(config_path, UIO_SYSSPACE, FREAD, 0, &vp, 0, 0)) {
177*0Sstevel@tonic-gate 		cpr_err(CE_CONT, fmt, "open", config_path, err);
178*0Sstevel@tonic-gate 		return (err);
179*0Sstevel@tonic-gate 	}
180*0Sstevel@tonic-gate 
181*0Sstevel@tonic-gate 	err = cpr_rdwr(UIO_READ, vp, cf, sizeof (*cf));
182*0Sstevel@tonic-gate 	(void) VOP_CLOSE(vp, FREAD, 1, (offset_t)0, CRED());
183*0Sstevel@tonic-gate 	VN_RELE(vp);
184*0Sstevel@tonic-gate 	if (err) {
185*0Sstevel@tonic-gate 		cpr_err(CE_CONT, fmt, "read", config_path, err);
186*0Sstevel@tonic-gate 		return (err);
187*0Sstevel@tonic-gate 	}
188*0Sstevel@tonic-gate 
189*0Sstevel@tonic-gate 	if (cf->cf_magic == CPR_CONFIG_MAGIC)
190*0Sstevel@tonic-gate 		cprconfig_loaded = 1;
191*0Sstevel@tonic-gate 	else {
192*0Sstevel@tonic-gate 		cpr_err(CE_CONT, "invalid config file \"%s\", "
193*0Sstevel@tonic-gate 		    "rerun pmconfig(1M)\n", config_path);
194*0Sstevel@tonic-gate 		err = EINVAL;
195*0Sstevel@tonic-gate 	}
196*0Sstevel@tonic-gate 
197*0Sstevel@tonic-gate 	return (err);
198*0Sstevel@tonic-gate }
199*0Sstevel@tonic-gate 
200*0Sstevel@tonic-gate 
201*0Sstevel@tonic-gate /*
202*0Sstevel@tonic-gate  * concat fs and path fields of the cprconfig structure;
203*0Sstevel@tonic-gate  * returns pointer to the base of static data
204*0Sstevel@tonic-gate  */
205*0Sstevel@tonic-gate static char *
206*0Sstevel@tonic-gate cpr_cprconfig_to_path(void)
207*0Sstevel@tonic-gate {
208*0Sstevel@tonic-gate 	static char full_path[MAXNAMELEN];
209*0Sstevel@tonic-gate 	struct cprconfig *cf = &cprconfig;
210*0Sstevel@tonic-gate 	char *ptr;
211*0Sstevel@tonic-gate 
212*0Sstevel@tonic-gate 	/*
213*0Sstevel@tonic-gate 	 * build /fs/path without extra '/'
214*0Sstevel@tonic-gate 	 */
215*0Sstevel@tonic-gate 	(void) strcpy(full_path, cf->cf_fs);
216*0Sstevel@tonic-gate 	if (strcmp(cf->cf_fs, "/"))
217*0Sstevel@tonic-gate 		(void) strcat(full_path, "/");
218*0Sstevel@tonic-gate 	ptr = cf->cf_path;
219*0Sstevel@tonic-gate 	if (*ptr == '/')
220*0Sstevel@tonic-gate 		ptr++;
221*0Sstevel@tonic-gate 	(void) strcat(full_path, ptr);
222*0Sstevel@tonic-gate 	return (full_path);
223*0Sstevel@tonic-gate }
224*0Sstevel@tonic-gate 
225*0Sstevel@tonic-gate 
226*0Sstevel@tonic-gate /*
227*0Sstevel@tonic-gate  * Verify that the information in the configuration file regarding the
228*0Sstevel@tonic-gate  * location for the statefile is still valid, depending on cf_type.
229*0Sstevel@tonic-gate  * for CFT_UFS, cf_fs must still be a mounted filesystem, it must be
230*0Sstevel@tonic-gate  *	mounted on the same device as when pmconfig was last run,
231*0Sstevel@tonic-gate  *	and the translation of that device to a node in the prom's
232*0Sstevel@tonic-gate  *	device tree must be the same as when pmconfig was last run.
233*0Sstevel@tonic-gate  * for CFT_SPEC, cf_path must be the path to a block special file,
234*0Sstevel@tonic-gate  *	it must have no file system mounted on it,
235*0Sstevel@tonic-gate  *	and the translation of that device to a node in the prom's
236*0Sstevel@tonic-gate  *	device tree must be the same as when pmconfig was last run.
237*0Sstevel@tonic-gate  */
238*0Sstevel@tonic-gate static int
239*0Sstevel@tonic-gate cpr_verify_statefile_path(void)
240*0Sstevel@tonic-gate {
241*0Sstevel@tonic-gate 	struct cprconfig *cf = &cprconfig;
242*0Sstevel@tonic-gate 	static const char long_name[] = "Statefile pathname is too long.\n";
243*0Sstevel@tonic-gate 	static const char lookup_fmt[] = "Lookup failed for "
244*0Sstevel@tonic-gate 	    "cpr statefile device %s.\n";
245*0Sstevel@tonic-gate 	static const char path_chg_fmt[] = "Device path for statefile "
246*0Sstevel@tonic-gate 	    "has changed from %s to %s.\t%s\n";
247*0Sstevel@tonic-gate 	static const char rerun[] = "Please rerun pmconfig(1m).";
248*0Sstevel@tonic-gate 	struct vfs *vfsp = NULL, *vfsp_save = rootvfs;
249*0Sstevel@tonic-gate 	ufsvfs_t *ufsvfsp = (ufsvfs_t *)rootvfs->vfs_data;
250*0Sstevel@tonic-gate 	ufsvfs_t *ufsvfsp_save = ufsvfsp;
251*0Sstevel@tonic-gate 	int error;
252*0Sstevel@tonic-gate 	struct vnode *vp;
253*0Sstevel@tonic-gate 	char *slash, *tail, *longest;
254*0Sstevel@tonic-gate 	char *errstr;
255*0Sstevel@tonic-gate 	int found = 0;
256*0Sstevel@tonic-gate 	union {
257*0Sstevel@tonic-gate 		char un_devpath[OBP_MAXPATHLEN];
258*0Sstevel@tonic-gate 		char un_sfpath[MAXNAMELEN];
259*0Sstevel@tonic-gate 	} un;
260*0Sstevel@tonic-gate #define	devpath	un.un_devpath
261*0Sstevel@tonic-gate #define	sfpath	un.un_sfpath
262*0Sstevel@tonic-gate 
263*0Sstevel@tonic-gate 	ASSERT(cprconfig_loaded);
264*0Sstevel@tonic-gate 	/*
265*0Sstevel@tonic-gate 	 * We need not worry about locking or the timing of releasing
266*0Sstevel@tonic-gate 	 * the vnode, since we are single-threaded now.
267*0Sstevel@tonic-gate 	 */
268*0Sstevel@tonic-gate 
269*0Sstevel@tonic-gate 	switch (cf->cf_type) {
270*0Sstevel@tonic-gate 	case CFT_SPEC:
271*0Sstevel@tonic-gate 		if (strlen(cf->cf_path) > sizeof (sfpath)) {
272*0Sstevel@tonic-gate 			cpr_err(CE_CONT, long_name);
273*0Sstevel@tonic-gate 			return (ENAMETOOLONG);
274*0Sstevel@tonic-gate 		}
275*0Sstevel@tonic-gate 		if ((error = lookupname(cf->cf_devfs,
276*0Sstevel@tonic-gate 		    UIO_SYSSPACE, FOLLOW, NULLVPP, &vp)) != 0) {
277*0Sstevel@tonic-gate 			cpr_err(CE_CONT, lookup_fmt, cf->cf_devfs);
278*0Sstevel@tonic-gate 			return (error);
279*0Sstevel@tonic-gate 		}
280*0Sstevel@tonic-gate 		if (vp->v_type != VBLK)
281*0Sstevel@tonic-gate 			errstr = "statefile must be a block device";
282*0Sstevel@tonic-gate 		else if (vfs_devismounted(vp->v_rdev))
283*0Sstevel@tonic-gate 			errstr = "statefile device must not "
284*0Sstevel@tonic-gate 			    "have a file system mounted on it";
285*0Sstevel@tonic-gate 		else if (IS_SWAPVP(vp))
286*0Sstevel@tonic-gate 			errstr = "statefile device must not "
287*0Sstevel@tonic-gate 			    "be configured as swap file";
288*0Sstevel@tonic-gate 		else
289*0Sstevel@tonic-gate 			errstr = NULL;
290*0Sstevel@tonic-gate 
291*0Sstevel@tonic-gate 		VN_RELE(vp);
292*0Sstevel@tonic-gate 		if (errstr) {
293*0Sstevel@tonic-gate 			cpr_err(CE_CONT, "%s.\n", errstr);
294*0Sstevel@tonic-gate 			return (ENOTSUP);
295*0Sstevel@tonic-gate 		}
296*0Sstevel@tonic-gate 
297*0Sstevel@tonic-gate 		error = i_devname_to_promname(cf->cf_devfs, devpath,
298*0Sstevel@tonic-gate 		    OBP_MAXPATHLEN);
299*0Sstevel@tonic-gate 		if (error || strcmp(devpath, cf->cf_dev_prom)) {
300*0Sstevel@tonic-gate 			cpr_err(CE_CONT, path_chg_fmt,
301*0Sstevel@tonic-gate 			    cf->cf_dev_prom, devpath, rerun);
302*0Sstevel@tonic-gate 		}
303*0Sstevel@tonic-gate 		return (error);
304*0Sstevel@tonic-gate 	case CFT_UFS:
305*0Sstevel@tonic-gate 		break;		/* don't indent all the original code */
306*0Sstevel@tonic-gate 	default:
307*0Sstevel@tonic-gate 		cpr_err(CE_PANIC, "invalid cf_type");
308*0Sstevel@tonic-gate 	}
309*0Sstevel@tonic-gate 
310*0Sstevel@tonic-gate 	/*
311*0Sstevel@tonic-gate 	 * The original code for UFS statefile
312*0Sstevel@tonic-gate 	 */
313*0Sstevel@tonic-gate 	if (strlen(cf->cf_fs) + strlen(cf->cf_path) + 2 > sizeof (sfpath)) {
314*0Sstevel@tonic-gate 		cpr_err(CE_CONT, long_name);
315*0Sstevel@tonic-gate 		return (ENAMETOOLONG);
316*0Sstevel@tonic-gate 	}
317*0Sstevel@tonic-gate 
318*0Sstevel@tonic-gate 	bzero(sfpath, sizeof (sfpath));
319*0Sstevel@tonic-gate 	(void) strcpy(sfpath, cpr_cprconfig_to_path());
320*0Sstevel@tonic-gate 
321*0Sstevel@tonic-gate 	if (*sfpath != '/') {
322*0Sstevel@tonic-gate 		cpr_err(CE_CONT, "Statefile pathname %s "
323*0Sstevel@tonic-gate 		    "must begin with a /\n", sfpath);
324*0Sstevel@tonic-gate 		return (EINVAL);
325*0Sstevel@tonic-gate 	}
326*0Sstevel@tonic-gate 
327*0Sstevel@tonic-gate 	/*
328*0Sstevel@tonic-gate 	 * Find the longest prefix of the statefile pathname which
329*0Sstevel@tonic-gate 	 * is the mountpoint of a filesystem.  This string must
330*0Sstevel@tonic-gate 	 * match the cf_fs field we read from the config file.  Other-
331*0Sstevel@tonic-gate 	 * wise the user has changed things without running pmconfig.
332*0Sstevel@tonic-gate 	 */
333*0Sstevel@tonic-gate 	tail = longest = sfpath + 1;	/* pt beyond the leading "/" */
334*0Sstevel@tonic-gate 	while ((slash = strchr(tail, '/')) != NULL) {
335*0Sstevel@tonic-gate 		*slash = '\0';	  /* temporarily terminate the string */
336*0Sstevel@tonic-gate 		if ((error = lookupname(sfpath,
337*0Sstevel@tonic-gate 		    UIO_SYSSPACE, FOLLOW, NULLVPP, &vp)) != 0) {
338*0Sstevel@tonic-gate 			*slash = '/';
339*0Sstevel@tonic-gate 			cpr_err(CE_CONT, "A directory in the "
340*0Sstevel@tonic-gate 			    "statefile path %s was not found.\n", sfpath);
341*0Sstevel@tonic-gate 			VN_RELE(vp);
342*0Sstevel@tonic-gate 
343*0Sstevel@tonic-gate 			return (error);
344*0Sstevel@tonic-gate 		}
345*0Sstevel@tonic-gate 
346*0Sstevel@tonic-gate 		vfs_list_read_lock();
347*0Sstevel@tonic-gate 		vfsp = rootvfs;
348*0Sstevel@tonic-gate 		do {
349*0Sstevel@tonic-gate 			ufsvfsp = (struct ufsvfs *)vfsp->vfs_data;
350*0Sstevel@tonic-gate 			if (ufsvfsp != NULL && ufsvfsp->vfs_root == vp) {
351*0Sstevel@tonic-gate 				found = 1;
352*0Sstevel@tonic-gate 				break;
353*0Sstevel@tonic-gate 			}
354*0Sstevel@tonic-gate 			vfsp = vfsp->vfs_next;
355*0Sstevel@tonic-gate 		} while (vfsp != rootvfs);
356*0Sstevel@tonic-gate 		vfs_list_unlock();
357*0Sstevel@tonic-gate 
358*0Sstevel@tonic-gate 		/*
359*0Sstevel@tonic-gate 		 * If we have found a filesystem mounted on the current
360*0Sstevel@tonic-gate 		 * path prefix, remember the end of the string in
361*0Sstevel@tonic-gate 		 * "longest".  If it happens to be the the exact fs
362*0Sstevel@tonic-gate 		 * saved in the configuration file, save the current
363*0Sstevel@tonic-gate 		 * ufsvfsp so we can make additional checks further down.
364*0Sstevel@tonic-gate 		 */
365*0Sstevel@tonic-gate 		if (found) {
366*0Sstevel@tonic-gate 			longest = slash;
367*0Sstevel@tonic-gate 			if (strcmp(cf->cf_fs, sfpath) == 0) {
368*0Sstevel@tonic-gate 				ufsvfsp_save = ufsvfsp;
369*0Sstevel@tonic-gate 				vfsp_save = vfsp;
370*0Sstevel@tonic-gate 			}
371*0Sstevel@tonic-gate 			found = 0;
372*0Sstevel@tonic-gate 		}
373*0Sstevel@tonic-gate 
374*0Sstevel@tonic-gate 		VN_RELE(vp);
375*0Sstevel@tonic-gate 		*slash = '/';
376*0Sstevel@tonic-gate 		tail = slash + 1;
377*0Sstevel@tonic-gate 	}
378*0Sstevel@tonic-gate 	*longest = '\0';
379*0Sstevel@tonic-gate 	if (cpr_is_ufs(vfsp_save) == 0 || strcmp(cf->cf_fs, sfpath)) {
380*0Sstevel@tonic-gate 		cpr_err(CE_CONT, "Filesystem containing "
381*0Sstevel@tonic-gate 		    "the statefile when pmconfig was run (%s) has "
382*0Sstevel@tonic-gate 		    "changed to %s. %s\n", cf->cf_fs, sfpath, rerun);
383*0Sstevel@tonic-gate 		return (EINVAL);
384*0Sstevel@tonic-gate 	}
385*0Sstevel@tonic-gate 
386*0Sstevel@tonic-gate 	if ((error = lookupname(cf->cf_devfs,
387*0Sstevel@tonic-gate 	    UIO_SYSSPACE, FOLLOW, NULLVPP, &vp)) != 0) {
388*0Sstevel@tonic-gate 		cpr_err(CE_CONT, lookup_fmt, cf->cf_devfs);
389*0Sstevel@tonic-gate 		return (error);
390*0Sstevel@tonic-gate 	}
391*0Sstevel@tonic-gate 
392*0Sstevel@tonic-gate 	if (ufsvfsp_save->vfs_devvp->v_rdev != vp->v_rdev) {
393*0Sstevel@tonic-gate 		cpr_err(CE_CONT, "Filesystem containing "
394*0Sstevel@tonic-gate 		    "statefile no longer mounted on device %s. "
395*0Sstevel@tonic-gate 		    "See power.conf(4).", cf->cf_devfs);
396*0Sstevel@tonic-gate 		VN_RELE(vp);
397*0Sstevel@tonic-gate 		return (ENXIO);
398*0Sstevel@tonic-gate 	}
399*0Sstevel@tonic-gate 	VN_RELE(vp);
400*0Sstevel@tonic-gate 
401*0Sstevel@tonic-gate 	error = i_devname_to_promname(cf->cf_devfs, devpath, OBP_MAXPATHLEN);
402*0Sstevel@tonic-gate 	if (error || strcmp(devpath, cf->cf_dev_prom)) {
403*0Sstevel@tonic-gate 		cpr_err(CE_CONT, path_chg_fmt,
404*0Sstevel@tonic-gate 		    cf->cf_dev_prom, devpath, rerun);
405*0Sstevel@tonic-gate 		return (error);
406*0Sstevel@tonic-gate 	}
407*0Sstevel@tonic-gate 
408*0Sstevel@tonic-gate 	return (0);
409*0Sstevel@tonic-gate }
410*0Sstevel@tonic-gate 
411*0Sstevel@tonic-gate /*
412*0Sstevel@tonic-gate  * Make sure that the statefile can be used as a block special statefile
413*0Sstevel@tonic-gate  * (meaning that is exists and has nothing mounted on it)
414*0Sstevel@tonic-gate  * Returns errno if not a valid statefile.
415*0Sstevel@tonic-gate  */
416*0Sstevel@tonic-gate int
417*0Sstevel@tonic-gate cpr_check_spec_statefile(void)
418*0Sstevel@tonic-gate {
419*0Sstevel@tonic-gate 	int err;
420*0Sstevel@tonic-gate 
421*0Sstevel@tonic-gate 	if (err = cpr_get_config())
422*0Sstevel@tonic-gate 		return (err);
423*0Sstevel@tonic-gate 	ASSERT(cprconfig.cf_type == CFT_SPEC);
424*0Sstevel@tonic-gate 
425*0Sstevel@tonic-gate 	if (cprconfig.cf_devfs == NULL)
426*0Sstevel@tonic-gate 		return (ENXIO);
427*0Sstevel@tonic-gate 
428*0Sstevel@tonic-gate 	return (cpr_verify_statefile_path());
429*0Sstevel@tonic-gate 
430*0Sstevel@tonic-gate }
431*0Sstevel@tonic-gate 
432*0Sstevel@tonic-gate int
433*0Sstevel@tonic-gate cpr_alloc_statefile(int alloc_retry)
434*0Sstevel@tonic-gate {
435*0Sstevel@tonic-gate 	register int rc = 0;
436*0Sstevel@tonic-gate 	char *str;
437*0Sstevel@tonic-gate 
438*0Sstevel@tonic-gate 	/*
439*0Sstevel@tonic-gate 	 * Statefile size validation. If checkpoint the first time, disk blocks
440*0Sstevel@tonic-gate 	 * allocation will be done; otherwise, just do file size check.
441*0Sstevel@tonic-gate 	 * if statefile allocation is being retried, C_VP will be inited
442*0Sstevel@tonic-gate 	 */
443*0Sstevel@tonic-gate 	if (alloc_retry) {
444*0Sstevel@tonic-gate 		str = "\n-->Retrying statefile allocation...";
445*0Sstevel@tonic-gate 		if (cpr_debug & (LEVEL1 | LEVEL7))
446*0Sstevel@tonic-gate 			errp(str);
447*0Sstevel@tonic-gate 		if (C_VP->v_type != VBLK)
448*0Sstevel@tonic-gate 			(void) VOP_DUMPCTL(C_VP, DUMP_FREE, NULL);
449*0Sstevel@tonic-gate 	} else {
450*0Sstevel@tonic-gate 		/*
451*0Sstevel@tonic-gate 		 * Open an exiting file for writing, the state file needs to be
452*0Sstevel@tonic-gate 		 * pre-allocated since we can't and don't want to do allocation
453*0Sstevel@tonic-gate 		 * during checkpoint (too much of the OS is disabled).
454*0Sstevel@tonic-gate 		 *    - do a preliminary size checking here, if it is too small,
455*0Sstevel@tonic-gate 		 *	allocate more space internally and retry.
456*0Sstevel@tonic-gate 		 *    - check the vp to make sure it's the right type.
457*0Sstevel@tonic-gate 		 */
458*0Sstevel@tonic-gate 		char *path = cpr_build_statefile_path();
459*0Sstevel@tonic-gate 
460*0Sstevel@tonic-gate 		if (path == NULL)
461*0Sstevel@tonic-gate 			return (ENXIO);
462*0Sstevel@tonic-gate 		else if (rc = cpr_verify_statefile_path())
463*0Sstevel@tonic-gate 			return (rc);
464*0Sstevel@tonic-gate 
465*0Sstevel@tonic-gate 		if (rc = vn_open(path, UIO_SYSSPACE,
466*0Sstevel@tonic-gate 		    FCREAT|FWRITE, 0600, &C_VP, CRCREAT, 0)) {
467*0Sstevel@tonic-gate 			cpr_err(CE_WARN, "cannot open statefile %s", path);
468*0Sstevel@tonic-gate 			return (rc);
469*0Sstevel@tonic-gate 		}
470*0Sstevel@tonic-gate 	}
471*0Sstevel@tonic-gate 
472*0Sstevel@tonic-gate 	/*
473*0Sstevel@tonic-gate 	 * Only ufs and block special statefiles supported
474*0Sstevel@tonic-gate 	 */
475*0Sstevel@tonic-gate 	if (C_VP->v_type != VREG && C_VP->v_type != VBLK) {
476*0Sstevel@tonic-gate 		cpr_err(CE_CONT,
477*0Sstevel@tonic-gate 		    "Statefile must be regular file or block special file.");
478*0Sstevel@tonic-gate 		return (EACCES);
479*0Sstevel@tonic-gate 	}
480*0Sstevel@tonic-gate 
481*0Sstevel@tonic-gate 	if (rc = cpr_statefile_ok(C_VP, alloc_retry))
482*0Sstevel@tonic-gate 		return (rc);
483*0Sstevel@tonic-gate 
484*0Sstevel@tonic-gate 	if (C_VP->v_type != VBLK) {
485*0Sstevel@tonic-gate 		/*
486*0Sstevel@tonic-gate 		 * sync out the fs change due to the statefile reservation.
487*0Sstevel@tonic-gate 		 */
488*0Sstevel@tonic-gate 		(void) VFS_SYNC(C_VP->v_vfsp, 0, CRED());
489*0Sstevel@tonic-gate 
490*0Sstevel@tonic-gate 		/*
491*0Sstevel@tonic-gate 		 * Validate disk blocks allocation for the state file.
492*0Sstevel@tonic-gate 		 * Ask the file system prepare itself for the dump operation.
493*0Sstevel@tonic-gate 		 */
494*0Sstevel@tonic-gate 		if (rc = VOP_DUMPCTL(C_VP, DUMP_ALLOC, NULL)) {
495*0Sstevel@tonic-gate 			cpr_err(CE_CONT, "Error allocating "
496*0Sstevel@tonic-gate 			    "blocks for cpr statefile.");
497*0Sstevel@tonic-gate 			return (rc);
498*0Sstevel@tonic-gate 		}
499*0Sstevel@tonic-gate 	}
500*0Sstevel@tonic-gate 	return (0);
501*0Sstevel@tonic-gate }
502*0Sstevel@tonic-gate 
503*0Sstevel@tonic-gate 
504*0Sstevel@tonic-gate /*
505*0Sstevel@tonic-gate  * lookup device size in blocks,
506*0Sstevel@tonic-gate  * and return available space in bytes
507*0Sstevel@tonic-gate  */
508*0Sstevel@tonic-gate size_t
509*0Sstevel@tonic-gate cpr_get_devsize(dev_t dev)
510*0Sstevel@tonic-gate {
511*0Sstevel@tonic-gate 	size_t bytes = 0;
512*0Sstevel@tonic-gate 	int64_t Nblocks;
513*0Sstevel@tonic-gate 	int nblocks;
514*0Sstevel@tonic-gate 
515*0Sstevel@tonic-gate 	if ((Nblocks = bdev_Size(dev)) != -1)
516*0Sstevel@tonic-gate 		bytes = dbtob(Nblocks);
517*0Sstevel@tonic-gate 	else if ((nblocks = bdev_size(dev)) != -1)
518*0Sstevel@tonic-gate 		bytes = dbtob(nblocks);
519*0Sstevel@tonic-gate 
520*0Sstevel@tonic-gate 	if (bytes > CPR_SPEC_OFFSET)
521*0Sstevel@tonic-gate 		bytes -= CPR_SPEC_OFFSET;
522*0Sstevel@tonic-gate 	else
523*0Sstevel@tonic-gate 		bytes = 0;
524*0Sstevel@tonic-gate 
525*0Sstevel@tonic-gate 	return (bytes);
526*0Sstevel@tonic-gate }
527*0Sstevel@tonic-gate 
528*0Sstevel@tonic-gate 
529*0Sstevel@tonic-gate /*
530*0Sstevel@tonic-gate  * increase statefile size
531*0Sstevel@tonic-gate  */
532*0Sstevel@tonic-gate static int
533*0Sstevel@tonic-gate cpr_grow_statefile(vnode_t *vp, u_longlong_t newsize)
534*0Sstevel@tonic-gate {
535*0Sstevel@tonic-gate 	extern uchar_t cpr_pagecopy[];
536*0Sstevel@tonic-gate 	struct inode *ip = VTOI(vp);
537*0Sstevel@tonic-gate 	u_longlong_t offset;
538*0Sstevel@tonic-gate 	int error, increase;
539*0Sstevel@tonic-gate 	ssize_t resid;
540*0Sstevel@tonic-gate 
541*0Sstevel@tonic-gate 	rw_enter(&ip->i_contents, RW_READER);
542*0Sstevel@tonic-gate 	increase = (ip->i_size < newsize);
543*0Sstevel@tonic-gate 	offset = ip->i_size;
544*0Sstevel@tonic-gate 	rw_exit(&ip->i_contents);
545*0Sstevel@tonic-gate 
546*0Sstevel@tonic-gate 	if (increase == 0)
547*0Sstevel@tonic-gate 		return (0);
548*0Sstevel@tonic-gate 
549*0Sstevel@tonic-gate 	/*
550*0Sstevel@tonic-gate 	 * write to each logical block to reserve disk space
551*0Sstevel@tonic-gate 	 */
552*0Sstevel@tonic-gate 	error = 0;
553*0Sstevel@tonic-gate 	cpr_pagecopy[0] = '1';
554*0Sstevel@tonic-gate 	for (; offset < newsize; offset += ip->i_fs->fs_bsize) {
555*0Sstevel@tonic-gate 		if (error = vn_rdwr(UIO_WRITE, vp, (caddr_t)cpr_pagecopy,
556*0Sstevel@tonic-gate 		    ip->i_fs->fs_bsize, (offset_t)offset, UIO_SYSSPACE, 0,
557*0Sstevel@tonic-gate 		    (rlim64_t)MAXOFF_T, CRED(), &resid)) {
558*0Sstevel@tonic-gate 			if (error == ENOSPC) {
559*0Sstevel@tonic-gate 				cpr_err(CE_WARN, "error %d while reserving "
560*0Sstevel@tonic-gate 				    "disk space for statefile %s\n"
561*0Sstevel@tonic-gate 				    "wanted %lld bytes, file is %lld short",
562*0Sstevel@tonic-gate 				    error, cpr_cprconfig_to_path(),
563*0Sstevel@tonic-gate 				    newsize, newsize - offset);
564*0Sstevel@tonic-gate 			}
565*0Sstevel@tonic-gate 			break;
566*0Sstevel@tonic-gate 		}
567*0Sstevel@tonic-gate 	}
568*0Sstevel@tonic-gate 	return (error);
569*0Sstevel@tonic-gate }
570*0Sstevel@tonic-gate 
571*0Sstevel@tonic-gate 
572*0Sstevel@tonic-gate /*
573*0Sstevel@tonic-gate  * do a simple estimate of the space needed to hold the statefile
574*0Sstevel@tonic-gate  * taking compression into account, but be fairly conservative
575*0Sstevel@tonic-gate  * so we have a better chance of completing; when dump fails,
576*0Sstevel@tonic-gate  * the retry cost is fairly high.
577*0Sstevel@tonic-gate  *
578*0Sstevel@tonic-gate  * Do disk blocks allocation for the state file if no space has
579*0Sstevel@tonic-gate  * been allocated yet. Since the state file will not be removed,
580*0Sstevel@tonic-gate  * allocation should only be done once.
581*0Sstevel@tonic-gate  */
582*0Sstevel@tonic-gate static int
583*0Sstevel@tonic-gate cpr_statefile_ok(vnode_t *vp, int alloc_retry)
584*0Sstevel@tonic-gate {
585*0Sstevel@tonic-gate 	extern size_t cpr_bitmap_size;
586*0Sstevel@tonic-gate 	struct inode *ip = VTOI(vp);
587*0Sstevel@tonic-gate 	const int UCOMP_RATE = 20; /* comp. ratio*10 for user pages */
588*0Sstevel@tonic-gate 	u_longlong_t size, isize, ksize, raw_data;
589*0Sstevel@tonic-gate 	char *str, *est_fmt;
590*0Sstevel@tonic-gate 	size_t space;
591*0Sstevel@tonic-gate 	int error;
592*0Sstevel@tonic-gate 
593*0Sstevel@tonic-gate 	/*
594*0Sstevel@tonic-gate 	 * number of pages short for swapping.
595*0Sstevel@tonic-gate 	 */
596*0Sstevel@tonic-gate 	STAT->cs_nosw_pages = k_anoninfo.ani_mem_resv;
597*0Sstevel@tonic-gate 	if (STAT->cs_nosw_pages < 0)
598*0Sstevel@tonic-gate 		STAT->cs_nosw_pages = 0;
599*0Sstevel@tonic-gate 
600*0Sstevel@tonic-gate 	str = "cpr_statefile_ok:";
601*0Sstevel@tonic-gate 
602*0Sstevel@tonic-gate 	DEBUG9(errp("Phys swap: max=%lu resv=%lu\n",
603*0Sstevel@tonic-gate 	    k_anoninfo.ani_max, k_anoninfo.ani_phys_resv));
604*0Sstevel@tonic-gate 	DEBUG9(errp("Mem swap: max=%ld resv=%lu\n",
605*0Sstevel@tonic-gate 	    MAX(availrmem - swapfs_minfree, 0),
606*0Sstevel@tonic-gate 	    k_anoninfo.ani_mem_resv));
607*0Sstevel@tonic-gate 	DEBUG9(errp("Total available swap: %ld\n",
608*0Sstevel@tonic-gate 		CURRENT_TOTAL_AVAILABLE_SWAP));
609*0Sstevel@tonic-gate 
610*0Sstevel@tonic-gate 	/*
611*0Sstevel@tonic-gate 	 * try increasing filesize by 15%
612*0Sstevel@tonic-gate 	 */
613*0Sstevel@tonic-gate 	if (alloc_retry) {
614*0Sstevel@tonic-gate 		/*
615*0Sstevel@tonic-gate 		 * block device doesn't get any bigger
616*0Sstevel@tonic-gate 		 */
617*0Sstevel@tonic-gate 		if (vp->v_type == VBLK) {
618*0Sstevel@tonic-gate 			if (cpr_debug & (LEVEL1 | LEVEL6))
619*0Sstevel@tonic-gate 				errp("Retry statefile on special file\n");
620*0Sstevel@tonic-gate 			return (ENOMEM);
621*0Sstevel@tonic-gate 		} else {
622*0Sstevel@tonic-gate 			rw_enter(&ip->i_contents, RW_READER);
623*0Sstevel@tonic-gate 			size = (ip->i_size * SIZE_RATE) / INTEGRAL;
624*0Sstevel@tonic-gate 			rw_exit(&ip->i_contents);
625*0Sstevel@tonic-gate 		}
626*0Sstevel@tonic-gate 		if (cpr_debug & (LEVEL1 | LEVEL6))
627*0Sstevel@tonic-gate 			errp("Retry statefile size = %lld\n", size);
628*0Sstevel@tonic-gate 	} else {
629*0Sstevel@tonic-gate 		u_longlong_t cpd_size;
630*0Sstevel@tonic-gate 		pgcnt_t npages, nback;
631*0Sstevel@tonic-gate 		int ndvram;
632*0Sstevel@tonic-gate 
633*0Sstevel@tonic-gate 		ndvram = 0;
634*0Sstevel@tonic-gate 		(void) callb_execute_class(CB_CL_CPR_FB, (int)&ndvram);
635*0Sstevel@tonic-gate 		if (cpr_debug & (LEVEL1 | LEVEL6))
636*0Sstevel@tonic-gate 			errp("ndvram size = %d\n", ndvram);
637*0Sstevel@tonic-gate 
638*0Sstevel@tonic-gate 		/*
639*0Sstevel@tonic-gate 		 * estimate 1 cpd_t for every (CPR_MAXCONTIG / 2) pages
640*0Sstevel@tonic-gate 		 */
641*0Sstevel@tonic-gate 		npages = cpr_count_kpages(REGULAR_BITMAP, cpr_nobit);
642*0Sstevel@tonic-gate 		cpd_size = sizeof (cpd_t) * (npages / (CPR_MAXCONTIG / 2));
643*0Sstevel@tonic-gate 		raw_data = cpd_size + cpr_bitmap_size;
644*0Sstevel@tonic-gate 		ksize = ndvram + mmu_ptob(npages);
645*0Sstevel@tonic-gate 
646*0Sstevel@tonic-gate 		est_fmt = "%s estimated size with "
647*0Sstevel@tonic-gate 		    "%scompression %lld, ksize %lld\n";
648*0Sstevel@tonic-gate 		nback = mmu_ptob(STAT->cs_nosw_pages);
649*0Sstevel@tonic-gate 		if (CPR->c_flags & C_COMPRESSING) {
650*0Sstevel@tonic-gate 			size = ((ksize * COMPRESS_PERCENT) / INTEGRAL) +
651*0Sstevel@tonic-gate 			    raw_data + ((nback * 10) / UCOMP_RATE);
652*0Sstevel@tonic-gate 			DEBUG1(errp(est_fmt, str, "", size, ksize));
653*0Sstevel@tonic-gate 		} else {
654*0Sstevel@tonic-gate 			size = ksize + raw_data + nback;
655*0Sstevel@tonic-gate 			DEBUG1(errp(est_fmt, str, "no ", size, ksize));
656*0Sstevel@tonic-gate 		}
657*0Sstevel@tonic-gate 	}
658*0Sstevel@tonic-gate 
659*0Sstevel@tonic-gate 	/*
660*0Sstevel@tonic-gate 	 * All this is much simpler for a block device
661*0Sstevel@tonic-gate 	 */
662*0Sstevel@tonic-gate 	if (vp->v_type == VBLK) {
663*0Sstevel@tonic-gate 		space = cpr_get_devsize(vp->v_rdev);
664*0Sstevel@tonic-gate 		if (cpr_debug & (LEVEL1 | LEVEL6))
665*0Sstevel@tonic-gate 			errp("statefile dev size %lu\n", space);
666*0Sstevel@tonic-gate 
667*0Sstevel@tonic-gate 		/*
668*0Sstevel@tonic-gate 		 * Export the estimated filesize info, this value will be
669*0Sstevel@tonic-gate 		 * compared before dumping out the statefile in the case of
670*0Sstevel@tonic-gate 		 * no compression.
671*0Sstevel@tonic-gate 		 */
672*0Sstevel@tonic-gate 		STAT->cs_est_statefsz = size;
673*0Sstevel@tonic-gate 		if (cpr_debug & (LEVEL1 | LEVEL6))
674*0Sstevel@tonic-gate 			errp("%s Estimated statefile size %d, space %lu\n",
675*0Sstevel@tonic-gate 			    str, size, space);
676*0Sstevel@tonic-gate 		if (size > space) {
677*0Sstevel@tonic-gate 			cpr_err(CE_CONT, "Statefile partition too small.");
678*0Sstevel@tonic-gate 			return (ENOMEM);
679*0Sstevel@tonic-gate 		}
680*0Sstevel@tonic-gate 		return (0);
681*0Sstevel@tonic-gate 	} else {
682*0Sstevel@tonic-gate 		if (CPR->c_alloc_cnt++ > C_MAX_ALLOC_RETRY) {
683*0Sstevel@tonic-gate 			cpr_err(CE_CONT, "Statefile allocation retry failed\n");
684*0Sstevel@tonic-gate 			return (ENOMEM);
685*0Sstevel@tonic-gate 		}
686*0Sstevel@tonic-gate 
687*0Sstevel@tonic-gate 		/*
688*0Sstevel@tonic-gate 		 * Estimate space needed for the state file.
689*0Sstevel@tonic-gate 		 *
690*0Sstevel@tonic-gate 		 * State file size in bytes:
691*0Sstevel@tonic-gate 		 * 	kernel size + non-cache pte seg +
692*0Sstevel@tonic-gate 		 *	bitmap size + cpr state file headers size
693*0Sstevel@tonic-gate 		 * (round up to fs->fs_bsize)
694*0Sstevel@tonic-gate 		 */
695*0Sstevel@tonic-gate 		size = blkroundup(ip->i_fs, size);
696*0Sstevel@tonic-gate 
697*0Sstevel@tonic-gate 		/*
698*0Sstevel@tonic-gate 		 * Export the estimated filesize info, this value will be
699*0Sstevel@tonic-gate 		 * compared before dumping out the statefile in the case of
700*0Sstevel@tonic-gate 		 * no compression.
701*0Sstevel@tonic-gate 		 */
702*0Sstevel@tonic-gate 		STAT->cs_est_statefsz = size;
703*0Sstevel@tonic-gate 		error = cpr_grow_statefile(vp, size);
704*0Sstevel@tonic-gate 		if (cpr_debug & (LEVEL1 | LEVEL6)) {
705*0Sstevel@tonic-gate 			rw_enter(&ip->i_contents, RW_READER);
706*0Sstevel@tonic-gate 			isize = ip->i_size;
707*0Sstevel@tonic-gate 			rw_exit(&ip->i_contents);
708*0Sstevel@tonic-gate 			errp("%s Estimated statefile size %lld, i_size %lld\n",
709*0Sstevel@tonic-gate 			    str, size, isize);
710*0Sstevel@tonic-gate 		}
711*0Sstevel@tonic-gate 
712*0Sstevel@tonic-gate 		return (error);
713*0Sstevel@tonic-gate 	}
714*0Sstevel@tonic-gate }
715*0Sstevel@tonic-gate 
716*0Sstevel@tonic-gate 
717*0Sstevel@tonic-gate void
718*0Sstevel@tonic-gate cpr_statef_close(void)
719*0Sstevel@tonic-gate {
720*0Sstevel@tonic-gate 	if (C_VP) {
721*0Sstevel@tonic-gate 		if (!cpr_reusable_mode)
722*0Sstevel@tonic-gate 			(void) VOP_DUMPCTL(C_VP, DUMP_FREE, NULL);
723*0Sstevel@tonic-gate 		(void) VOP_CLOSE(C_VP, FWRITE, 1, (offset_t)0, CRED());
724*0Sstevel@tonic-gate 		VN_RELE(C_VP);
725*0Sstevel@tonic-gate 		C_VP = 0;
726*0Sstevel@tonic-gate 	}
727*0Sstevel@tonic-gate }
728*0Sstevel@tonic-gate 
729*0Sstevel@tonic-gate 
730*0Sstevel@tonic-gate /*
731*0Sstevel@tonic-gate  * open cpr default file and display error
732*0Sstevel@tonic-gate  */
733*0Sstevel@tonic-gate int
734*0Sstevel@tonic-gate cpr_open_deffile(int mode, vnode_t **vpp)
735*0Sstevel@tonic-gate {
736*0Sstevel@tonic-gate 	int error;
737*0Sstevel@tonic-gate 
738*0Sstevel@tonic-gate 	if (error = cpr_open(cpr_default_path, mode, vpp))
739*0Sstevel@tonic-gate 		cpr_err(CE_CONT, "cannot open \"%s\", error %d\n",
740*0Sstevel@tonic-gate 		    cpr_default_path, error);
741*0Sstevel@tonic-gate 	return (error);
742*0Sstevel@tonic-gate }
743*0Sstevel@tonic-gate 
744*0Sstevel@tonic-gate 
745*0Sstevel@tonic-gate /*
746*0Sstevel@tonic-gate  * write cdef_t to disk.  This contains the original values of prom
747*0Sstevel@tonic-gate  * properties that we modify.  We fill in the magic number of the file
748*0Sstevel@tonic-gate  * here as a signal to the booter code that the state file is valid.
749*0Sstevel@tonic-gate  * Be sure the file gets synced, since we may be shutting down the OS.
750*0Sstevel@tonic-gate  */
751*0Sstevel@tonic-gate int
752*0Sstevel@tonic-gate cpr_write_deffile(cdef_t *cdef)
753*0Sstevel@tonic-gate {
754*0Sstevel@tonic-gate 	struct vnode *vp;
755*0Sstevel@tonic-gate 	char *str;
756*0Sstevel@tonic-gate 	int rc;
757*0Sstevel@tonic-gate 
758*0Sstevel@tonic-gate 	if (rc = cpr_open_deffile(FCREAT|FWRITE, &vp))
759*0Sstevel@tonic-gate 		return (rc);
760*0Sstevel@tonic-gate 
761*0Sstevel@tonic-gate 	if (rc = cpr_rdwr(UIO_WRITE, vp, cdef, sizeof (*cdef)))
762*0Sstevel@tonic-gate 		str = "write";
763*0Sstevel@tonic-gate 	else if (rc = VOP_FSYNC(vp, FSYNC, CRED()))
764*0Sstevel@tonic-gate 		str = "fsync";
765*0Sstevel@tonic-gate 	(void) VOP_CLOSE(vp, FWRITE, 1, (offset_t)0, CRED());
766*0Sstevel@tonic-gate 	VN_RELE(vp);
767*0Sstevel@tonic-gate 
768*0Sstevel@tonic-gate 	if (rc) {
769*0Sstevel@tonic-gate 		cpr_err(CE_WARN, "%s error %d, file \"%s\"",
770*0Sstevel@tonic-gate 		    str, rc, cpr_default_path);
771*0Sstevel@tonic-gate 	}
772*0Sstevel@tonic-gate 	return (rc);
773*0Sstevel@tonic-gate }
774*0Sstevel@tonic-gate 
775*0Sstevel@tonic-gate /*
776*0Sstevel@tonic-gate  * Clear the magic number in the defaults file.  This tells the booter
777*0Sstevel@tonic-gate  * program that the state file is not current and thus prevents
778*0Sstevel@tonic-gate  * any attempt to restore from an obsolete state file.
779*0Sstevel@tonic-gate  */
780*0Sstevel@tonic-gate void
781*0Sstevel@tonic-gate cpr_clear_definfo(void)
782*0Sstevel@tonic-gate {
783*0Sstevel@tonic-gate 	struct vnode *vp;
784*0Sstevel@tonic-gate 	cmini_t mini;
785*0Sstevel@tonic-gate 
786*0Sstevel@tonic-gate 	if ((CPR->c_cprboot_magic != CPR_DEFAULT_MAGIC) ||
787*0Sstevel@tonic-gate 	    cpr_open_deffile(FCREAT|FWRITE, &vp))
788*0Sstevel@tonic-gate 		return;
789*0Sstevel@tonic-gate 	mini.magic = mini.reusable = 0;
790*0Sstevel@tonic-gate 	(void) cpr_rdwr(UIO_WRITE, vp, &mini, sizeof (mini));
791*0Sstevel@tonic-gate 	(void) VOP_CLOSE(vp, FWRITE, 1, (offset_t)0, CRED());
792*0Sstevel@tonic-gate 	VN_RELE(vp);
793*0Sstevel@tonic-gate }
794*0Sstevel@tonic-gate 
795*0Sstevel@tonic-gate /*
796*0Sstevel@tonic-gate  * If the cpr default file is invalid, then we must not be in reusable mode
797*0Sstevel@tonic-gate  * if it is valid, it tells us our mode
798*0Sstevel@tonic-gate  */
799*0Sstevel@tonic-gate int
800*0Sstevel@tonic-gate cpr_get_reusable_mode(void)
801*0Sstevel@tonic-gate {
802*0Sstevel@tonic-gate 	struct vnode *vp;
803*0Sstevel@tonic-gate 	cmini_t mini;
804*0Sstevel@tonic-gate 	int rc;
805*0Sstevel@tonic-gate 
806*0Sstevel@tonic-gate 	if (cpr_open(cpr_default_path, FREAD, &vp))
807*0Sstevel@tonic-gate 		return (0);
808*0Sstevel@tonic-gate 
809*0Sstevel@tonic-gate 	rc = cpr_rdwr(UIO_READ, vp, &mini, sizeof (mini));
810*0Sstevel@tonic-gate 	(void) VOP_CLOSE(vp, FREAD, 1, (offset_t)0, CRED());
811*0Sstevel@tonic-gate 	VN_RELE(vp);
812*0Sstevel@tonic-gate 	if (rc == 0 && mini.magic == CPR_DEFAULT_MAGIC)
813*0Sstevel@tonic-gate 		return (mini.reusable);
814*0Sstevel@tonic-gate 
815*0Sstevel@tonic-gate 	return (0);
816*0Sstevel@tonic-gate }
817*0Sstevel@tonic-gate 
818*0Sstevel@tonic-gate /*
819*0Sstevel@tonic-gate  * clock/time related routines
820*0Sstevel@tonic-gate  */
821*0Sstevel@tonic-gate static time_t   cpr_time_stamp;
822*0Sstevel@tonic-gate 
823*0Sstevel@tonic-gate 
824*0Sstevel@tonic-gate void
825*0Sstevel@tonic-gate cpr_tod_get(cpr_time_t *ctp)
826*0Sstevel@tonic-gate {
827*0Sstevel@tonic-gate 	timestruc_t ts;
828*0Sstevel@tonic-gate 
829*0Sstevel@tonic-gate 	mutex_enter(&tod_lock);
830*0Sstevel@tonic-gate 	ts = tod_get();
831*0Sstevel@tonic-gate 	mutex_exit(&tod_lock);
832*0Sstevel@tonic-gate 	ctp->tv_sec = (time32_t)ts.tv_sec;
833*0Sstevel@tonic-gate 	ctp->tv_nsec = (int32_t)ts.tv_nsec;
834*0Sstevel@tonic-gate }
835*0Sstevel@tonic-gate 
836*0Sstevel@tonic-gate void
837*0Sstevel@tonic-gate cpr_tod_fault_reset(void)
838*0Sstevel@tonic-gate {
839*0Sstevel@tonic-gate 	mutex_enter(&tod_lock);
840*0Sstevel@tonic-gate 	tod_fault_reset();
841*0Sstevel@tonic-gate 	mutex_exit(&tod_lock);
842*0Sstevel@tonic-gate }
843*0Sstevel@tonic-gate 
844*0Sstevel@tonic-gate void
845*0Sstevel@tonic-gate cpr_save_time(void)
846*0Sstevel@tonic-gate {
847*0Sstevel@tonic-gate 	cpr_time_stamp = gethrestime_sec();
848*0Sstevel@tonic-gate }
849*0Sstevel@tonic-gate 
850*0Sstevel@tonic-gate /*
851*0Sstevel@tonic-gate  * correct time based on saved time stamp or hardware clock
852*0Sstevel@tonic-gate  */
853*0Sstevel@tonic-gate void
854*0Sstevel@tonic-gate cpr_restore_time(void)
855*0Sstevel@tonic-gate {
856*0Sstevel@tonic-gate 	clkset(cpr_time_stamp);
857*0Sstevel@tonic-gate }
858*0Sstevel@tonic-gate 
859*0Sstevel@tonic-gate /*
860*0Sstevel@tonic-gate  * CPU ONLINE/OFFLINE CODE
861*0Sstevel@tonic-gate  */
862*0Sstevel@tonic-gate int
863*0Sstevel@tonic-gate cpr_mp_offline(void)
864*0Sstevel@tonic-gate {
865*0Sstevel@tonic-gate 	cpu_t *cp, *bootcpu;
866*0Sstevel@tonic-gate 	int rc = 0;
867*0Sstevel@tonic-gate 	int brought_up_boot = 0;
868*0Sstevel@tonic-gate 
869*0Sstevel@tonic-gate 	/*
870*0Sstevel@tonic-gate 	 * Do nothing for UP.
871*0Sstevel@tonic-gate 	 */
872*0Sstevel@tonic-gate 	if (ncpus == 1)
873*0Sstevel@tonic-gate 		return (0);
874*0Sstevel@tonic-gate 
875*0Sstevel@tonic-gate 	mutex_enter(&cpu_lock);
876*0Sstevel@tonic-gate 
877*0Sstevel@tonic-gate 	cpr_save_mp_state();
878*0Sstevel@tonic-gate 
879*0Sstevel@tonic-gate 	bootcpu = i_cpr_bootcpu();
880*0Sstevel@tonic-gate 	if (!CPU_ACTIVE(bootcpu)) {
881*0Sstevel@tonic-gate 		if ((rc = cpr_p_online(bootcpu, CPU_CPR_ONLINE))) {
882*0Sstevel@tonic-gate 			mutex_exit(&cpu_lock);
883*0Sstevel@tonic-gate 			return (rc);
884*0Sstevel@tonic-gate 		}
885*0Sstevel@tonic-gate 		brought_up_boot = 1;
886*0Sstevel@tonic-gate 	}
887*0Sstevel@tonic-gate 
888*0Sstevel@tonic-gate 	cp = cpu_list;
889*0Sstevel@tonic-gate 	do {
890*0Sstevel@tonic-gate 		if (cp == bootcpu)
891*0Sstevel@tonic-gate 			continue;
892*0Sstevel@tonic-gate 		if (cp->cpu_flags & CPU_OFFLINE)
893*0Sstevel@tonic-gate 			continue;
894*0Sstevel@tonic-gate 		if ((rc = cpr_p_online(cp, CPU_CPR_OFFLINE))) {
895*0Sstevel@tonic-gate 			mutex_exit(&cpu_lock);
896*0Sstevel@tonic-gate 			return (rc);
897*0Sstevel@tonic-gate 		}
898*0Sstevel@tonic-gate 	} while ((cp = cp->cpu_next) != cpu_list);
899*0Sstevel@tonic-gate 	if (brought_up_boot && (cpr_debug & (LEVEL1 | LEVEL6)))
900*0Sstevel@tonic-gate 		errp("changed cpu %d to state %d\n", bootcpu, CPU_CPR_ONLINE);
901*0Sstevel@tonic-gate 	mutex_exit(&cpu_lock);
902*0Sstevel@tonic-gate 
903*0Sstevel@tonic-gate 	return (rc);
904*0Sstevel@tonic-gate }
905*0Sstevel@tonic-gate 
906*0Sstevel@tonic-gate int
907*0Sstevel@tonic-gate cpr_mp_online(void)
908*0Sstevel@tonic-gate {
909*0Sstevel@tonic-gate 	cpu_t *cp, *bootcpu = CPU;
910*0Sstevel@tonic-gate 	int rc = 0;
911*0Sstevel@tonic-gate 
912*0Sstevel@tonic-gate 	/*
913*0Sstevel@tonic-gate 	 * Do nothing for UP.
914*0Sstevel@tonic-gate 	 */
915*0Sstevel@tonic-gate 	if (ncpus == 1)
916*0Sstevel@tonic-gate 		return (0);
917*0Sstevel@tonic-gate 
918*0Sstevel@tonic-gate 	/*
919*0Sstevel@tonic-gate 	 * cpr_save_mp_state() sets CPU_CPR_ONLINE in cpu_cpr_flags
920*0Sstevel@tonic-gate 	 * to indicate a cpu was online at the time of cpr_suspend();
921*0Sstevel@tonic-gate 	 * now restart those cpus that were marked as CPU_CPR_ONLINE
922*0Sstevel@tonic-gate 	 * and actually are offline.
923*0Sstevel@tonic-gate 	 */
924*0Sstevel@tonic-gate 	mutex_enter(&cpu_lock);
925*0Sstevel@tonic-gate 	for (cp = bootcpu->cpu_next; cp != bootcpu; cp = cp->cpu_next) {
926*0Sstevel@tonic-gate 		/*
927*0Sstevel@tonic-gate 		 * Clear the CPU_FROZEN flag in all cases.
928*0Sstevel@tonic-gate 		 */
929*0Sstevel@tonic-gate 		cp->cpu_flags &= ~CPU_FROZEN;
930*0Sstevel@tonic-gate 
931*0Sstevel@tonic-gate 		if (CPU_CPR_IS_OFFLINE(cp))
932*0Sstevel@tonic-gate 			continue;
933*0Sstevel@tonic-gate 		if (CPU_ACTIVE(cp))
934*0Sstevel@tonic-gate 			continue;
935*0Sstevel@tonic-gate 		if ((rc = cpr_p_online(cp, CPU_CPR_ONLINE))) {
936*0Sstevel@tonic-gate 			mutex_exit(&cpu_lock);
937*0Sstevel@tonic-gate 			return (rc);
938*0Sstevel@tonic-gate 		}
939*0Sstevel@tonic-gate 	}
940*0Sstevel@tonic-gate 
941*0Sstevel@tonic-gate 	/*
942*0Sstevel@tonic-gate 	 * turn off the boot cpu if it was offlined
943*0Sstevel@tonic-gate 	 */
944*0Sstevel@tonic-gate 	if (CPU_CPR_IS_OFFLINE(bootcpu)) {
945*0Sstevel@tonic-gate 		if ((rc = cpr_p_online(bootcpu, CPU_CPR_OFFLINE))) {
946*0Sstevel@tonic-gate 			mutex_exit(&cpu_lock);
947*0Sstevel@tonic-gate 			return (rc);
948*0Sstevel@tonic-gate 		}
949*0Sstevel@tonic-gate 	}
950*0Sstevel@tonic-gate 	mutex_exit(&cpu_lock);
951*0Sstevel@tonic-gate 	return (0);
952*0Sstevel@tonic-gate }
953*0Sstevel@tonic-gate 
954*0Sstevel@tonic-gate static void
955*0Sstevel@tonic-gate cpr_save_mp_state(void)
956*0Sstevel@tonic-gate {
957*0Sstevel@tonic-gate 	cpu_t *cp;
958*0Sstevel@tonic-gate 
959*0Sstevel@tonic-gate 	ASSERT(MUTEX_HELD(&cpu_lock));
960*0Sstevel@tonic-gate 
961*0Sstevel@tonic-gate 	cp = cpu_list;
962*0Sstevel@tonic-gate 	do {
963*0Sstevel@tonic-gate 		cp->cpu_cpr_flags &= ~CPU_CPR_ONLINE;
964*0Sstevel@tonic-gate 		if (CPU_ACTIVE(cp))
965*0Sstevel@tonic-gate 			CPU_SET_CPR_FLAGS(cp, CPU_CPR_ONLINE);
966*0Sstevel@tonic-gate 	} while ((cp = cp->cpu_next) != cpu_list);
967*0Sstevel@tonic-gate }
968*0Sstevel@tonic-gate 
969*0Sstevel@tonic-gate /*
970*0Sstevel@tonic-gate  * change cpu to online/offline
971*0Sstevel@tonic-gate  */
972*0Sstevel@tonic-gate static int
973*0Sstevel@tonic-gate cpr_p_online(cpu_t *cp, int state)
974*0Sstevel@tonic-gate {
975*0Sstevel@tonic-gate 	int rc;
976*0Sstevel@tonic-gate 
977*0Sstevel@tonic-gate 	ASSERT(MUTEX_HELD(&cpu_lock));
978*0Sstevel@tonic-gate 
979*0Sstevel@tonic-gate 	switch (state) {
980*0Sstevel@tonic-gate 	case CPU_CPR_ONLINE:
981*0Sstevel@tonic-gate 		rc = cpu_online(cp);
982*0Sstevel@tonic-gate 		break;
983*0Sstevel@tonic-gate 	case CPU_CPR_OFFLINE:
984*0Sstevel@tonic-gate 		rc = cpu_offline(cp, CPU_FORCED);
985*0Sstevel@tonic-gate 		break;
986*0Sstevel@tonic-gate 	}
987*0Sstevel@tonic-gate 	if (rc) {
988*0Sstevel@tonic-gate 		cpr_err(CE_WARN, "Failed to change processor %d to "
989*0Sstevel@tonic-gate 		    "state %d, (errno %d)", cp->cpu_id, state, rc);
990*0Sstevel@tonic-gate 	}
991*0Sstevel@tonic-gate 	return (rc);
992*0Sstevel@tonic-gate }
993*0Sstevel@tonic-gate 
994*0Sstevel@tonic-gate /*
995*0Sstevel@tonic-gate  * Construct the pathname of the state file and return a pointer to
996*0Sstevel@tonic-gate  * caller.  Read the config file to get the mount point of the
997*0Sstevel@tonic-gate  * filesystem and the pathname within fs.
998*0Sstevel@tonic-gate  */
999*0Sstevel@tonic-gate char *
1000*0Sstevel@tonic-gate cpr_build_statefile_path(void)
1001*0Sstevel@tonic-gate {
1002*0Sstevel@tonic-gate 	struct cprconfig *cf = &cprconfig;
1003*0Sstevel@tonic-gate 
1004*0Sstevel@tonic-gate 	if (cpr_get_config())
1005*0Sstevel@tonic-gate 		return (NULL);
1006*0Sstevel@tonic-gate 
1007*0Sstevel@tonic-gate 	switch (cf->cf_type) {
1008*0Sstevel@tonic-gate 	case CFT_UFS:
1009*0Sstevel@tonic-gate 		if (strlen(cf->cf_path) + strlen(cf->cf_fs) >= MAXNAMELEN - 1) {
1010*0Sstevel@tonic-gate 			cpr_err(CE_CONT, "Statefile path is too long.\n");
1011*0Sstevel@tonic-gate 			return (NULL);
1012*0Sstevel@tonic-gate 		}
1013*0Sstevel@tonic-gate 		return (cpr_cprconfig_to_path());
1014*0Sstevel@tonic-gate 	case CFT_SPEC:
1015*0Sstevel@tonic-gate 		return (cf->cf_devfs);
1016*0Sstevel@tonic-gate 	default:
1017*0Sstevel@tonic-gate 		cpr_err(CE_PANIC, "invalid statefile type");
1018*0Sstevel@tonic-gate 		/*NOTREACHED*/
1019*0Sstevel@tonic-gate 	}
1020*0Sstevel@tonic-gate }
1021*0Sstevel@tonic-gate 
1022*0Sstevel@tonic-gate int
1023*0Sstevel@tonic-gate cpr_statefile_is_spec(void)
1024*0Sstevel@tonic-gate {
1025*0Sstevel@tonic-gate 	if (cpr_get_config())
1026*0Sstevel@tonic-gate 		return (0);
1027*0Sstevel@tonic-gate 	return (cprconfig.cf_type == CFT_SPEC);
1028*0Sstevel@tonic-gate }
1029*0Sstevel@tonic-gate 
1030*0Sstevel@tonic-gate char *
1031*0Sstevel@tonic-gate cpr_get_statefile_prom_path(void)
1032*0Sstevel@tonic-gate {
1033*0Sstevel@tonic-gate 	struct cprconfig *cf = &cprconfig;
1034*0Sstevel@tonic-gate 
1035*0Sstevel@tonic-gate 	ASSERT(cprconfig_loaded);
1036*0Sstevel@tonic-gate 	ASSERT(cf->cf_magic == CPR_CONFIG_MAGIC);
1037*0Sstevel@tonic-gate 	ASSERT(cf->cf_type == CFT_SPEC);
1038*0Sstevel@tonic-gate 	return (cf->cf_dev_prom);
1039*0Sstevel@tonic-gate }
1040*0Sstevel@tonic-gate 
1041*0Sstevel@tonic-gate 
1042*0Sstevel@tonic-gate /*
1043*0Sstevel@tonic-gate  * XXX The following routines need to be in the vfs source code.
1044*0Sstevel@tonic-gate  */
1045*0Sstevel@tonic-gate 
1046*0Sstevel@tonic-gate int
1047*0Sstevel@tonic-gate cpr_is_ufs(struct vfs *vfsp)
1048*0Sstevel@tonic-gate {
1049*0Sstevel@tonic-gate 	char *fsname;
1050*0Sstevel@tonic-gate 
1051*0Sstevel@tonic-gate 	fsname = vfssw[vfsp->vfs_fstype].vsw_name;
1052*0Sstevel@tonic-gate 	return (strcmp(fsname, "ufs") == 0);
1053*0Sstevel@tonic-gate }
1054*0Sstevel@tonic-gate 
1055*0Sstevel@tonic-gate /*
1056*0Sstevel@tonic-gate  * This is a list of file systems that are allowed to be writeable when a
1057*0Sstevel@tonic-gate  * reusable statefile checkpoint is taken.  They must not have any state that
1058*0Sstevel@tonic-gate  * cannot be restored to consistency by simply rebooting using the checkpoint.
1059*0Sstevel@tonic-gate  * (In contrast to ufs, cachefs and pcfs which have disk state that could get
1060*0Sstevel@tonic-gate  * out of sync with the in-kernel data).
1061*0Sstevel@tonic-gate  */
1062*0Sstevel@tonic-gate int
1063*0Sstevel@tonic-gate cpr_reusable_mount_check(void)
1064*0Sstevel@tonic-gate {
1065*0Sstevel@tonic-gate 	struct vfs *vfsp;
1066*0Sstevel@tonic-gate 	char *fsname;
1067*0Sstevel@tonic-gate 	char **cpp;
1068*0Sstevel@tonic-gate 	static char *cpr_writeok_fss[] = {
1069*0Sstevel@tonic-gate 		"autofs", "devfs", "fd", "lofs", "mntfs", "namefs", "nfs",
1070*0Sstevel@tonic-gate 		"proc", "tmpfs", "ctfs", "objfs", NULL
1071*0Sstevel@tonic-gate 	};
1072*0Sstevel@tonic-gate 
1073*0Sstevel@tonic-gate 	vfs_list_read_lock();
1074*0Sstevel@tonic-gate 	vfsp = rootvfs;
1075*0Sstevel@tonic-gate 	do {
1076*0Sstevel@tonic-gate 		if (vfsp->vfs_flag & VFS_RDONLY) {
1077*0Sstevel@tonic-gate 			vfsp = vfsp->vfs_next;
1078*0Sstevel@tonic-gate 			continue;
1079*0Sstevel@tonic-gate 		}
1080*0Sstevel@tonic-gate 		fsname = vfssw[vfsp->vfs_fstype].vsw_name;
1081*0Sstevel@tonic-gate 		for (cpp = cpr_writeok_fss; *cpp; cpp++) {
1082*0Sstevel@tonic-gate 			if (strcmp(fsname, *cpp) == 0)
1083*0Sstevel@tonic-gate 				break;
1084*0Sstevel@tonic-gate 		}
1085*0Sstevel@tonic-gate 		/*
1086*0Sstevel@tonic-gate 		 * if the inner loop reached the NULL terminator,
1087*0Sstevel@tonic-gate 		 * the current fs-type does not match any OK-type
1088*0Sstevel@tonic-gate 		 */
1089*0Sstevel@tonic-gate 		if (*cpp == NULL) {
1090*0Sstevel@tonic-gate 			cpr_err(CE_CONT, "a filesystem of type %s is "
1091*0Sstevel@tonic-gate 			    "mounted read/write.\nReusable statefile requires "
1092*0Sstevel@tonic-gate 			    "no writeable filesystem of this type be mounted\n",
1093*0Sstevel@tonic-gate 			    fsname);
1094*0Sstevel@tonic-gate 			vfs_list_unlock();
1095*0Sstevel@tonic-gate 			return (EINVAL);
1096*0Sstevel@tonic-gate 		}
1097*0Sstevel@tonic-gate 		vfsp = vfsp->vfs_next;
1098*0Sstevel@tonic-gate 	} while (vfsp != rootvfs);
1099*0Sstevel@tonic-gate 	vfs_list_unlock();
1100*0Sstevel@tonic-gate 	return (0);
1101*0Sstevel@tonic-gate }
1102*0Sstevel@tonic-gate 
1103*0Sstevel@tonic-gate /*
1104*0Sstevel@tonic-gate  * Force a fresh read of the cprinfo per uadmin 3 call
1105*0Sstevel@tonic-gate  */
1106*0Sstevel@tonic-gate void
1107*0Sstevel@tonic-gate cpr_forget_cprconfig(void)
1108*0Sstevel@tonic-gate {
1109*0Sstevel@tonic-gate 	cprconfig_loaded = 0;
1110*0Sstevel@tonic-gate }
1111*0Sstevel@tonic-gate 
1112*0Sstevel@tonic-gate 
1113*0Sstevel@tonic-gate /*
1114*0Sstevel@tonic-gate  * return statefile offset in DEV_BSIZE units
1115*0Sstevel@tonic-gate  */
1116*0Sstevel@tonic-gate int
1117*0Sstevel@tonic-gate cpr_statefile_offset(void)
1118*0Sstevel@tonic-gate {
1119*0Sstevel@tonic-gate 	return (cpr_statefile_is_spec() ? btod(CPR_SPEC_OFFSET) : 0);
1120*0Sstevel@tonic-gate }
1121