/* * 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); }
/* * This opens and closes the appropriate device with minor number - * hopefully, it will cause the driver to attach and register a controller * with us */ static vnode_t * rsmops_device_open(const char *major_name, const minor_t minor_num) { major_t maj; vnode_t *vp; int ret; if (minor_num == (minor_t)-1) { return (NULL); } maj = ddi_name_to_major((char *)major_name); if (maj == (major_t)-1) { return (NULL); } vp = makespecvp(makedevice(maj, minor_num), VCHR); ret = VOP_OPEN(&vp, FREAD|FWRITE, CRED(), NULL); if (ret == 0) { return (vp); } else { VN_RELE(vp); return (NULL); } }
/* * 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); }
int t_kopen(file_t *fp, dev_t rdev, int flags, TIUSER **tiptr, cred_t *cr) { int madefp = 0; struct T_info_ack inforeq; int retval; vnode_t *vp; struct strioctl strioc; int error; TIUSER *ntiptr; int rtries = 0; /* * Special case for install: miniroot needs to be able to access files * via NFS as though it were always in the global zone. */ if (nfs_global_client_only != 0) cr = kcred; KTLILOG(2, "t_kopen: fp %x, ", fp); KTLILOG(2, "rdev %x, ", rdev); KTLILOG(2, "flags %x\n", flags); *tiptr = NULL; error = 0; retval = 0; if (fp == NULL) { if (rdev == 0 || rdev == NODEV) { KTLILOG(1, "t_kopen: null device\n", 0); return (EINVAL); } /* * allocate a file pointer, but * no file descripter. */ if ((error = falloc(NULL, flags, &fp, NULL)) != 0) { KTLILOG(1, "t_kopen: falloc: %d\n", error); return (error); } /* Install proper cred in file */ if (cr != fp->f_cred) { crhold(cr); crfree(fp->f_cred); fp->f_cred = cr; } vp = makespecvp(rdev, VCHR); /* * this will call the streams open for us. * Want to retry if error is EAGAIN, the streams open routine * might fail due to temporarely out of memory. */ do { if ((error = VOP_OPEN(&vp, flags, cr)) == EAGAIN) { (void) delay(hz); } } while (error == EAGAIN && ++rtries < 5); if (error) { KTLILOG(1, "t_kopen: VOP_OPEN: %d\n", error); unfalloc(fp); VN_RELE(vp); return (error); } /* * fp is completely initialized so drop the write lock. * I actually don't need any locking on fp in here since * there is no fd pointing at it. However, since I could * call closef if there is an error and closef requires * the fp read locked, I will acquire the read lock here * and make sure I release it before I leave this routine. */ fp->f_vnode = vp; mutex_exit(&fp->f_tlock); madefp = 1; } else { vp = fp->f_vnode; } if (vp->v_stream == NULL) { if (madefp) (void) closef(fp); KTLILOG(1, "t_kopen: not a streams device\n", 0); return (ENOSTR); } /* * allocate a new transport structure */ ntiptr = kmem_alloc(TIUSERSZ, KM_SLEEP); ntiptr->fp = fp; ntiptr->flags = madefp ? MADE_FP : 0; KTLILOG(2, "t_kopen: vp %x, ", vp); KTLILOG(2, "stp %x\n", vp->v_stream); /* * see if TIMOD is already pushed */ error = strioctl(vp, I_FIND, (intptr_t)"timod", 0, K_TO_K, cr, &retval); if (error) { kmem_free(ntiptr, TIUSERSZ); if (madefp) (void) closef(fp); KTLILOG(1, "t_kopen: strioctl(I_FIND, timod): %d\n", error); return (error); } if (retval == 0) { tryagain: error = strioctl(vp, I_PUSH, (intptr_t)"timod", 0, K_TO_K, cr, &retval); if (error) { switch (error) { case ENOSPC: case EAGAIN: case ENOSR: /* * This probably means the master file * should be tuned. */ cmn_err(CE_WARN, "t_kopen: I_PUSH of timod failed, error %d\n", error); (void) delay(hz); error = 0; goto tryagain; default: kmem_free(ntiptr, TIUSERSZ); if (madefp) (void) closef(fp); KTLILOG(1, "t_kopen: I_PUSH (timod): %d", error); return (error); } } } inforeq.PRIM_type = T_INFO_REQ; strioc.ic_cmd = TI_GETINFO; strioc.ic_timout = 0; strioc.ic_dp = (char *)&inforeq; strioc.ic_len = (int)sizeof (struct T_info_req); error = strdoioctl(vp->v_stream, &strioc, FNATIVE, K_TO_K, cr, &retval); if (error) { kmem_free(ntiptr, TIUSERSZ); if (madefp) (void) closef(fp); KTLILOG(1, "t_kopen: strdoioctl(T_INFO_REQ): %d\n", error); return (error); } if (retval) { if ((retval & 0xff) == TSYSERR) error = (retval >> 8) & 0xff; else error = t_tlitosyserr(retval & 0xff); kmem_free(ntiptr, TIUSERSZ); if (madefp) (void) closef(fp); KTLILOG(1, "t_kopen: strdoioctl(T_INFO_REQ): retval: 0x%x\n", retval); return (error); }