Home | History | Annotate | Download | only in io
      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 
     27 /*
     28  * Dump driver.  Provides ioctls to get/set crash dump configuration.
     29  */
     30 
     31 #include <sys/types.h>
     32 #include <sys/param.h>
     33 #include <sys/systm.h>
     34 #include <sys/vnode.h>
     35 #include <sys/uio.h>
     36 #include <sys/cred.h>
     37 #include <sys/kmem.h>
     38 #include <sys/errno.h>
     39 #include <sys/modctl.h>
     40 #include <sys/dumphdr.h>
     41 #include <sys/dumpadm.h>
     42 #include <sys/pathname.h>
     43 #include <sys/file.h>
     44 #include <vm/anon.h>
     45 #include <sys/stat.h>
     46 #include <sys/conf.h>
     47 #include <sys/ddi.h>
     48 #include <sys/sunddi.h>
     49 
     50 static dev_info_t *dump_devi;
     51 
     52 static int
     53 dump_attach(dev_info_t *devi, ddi_attach_cmd_t cmd)
     54 {
     55 	if (cmd != DDI_ATTACH)
     56 		return (DDI_FAILURE);
     57 	if (ddi_create_minor_node(devi, "dump", S_IFCHR, 0, DDI_PSEUDO, NULL) ==
     58 	    DDI_FAILURE) {
     59 		ddi_remove_minor_node(devi, NULL);
     60 		return (DDI_FAILURE);
     61 	}
     62 	dump_devi = devi;
     63 	return (DDI_SUCCESS);
     64 }
     65 
     66 static int
     67 dump_detach(dev_info_t *devi, ddi_detach_cmd_t cmd)
     68 {
     69 	if (cmd != DDI_DETACH)
     70 		return (DDI_FAILURE);
     71 	ddi_remove_minor_node(devi, NULL);
     72 	return (DDI_SUCCESS);
     73 }
     74 
     75 /*ARGSUSED*/
     76 static int
     77 dump_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result)
     78 {
     79 	switch (infocmd) {
     80 	case DDI_INFO_DEVT2DEVINFO:
     81 		*result = dump_devi;
     82 		return (DDI_SUCCESS);
     83 	case DDI_INFO_DEVT2INSTANCE:
     84 		*result = 0;
     85 		return (DDI_SUCCESS);
     86 	}
     87 	return (DDI_FAILURE);
     88 }
     89 
     90 /*ARGSUSED*/
     91 int
     92 dump_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *cred, int *rvalp)
     93 {
     94 	uint64_t size;
     95 	uint64_t dumpsize_in_pages;
     96 	int error = 0;
     97 	char *pathbuf = kmem_zalloc(MAXPATHLEN, KM_SLEEP);
     98 	vnode_t *vp;
     99 
    100 	switch (cmd) {
    101 	case DIOCGETDUMPSIZE:
    102 		if (dump_conflags & DUMP_ALL)
    103 			size = ptob((uint64_t)physmem) / DUMP_COMPRESS_RATIO;
    104 		else {
    105 			/*
    106 			 * We can't give a good answer for the DUMP_CURPROC
    107 			 * because we won't know which process to use until it
    108 			 * causes a panic.  We'll therefore punt and give the
    109 			 * caller the size for the kernel.
    110 			 *
    111 			 * This kernel size equation takes care of the
    112 			 * boot time kernel footprint and also accounts
    113 			 * for availrmem changes due to user explicit locking.
    114 			 * Refer to common/vm/vm_page.c for an explanation
    115 			 * of these counters.
    116 			 */
    117 			dumpsize_in_pages = (physinstalled - obp_pages -
    118 			    availrmem -
    119 			    anon_segkp_pages_locked -
    120 			    k_anoninfo.ani_mem_resv -
    121 			    pages_locked -
    122 			    pages_claimed -
    123 			    pages_useclaim);
    124 
    125 			/*
    126 			 * Protect against vm vagaries.
    127 			 */
    128 			if (dumpsize_in_pages > (uint64_t)physmem)
    129 				dumpsize_in_pages = (uint64_t)physmem;
    130 
    131 			size = ptob(dumpsize_in_pages) / DUMP_COMPRESS_RATIO;
    132 		}
    133 		if (copyout(&size, (void *)arg, sizeof (size)) < 0)
    134 			error = EFAULT;
    135 		break;
    136 
    137 	case DIOCGETCONF:
    138 		mutex_enter(&dump_lock);
    139 		*rvalp = dump_conflags;
    140 		if (dumpvp && !(dumpvp->v_flag & VISSWAP))
    141 			*rvalp |= DUMP_EXCL;
    142 		mutex_exit(&dump_lock);
    143 		break;
    144 
    145 	case DIOCSETCONF:
    146 		mutex_enter(&dump_lock);
    147 		if (arg == DUMP_KERNEL || arg == DUMP_ALL ||
    148 		    arg == DUMP_CURPROC)
    149 			dump_conflags = arg;
    150 		else
    151 			error = EINVAL;
    152 		mutex_exit(&dump_lock);
    153 		break;
    154 
    155 	case DIOCGETDEV:
    156 		mutex_enter(&dump_lock);
    157 		if (dumppath == NULL) {
    158 			mutex_exit(&dump_lock);
    159 			error = ENODEV;
    160 			break;
    161 		}
    162 		(void) strcpy(pathbuf, dumppath);
    163 		mutex_exit(&dump_lock);
    164 		error = copyoutstr(pathbuf, (void *)arg, MAXPATHLEN, NULL);
    165 		break;
    166 
    167 	case DIOCSETDEV:
    168 	case DIOCTRYDEV:
    169 		if ((error = copyinstr((char *)arg, pathbuf, MAXPATHLEN,
    170 		    NULL)) != 0 || (error = lookupname(pathbuf, UIO_SYSSPACE,
    171 		    FOLLOW, NULLVPP, &vp)) != 0)
    172 			break;
    173 		mutex_enter(&dump_lock);
    174 		if (vp->v_type == VBLK)
    175 			error = dumpinit(vp, pathbuf, cmd == DIOCTRYDEV);
    176 		else
    177 			error = ENOTBLK;
    178 		mutex_exit(&dump_lock);
    179 		VN_RELE(vp);
    180 		break;
    181 
    182 	case DIOCDUMP:
    183 		mutex_enter(&dump_lock);
    184 		if (dumpvp == NULL)
    185 			error = ENODEV;
    186 		else if (dumpvp->v_flag & VISSWAP)
    187 			error = EBUSY;
    188 		else
    189 			dumpsys();
    190 		mutex_exit(&dump_lock);
    191 		break;
    192 
    193 	default:
    194 		error = ENXIO;
    195 	}
    196 
    197 	kmem_free(pathbuf, MAXPATHLEN);
    198 	return (error);
    199 }
    200 
    201 struct cb_ops dump_cb_ops = {
    202 	nulldev,		/* open */
    203 	nulldev,		/* close */
    204 	nodev,			/* strategy */
    205 	nodev,			/* print */
    206 	nodev,			/* dump */
    207 	nodev,			/* read */
    208 	nodev,			/* write */
    209 	dump_ioctl,		/* ioctl */
    210 	nodev,			/* devmap */
    211 	nodev,			/* mmap */
    212 	nodev, 			/* segmap */
    213 	nochpoll,		/* poll */
    214 	ddi_prop_op,		/* prop_op */
    215 	0,			/* streamtab  */
    216 	D_NEW|D_MP		/* Driver compatibility flag */
    217 };
    218 
    219 struct dev_ops dump_ops = {
    220 	DEVO_REV,		/* devo_rev, */
    221 	0,			/* refcnt */
    222 	dump_info,		/* info */
    223 	nulldev,		/* identify */
    224 	nulldev,		/* probe */
    225 	dump_attach,		/* attach */
    226 	dump_detach,		/* detach */
    227 	nodev,			/* reset */
    228 	&dump_cb_ops,		/* driver operations */
    229 	(struct bus_ops *)0,	/* bus operations */
    230 	NULL,			/* power */
    231 	ddi_quiesce_not_needed,		/* quiesce */
    232 };
    233 
    234 static struct modldrv modldrv = {
    235 	&mod_driverops, "crash dump driver", &dump_ops,
    236 };
    237 
    238 static struct modlinkage modlinkage = {
    239 	MODREV_1, (void *)&modldrv, NULL
    240 };
    241 
    242 int
    243 _init(void)
    244 {
    245 	return (mod_install(&modlinkage));
    246 }
    247 
    248 int
    249 _fini(void)
    250 {
    251 	return (mod_remove(&modlinkage));
    252 }
    253 
    254 int
    255 _info(struct modinfo *modinfop)
    256 {
    257 	return (mod_info(&modlinkage, modinfop));
    258 }
    259