/* * New Leaf driver open entry point. We make a vnode and go through specfs * in order to obtain open close exclusions guarantees. Note that we drop * OTYP_LYR if it was specified - we are going through specfs and it provides * last close semantics (FKLYR is provided to open(9E)). Also, since * spec_open will drive attach via e_ddi_hold_devi_by_dev for a makespecvp * vnode with no SDIP_SET on the common snode, the dev_lopen caller no longer * needs to call ddi_hold_installed_driver. */ int dev_lopen(dev_t *devp, int flag, int otype, struct cred *cred) { struct vnode *vp; int error; struct vnode *cvp; vp = makespecvp(*devp, (otype == OTYP_BLK) ? VBLK : VCHR); error = VOP_OPEN(&vp, flag | FKLYR, cred, NULL); if (error == 0) { /* Pick up the (possibly) new dev_t value. */ *devp = vp->v_rdev; /* * Place extra hold on the common vnode, which contains the * open count, so that it is not destroyed by the VN_RELE of * the shadow makespecvp vnode below. */ cvp = STOV(VTOCS(vp)); VN_HOLD(cvp); } /* release the shadow makespecvp vnode. */ VN_RELE(vp); return (error); }
/* * Leaf driver close entry point. We make a vnode and go through specfs in * order to obtain open close exclusions guarantees. Note that we drop * OTYP_LYR if it was specified - we are going through specfs and it provides * last close semantics (FLKYR is provided to close(9E)). */ int dev_lclose(dev_t dev, int flag, int otype, struct cred *cred) { struct vnode *vp; int error; struct vnode *cvp; char *funcname; ulong_t offset; vp = makespecvp(dev, (otype == OTYP_BLK) ? VBLK : VCHR); error = VOP_CLOSE(vp, flag | FKLYR, 1, (offset_t)0, cred, NULL); /* * Release the extra dev_lopen hold on the common vnode. We inline a * VN_RELE(cvp) call so that we can detect more dev_lclose calls than * dev_lopen calls without panic. See vn_rele. If our inline of * vn_rele called VOP_INACTIVE(cvp, CRED(), ...) we would panic on the * "release the makespecvp vnode" VN_RELE(vp) that follows - so * instead we diagnose this situation. Note that the driver has * still seen a double close(9E), but that would have occurred with * the old dev_close implementation too. */ cvp = STOV(VTOCS(vp)); mutex_enter(&cvp->v_lock); switch (cvp->v_count) { default: cvp->v_count--; break; case 0: VTOS(vp)->s_commonvp = NULL; /* avoid panic */ /*FALLTHROUGH*/ case 1: /* * The following message indicates a serious problem in the * identified driver, the driver should be fixed. If obtaining * a panic dump is needed to diagnose the driver problem then * adding "set dev_lclose_ce=3" to /etc/system will cause a * panic when this occurs. */ funcname = modgetsymname((uintptr_t)caller(), &offset); cmn_err(dev_lclose_ce, "dev_lclose: extra close of dev_t 0x%lx " "from %s`%s()", dev, mod_containing_pc(caller()), funcname ? funcname : "unknown..."); break; } mutex_exit(&cvp->v_lock); /* release the makespecvp vnode. */ VN_RELE(vp); return (error); }
/* * wput(9E) is symmetric for master and slave sides, so this handles both * without splitting the codepath. (The only exception to this is the * processing of zcons ioctls, which is restricted to the master side.) * * zc_wput() looks at the other side; if there is no process holding that * side open, it frees the message. This prevents processes from hanging * if no one is holding open the console. Otherwise, it putnext's high * priority messages, putnext's normal messages if possible, and otherwise * enqueues the messages; in the case that something is enqueued, wsrv(9E) * will take care of eventually shuttling I/O to the other side. */ static void zc_wput(queue_t *qp, mblk_t *mp) { unsigned char type = mp->b_datap->db_type; zc_state_t *zcs; struct iocblk *iocbp; file_t *slave_filep; struct snode *slave_snodep; int slave_fd; ASSERT(qp->q_ptr); DBG1("entering zc_wput, %s side", zc_side(qp)); /* * Process zcons ioctl messages if qp is the master console's write * queue. */ zcs = (zc_state_t *)qp->q_ptr; if (zcs->zc_master_rdq != NULL && qp == WR(zcs->zc_master_rdq) && type == M_IOCTL) { iocbp = (struct iocblk *)(void *)mp->b_rptr; switch (iocbp->ioc_cmd) { case ZC_HOLDSLAVE: /* * Hold the slave's vnode and increment the refcount * of the snode. If the vnode is already held, then * indicate success. */ if (iocbp->ioc_count != TRANSPARENT) { miocack(qp, mp, 0, EINVAL); return; } if (zcs->zc_slave_vnode != NULL) { miocack(qp, mp, 0, 0); return; } /* * The process that passed the ioctl must be running in * the global zone. */ if (curzone != global_zone) { miocack(qp, mp, 0, EINVAL); return; } /* * The calling process must pass a file descriptor for * the slave device. */ slave_fd = (int)(intptr_t)*(caddr_t *)(void *)mp->b_cont-> b_rptr; slave_filep = getf(slave_fd); if (slave_filep == NULL) { miocack(qp, mp, 0, EINVAL); return; } if (ZC_STATE_TO_SLAVEDEV(zcs) != slave_filep->f_vnode->v_rdev) { releasef(slave_fd); miocack(qp, mp, 0, EINVAL); return; } /* * Get a reference to the slave's vnode. Also bump the * reference count on the associated snode. */ ASSERT(vn_matchops(slave_filep->f_vnode, spec_getvnodeops())); zcs->zc_slave_vnode = slave_filep->f_vnode; VN_HOLD(zcs->zc_slave_vnode); slave_snodep = VTOCS(zcs->zc_slave_vnode); mutex_enter(&slave_snodep->s_lock); ++slave_snodep->s_count; mutex_exit(&slave_snodep->s_lock); releasef(slave_fd); miocack(qp, mp, 0, 0); return; case ZC_RELEASESLAVE: /* * Release the master's handle on the slave's vnode. * If there isn't a handle for the vnode, then indicate * success. */ if (iocbp->ioc_count != TRANSPARENT) { miocack(qp, mp, 0, EINVAL); return; } if (zcs->zc_slave_vnode == NULL) { miocack(qp, mp, 0, 0); return; } /* * The process that passed the ioctl must be running in * the global zone. */ if (curzone != global_zone) { miocack(qp, mp, 0, EINVAL); return; } /* * The process that passed the ioctl must have provided * a file descriptor for the slave device. Make sure * this is correct. */ slave_fd = (int)(intptr_t)*(caddr_t *)(void *)mp->b_cont-> b_rptr; slave_filep = getf(slave_fd); if (slave_filep == NULL) { miocack(qp, mp, 0, EINVAL); return; } if (zcs->zc_slave_vnode->v_rdev != slave_filep->f_vnode->v_rdev) { releasef(slave_fd); miocack(qp, mp, 0, EINVAL); return; } /* * Decrement the snode's reference count and release the * vnode. */ ASSERT(vn_matchops(slave_filep->f_vnode, spec_getvnodeops())); slave_snodep = VTOCS(zcs->zc_slave_vnode); mutex_enter(&slave_snodep->s_lock); --slave_snodep->s_count; mutex_exit(&slave_snodep->s_lock); VN_RELE(zcs->zc_slave_vnode); zcs->zc_slave_vnode = NULL; releasef(slave_fd); miocack(qp, mp, 0, 0); return; default: break; } } if (zc_switch(RD(qp)) == NULL) { DBG1("wput to %s side (no one listening)", zc_side(qp)); switch (type) { case M_FLUSH: handle_mflush(qp, mp); break; case M_IOCTL: miocnak(qp, mp, 0, 0); break; default: freemsg(mp); break; } return; } if (type >= QPCTL) { DBG1("(hipri) wput, %s side", zc_side(qp)); switch (type) { case M_READ: /* supposedly from ldterm? */ DBG("zc_wput: tossing M_READ\n"); freemsg(mp); break; case M_FLUSH: handle_mflush(qp, mp); break; default: /* * Put this to the other side. */ ASSERT(zc_switch(RD(qp)) != NULL); putnext(zc_switch(RD(qp)), mp); break; } DBG1("done (hipri) wput, %s side", zc_side(qp)); return; } /* * Only putnext if there isn't already something in the queue. * otherwise things would wind up out of order. */ if (qp->q_first == NULL && bcanputnext(RD(zc_switch(qp)), mp->b_band)) { DBG("wput: putting message to other side\n"); putnext(RD(zc_switch(qp)), mp); } else { DBG("wput: putting msg onto queue\n"); (void) putq(qp, mp); } DBG1("done wput, %s side", zc_side(qp)); }