/* * bnull - /dev/bnull device driver, block device version of /dev/null. * Written for Solaris 10 x86/SPARC. * * 27-Nov-2005, ver 0.70 (first release!) * * INSTALL: * gcc/x86, * gcc -D_KERNEL -c bnull.c * gcc/sparc, * gcc -D_KERNEL -m64 -c bnull.c * both, * ld -r -o bnull bnull.o * cp bnull /kernel/drv * echo 'name="bnull" parent="pseudo";' > /kernel/drv/bnull.conf * add_drv -m '* 0666 root sys' bnull * modload -p drv/bnull * ln -s ../devices/pseudo/bnull\@0\:c /dev/bnull * * BASED ON: * Sample Solaris 8 Drivers (SUNWdrvs8), with Solaris 10 DDI * updates applied. * * NOTES: * While this is indeed a block device version of /dev/null, * it isn't any faster than /dev/null (in fact, it's much slower). * * WARNING: * This is fairly untested so far. As small mistakes in driver code can * cause the kernel to panic, this should only be run on test servers. * * PORTIONS: Copyright (c) 2005 Brendan Gregg. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * * (http://www.gnu.org/copyleft/gpl.html) * * 27-Nov-2005 Brendan Gregg Created this. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* * Device Geometry */ #define BN_SIZE (1024*1024) #define BN_MAXPHYS (1024*128) /* * Block Driver Roadmap * * modlinkage devstate * | * modldrv * | * dev_ops * | * cb_ops * * Chapter 15, http://docs.sun.com/app/docs/doc/816-4854 */ /* * Device State */ typedef struct { int maxphys; /* maximum I/O */ dev_info_t *dip; /* devinfo */ } bn_devstate_t; static void *bn_state; /* bn_devstate_t */ /* * Device Char/Block Operations - cb_ops(9S) */ static int bn_open(dev_t *devp, int flag, int otyp, cred_t *cred); static int bn_strategy(struct buf *bp); static int bn_read(dev_t dev, struct uio *uiop, cred_t *credp); static int bn_write(dev_t dev, struct uio *uiop, cred_t *credp); static int bn_print(dev_t dev, char *str); static struct cb_ops bn_cb_ops = { bn_open, /* cb_open */ nulldev, /* cb_close */ bn_strategy, /* cb_strategy */ bn_print, /* cb_print */ nodev, /* cb_dump */ bn_read, /* cb_read */ bn_write, /* cb_write */ nodev, /* cb_ioctl */ nodev, /* cb_devmap */ nodev, /* cb_mmap */ nodev, /* cb_segmap */ nochpoll, /* cb_chpoll */ ddi_prop_op, /* cb_prop_op */ (struct streamtab *)NULL, /* cb_stream */ D_NEW | D_MP, /* cb_flag */ 0, /* cb_rev */ nodev, /* cb_aread */ nodev /* cb_awrite */ }; /* * Device Operations - dev_ops(9S) */ static int bn_getinfo(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result); static int bn_attach(dev_info_t *dip, ddi_attach_cmd_t cmd); static int bn_detach(dev_info_t *dip, ddi_detach_cmd_t cmd); static struct dev_ops bn_ops = { DEVO_REV, /* devo_rev */ 0, /* devo_refcnt */ bn_getinfo, /* devo_getinfo */ nulldev, /* devo_identify */ nulldev, /* devo_probe */ bn_attach, /* devo_attach */ bn_detach, /* devo_detach */ nodev, /* devo_reset */ &bn_cb_ops, /* devo_cb_ops */ (struct bus_ops *)NULL, /* devo_bus_ops */ nulldev /* devo_power */ }; /* * Module Linkage - modldrv(9S) */ extern struct mod_ops mod_driverops; static struct modldrv modldrv = { &mod_driverops, /* module ops */ "bnull driver v0.70", /* link info */ &bn_ops /* device ops */ }; static struct modlinkage modlinkage = { MODREV_1, &modldrv, 0 }; int _init(void) { int status; if ((status = ddi_soft_state_init(&bn_state, sizeof (bn_devstate_t), 1)) != 0) { return (status); } if ((status = mod_install(&modlinkage)) != 0) { ddi_soft_state_fini(&bn_state); } return (status); } int _fini(void) { int status; if ((status = mod_remove(&modlinkage)) != 0) { return (status); } ddi_soft_state_fini(&bn_state); return (status); } int _info(struct modinfo *modinfop) { return (mod_info(&modlinkage, modinfop)); } static int bn_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) { int nblocks; int instance; bn_devstate_t *bsp; switch (cmd) { case DDI_ATTACH: instance = ddi_get_instance(dip); if (ddi_soft_state_zalloc(bn_state, instance) != DDI_SUCCESS) { cmn_err(CE_CONT, "%s%d: can't allocate state\n", ddi_get_name(dip), instance); return (DDI_FAILURE); } else bsp = ddi_get_soft_state(bn_state, instance); /* nblocks is needed for block devices */ nblocks = BN_SIZE; if (ddi_prop_create(DDI_DEV_T_NONE, dip, DDI_PROP_CANSLEEP, "nblocks", (caddr_t)&nblocks, sizeof (int)) != DDI_PROP_SUCCESS) { cmn_err(CE_CONT, "%s%d: can't create nblocks prop\n", ddi_get_name(dip), instance); goto attach_failed; } if ((ddi_create_minor_node(dip, "c,raw", S_IFCHR, instance, DDI_PSEUDO, 0) == DDI_FAILURE) || (ddi_create_minor_node(dip, "c", S_IFBLK, instance, DDI_PSEUDO, 0) == DDI_FAILURE)) { ddi_remove_minor_node(dip, NULL); goto attach_failed; } bsp->dip = dip; bsp->maxphys = BN_MAXPHYS; ddi_report_dev(dip); cmn_err(CE_CONT, "%s%d: bnull loaded.\n", ddi_get_name(dip), ddi_get_instance(dip)); return (DDI_SUCCESS); default: return (DDI_FAILURE); } attach_failed: cmn_err(CE_WARN, "%s%d: bnull failed.\n", ddi_get_name(dip), ddi_get_instance(dip)); /* cleanup */ (void) bn_detach(dip, DDI_DETACH); return (DDI_FAILURE); } static int bn_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) { int instance; register bn_devstate_t *bsp; switch (cmd) { case DDI_DETACH: ddi_prop_remove_all(dip); instance = ddi_get_instance(dip); bsp = ddi_get_soft_state(bn_state, instance); ddi_remove_minor_node(dip, NULL); ddi_soft_state_free(bn_state, instance); return (DDI_SUCCESS); default: return (DDI_FAILURE); } } /*ARGSUSED*/ static int bn_getinfo(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result) { bn_devstate_t *bsp; int error = DDI_FAILURE; switch (infocmd) { case DDI_INFO_DEVT2DEVINFO: if ((bsp = ddi_get_soft_state(bn_state, getminor((dev_t)arg))) != NULL) { *result = bsp->dip; error = DDI_SUCCESS; } else *result = NULL; break; case DDI_INFO_DEVT2INSTANCE: *result = (void *)getminor((dev_t)arg); error = DDI_SUCCESS; break; default: break; } return (error); } /*ARGSUSED*/ static int bn_open(dev_t *devp, int flag, int otyp, cred_t *cred) { if (otyp != OTYP_BLK && otyp != OTYP_CHR) return (EINVAL); if (ddi_get_soft_state(bn_state, getminor(*devp)) == NULL) return (ENXIO); return (0); } static void bn_minphys(struct buf *bp) { bn_devstate_t *bsp; bsp = ddi_get_soft_state(bn_state, getminor(bp->b_edev)); if (bp->b_bcount > bsp->maxphys) bp->b_bcount = bsp->maxphys; } /*ARGSUSED*/ static int bn_read(dev_t dev, struct uio *uiop, cred_t *credp) { int instance = getminor(dev); bn_devstate_t *bsp = ddi_get_soft_state(bn_state, instance); return (physio(bn_strategy, (struct buf *)0, dev, B_READ, bn_minphys, uiop)); } /*ARGSUSED*/ static int bn_write(dev_t dev, register struct uio *uiop, cred_t *credp) { int instance = getminor(dev); bn_devstate_t *bsp = ddi_get_soft_state(bn_state, instance); return (physio(bn_strategy, (struct buf *)0, dev, B_WRITE, bn_minphys, uiop)); } static int bn_strategy(struct buf *bp) { bn_devstate_t *bsp; u_long offset = bp->b_blkno * DEV_BSIZE; bsp = ddi_get_soft_state(bn_state, getminor(bp->b_edev)); if (bsp == NULL) { bp->b_error = ENXIO; bp->b_flags |= B_ERROR; } else { register caddr_t buf_addr, raddr; register unsigned nbytes; nbytes = bp->b_bcount; bp_mapin(bp); buf_addr = bp->b_un.b_addr; if (bp->b_flags & B_READ) (void) bzero(buf_addr, nbytes); bp->b_resid = bp->b_bcount - nbytes; } biodone(bp); return (0); } static int bn_print(dev_t dev, char *str) { int instance = getminor(dev); bn_devstate_t *bsp = ddi_get_soft_state(bn_state, instance); cmn_err(CE_WARN, "%s%d: %s\n", ddi_get_name(bsp->dip), instance, str); return (0); }