/* * Build directory vnodes based on the profile and the global * dev instance. */ void prof_filldir(sdev_node_t *ddv) { sdev_node_t *gdir; ASSERT(RW_READ_HELD(&ddv->sdev_contents)); if (!prof_dev_needupdate(ddv)) { ASSERT(RW_READ_HELD(&ddv->sdev_contents)); return; } /* * Upgrade to writer lock */ if (rw_tryupgrade(&ddv->sdev_contents) == 0) { /* * We need to drop the read lock and re-acquire it as a * write lock. While we do this the condition may change so we * need to re-check condition */ rw_exit(&ddv->sdev_contents); rw_enter(&ddv->sdev_contents, RW_WRITER); if (!prof_dev_needupdate(ddv)) { /* Downgrade back to the read lock before returning */ rw_downgrade(&ddv->sdev_contents); return; } } /* At this point we should have a write lock */ ASSERT(RW_WRITE_HELD(&ddv->sdev_contents)); sdcmn_err10(("devtree_gen (%s): %ld -> %ld\n", ddv->sdev_path, ddv->sdev_devtree_gen, devtree_gen)); gdir = ddv->sdev_origin; if (gdir != NULL) sdcmn_err10(("sdev_dir_gen (%s): %ld -> %ld\n", ddv->sdev_path, ddv->sdev_ldir_gen, gdir->sdev_gdir_gen)); /* update flags and generation number so next filldir is quick */ if ((ddv->sdev_flags & SDEV_BUILD) == SDEV_BUILD) { ddv->sdev_flags &= ~SDEV_BUILD; } ddv->sdev_devtree_gen = devtree_gen; if (gdir != NULL) ddv->sdev_ldir_gen = gdir->sdev_gdir_gen; prof_make_symlinks(ddv); prof_make_maps(ddv); prof_make_names(ddv); rw_downgrade(&ddv->sdev_contents); }
/* * Clean pts sdev_nodes that are no longer valid. */ static void devpts_prunedir(struct sdev_node *ddv) { struct vnode *vp; struct sdev_node *dv, *next = NULL; int (*vtor)(struct sdev_node *) = NULL; ASSERT(ddv->sdev_flags & SDEV_VTOR); vtor = (int (*)(struct sdev_node *))sdev_get_vtor(ddv); ASSERT(vtor); if (rw_tryupgrade(&ddv->sdev_contents) == NULL) { rw_exit(&ddv->sdev_contents); rw_enter(&ddv->sdev_contents, RW_WRITER); } for (dv = ddv->sdev_dot; dv; dv = next) { next = dv->sdev_next; /* skip stale nodes */ if (dv->sdev_flags & SDEV_STALE) continue; /* validate and prune only ready nodes */ if (dv->sdev_state != SDEV_READY) continue; switch (vtor(dv)) { case SDEV_VTOR_VALID: case SDEV_VTOR_SKIP: continue; case SDEV_VTOR_INVALID: sdcmn_err7(("prunedir: destroy invalid " "node: %s(%p)\n", dv->sdev_name, (void *)dv)); break; } vp = SDEVTOV(dv); if (vp->v_count > 0) continue; SDEV_HOLD(dv); /* remove the cache node */ (void) sdev_cache_update(ddv, &dv, dv->sdev_name, SDEV_CACHE_DELETE); } rw_downgrade(&ddv->sdev_contents); }
static int splat_rwlock_test5(struct file *file, void *arg) { rw_priv_t *rwp; int rc = -EINVAL; rwp = (rw_priv_t *)kmalloc(sizeof(*rwp), GFP_KERNEL); if (rwp == NULL) return -ENOMEM; splat_init_rw_priv(rwp, file); rw_enter(&rwp->rw_rwlock, RW_WRITER); if (!RW_WRITE_HELD(&rwp->rw_rwlock)) { splat_vprint(file, SPLAT_RWLOCK_TEST5_NAME, "rwlock should be write lock: %d\n", RW_WRITE_HELD(&rwp->rw_rwlock)); goto out; } rw_downgrade(&rwp->rw_rwlock); if (!RW_READ_HELD(&rwp->rw_rwlock)) { splat_vprint(file, SPLAT_RWLOCK_TEST5_NAME, "rwlock should be read lock: %d\n", RW_READ_HELD(&rwp->rw_rwlock)); goto out; } rc = 0; splat_vprint(file, SPLAT_RWLOCK_TEST5_NAME, "%s", "rwlock properly downgraded\n"); out: rw_exit(&rwp->rw_rwlock); rw_destroy(&rwp->rw_rwlock); kfree(rwp); return rc; }
void rumpuser_rw_downgrade(struct rumpuser_rw *rw) { rw_downgrade(rw); }
int sam_refresh_shared_reader_ino( sam_node_t *ip, /* Pointer to the inode */ boolean_t writelock, /* Inode WRITER lock held, */ /* otherwise READER lock held. */ cred_t *credp) /* credentials. */ { sam_id_t id; struct sam_perm_inode *permip; buf_t *bp; int refresh = 0; int error; if ((ip->updtime + ip->mp->mt.fi_invalid) > SAM_SECOND()) { return (0); } if (!writelock) { /* * Acquire inode lock before buffer lock. Recheck the update * time. */ if (!rw_tryupgrade(&ip->inode_rwl)) { RW_UNLOCK_OS(&ip->inode_rwl, RW_READER); RW_LOCK_OS(&ip->inode_rwl, RW_WRITER); if ((ip->updtime + ip->mp->mt.fi_invalid) > SAM_SECOND()) { error = 0; goto out; } } } id = ip->di.id; if ((error = sam_read_ino(ip->mp, id.ino, &bp, &permip))) { goto out; } if ((permip->di.mode != 0) && (permip->di.id.ino == ip->di.id.ino) && (permip->di.id.gen == ip->di.id.gen)) { if ((permip->di.modify_time.tv_sec != ip->di.modify_time.tv_sec) || (permip->di.modify_time.tv_nsec != ip->di.modify_time.tv_nsec)|| (permip->di.change_time.tv_sec != ip->di.change_time.tv_sec) || (permip->di.change_time.tv_nsec != ip->di.change_time.tv_nsec)|| (permip->di.residence_time != ip->di.residence_time) || (permip->di.rm.size != ip->di.rm.size) || (permip->di.mode != ip->di.mode)) { refresh = 1; } else { ip->di.uid = permip->di.uid; ip->di.gid = permip->di.gid; } } else { refresh = 1; error = ENOENT; /* This inode has been removed */ } if (refresh) { vnode_t *vp = SAM_ITOV(ip); /* * If a refresh is needed on a directory inode, * invalidate associated dnlc entries. */ if (S_ISDIR(ip->di.mode)) { sam_invalidate_dnlc(vp); } /* * Move shared_writer's inode copy into inode. Set size * and invalidate pages. Set shared_reader update time. */ ip->di = permip->di; /* Move disk ino to incore ino */ ip->di2 = permip->di2; brelse(bp); vp->v_type = IFTOVT(S_ISREQ(ip->di.mode) ? S_IFREG : ip->di.mode); sam_set_size(ip); (void) VOP_PUTPAGE_OS(vp, 0, 0, B_INVAL, credp, NULL); if (ip->di.status.b.acl) { (void) sam_acl_inactive(ip); error = sam_get_acl(ip, &ip->aclp); } ip->updtime = SAM_SECOND(); } else { ip->updtime = SAM_SECOND(); brelse(bp); } out: if (!writelock) { rw_downgrade(&ip->inode_rwl); } return (error); }
int zap_lockdir(objset_t *os, uint64_t obj, dmu_tx_t *tx, krw_t lti, int fatreader, zap_t **zapp) { zap_t *zap; dmu_buf_t *db; krw_t lt; int err; *zapp = NULL; err = dmu_buf_hold(os, obj, 0, NULL, &db); if (err) return (err); #ifdef ZFS_DEBUG { dmu_object_info_t doi; dmu_object_info_from_db(db, &doi); ASSERT(dmu_ot[doi.doi_type].ot_byteswap == zap_byteswap); } #endif zap = dmu_buf_get_user(db); if (zap == NULL) zap = mzap_open(os, obj, db); /* * We're checking zap_ismicro without the lock held, in order to * tell what type of lock we want. Once we have some sort of * lock, see if it really is the right type. In practice this * can only be different if it was upgraded from micro to fat, * and micro wanted WRITER but fat only needs READER. */ lt = (!zap->zap_ismicro && fatreader) ? RW_READER : lti; rw_enter(&zap->zap_rwlock, lt); if (lt != ((!zap->zap_ismicro && fatreader) ? RW_READER : lti)) { /* it was upgraded, now we only need reader */ ASSERT(lt == RW_WRITER); ASSERT(RW_READER == (!zap->zap_ismicro && fatreader) ? RW_READER : lti); rw_downgrade(&zap->zap_rwlock); lt = RW_READER; } zap->zap_objset = os; if (lt == RW_WRITER) dmu_buf_will_dirty(db, tx); ASSERT3P(zap->zap_dbuf, ==, db); ASSERT(!zap->zap_ismicro || zap->zap_m.zap_num_entries <= zap->zap_m.zap_num_chunks); if (zap->zap_ismicro && tx && zap->zap_m.zap_num_entries == zap->zap_m.zap_num_chunks) { uint64_t newsz = db->db_size + SPA_MINBLOCKSIZE; if (newsz > MZAP_MAX_BLKSZ) { dprintf("upgrading obj %llu: num_entries=%u\n", obj, zap->zap_m.zap_num_entries); mzap_upgrade(zap, tx); *zapp = zap; return (0); } err = dmu_object_set_blocksize(os, obj, newsz, 0, tx); ASSERT3U(err, ==, 0); zap->zap_m.zap_num_chunks = db->db_size / MZAP_ENT_LEN - 1; }
static void devvt_cleandir(struct vnode *dvp, struct cred *cred) { struct sdev_node *sdvp = VTOSDEV(dvp); struct sdev_node *dv, *next = NULL; int min, cnt; char found = 0; mutex_enter(&vc_lock); cnt = VC_INSTANCES_COUNT; mutex_exit(&vc_lock); /* We have to fool warlock this way, otherwise it will complain */ #ifndef __lock_lint if (rw_tryupgrade(&sdvp->sdev_contents) == NULL) { rw_exit(&sdvp->sdev_contents); rw_enter(&sdvp->sdev_contents, RW_WRITER); } #else rw_enter(&sdvp->sdev_contents, RW_WRITER); #endif /* 1. prune invalid nodes and rebuild stale symlinks */ devvt_prunedir(sdvp); /* 2. create missing nodes */ for (min = 0; min < cnt; min++) { char nm[16]; if (vt_minor_valid(min) == B_FALSE) continue; (void) snprintf(nm, sizeof (nm), "%d", min); found = 0; for (dv = SDEV_FIRST_ENTRY(sdvp); dv; dv = next) { next = SDEV_NEXT_ENTRY(sdvp, dv); /* validate only ready nodes */ if (dv->sdev_state != SDEV_READY) continue; if (strcmp(nm, dv->sdev_name) == 0) { found = 1; break; } } if (!found) { devvt_create_snode(sdvp, nm, cred, SDEV_VATTR); } } /* 3. create active link node and console user link node */ found = 0; for (dv = SDEV_FIRST_ENTRY(sdvp); dv; dv = next) { next = SDEV_NEXT_ENTRY(sdvp, dv); /* validate only ready nodes */ if (dv->sdev_state != SDEV_READY) continue; if ((strcmp(dv->sdev_name, DEVVT_ACTIVE_NAME) == NULL)) found |= 0x01; if ((strcmp(dv->sdev_name, DEVVT_CONSUSER_NAME) == NULL)) found |= 0x02; if ((found & 0x01) && (found & 0x02)) break; } if (!(found & 0x01)) devvt_create_snode(sdvp, DEVVT_ACTIVE_NAME, cred, SDEV_VLINK); if (!(found & 0x02)) devvt_create_snode(sdvp, DEVVT_CONSUSER_NAME, cred, SDEV_VLINK); #ifndef __lock_lint rw_downgrade(&sdvp->sdev_contents); #else rw_exit(&sdvp->sdev_contents); #endif }
/* * If DV_BUILD is set, we call into nexus driver to do a BUS_CONFIG_ALL. * Otherwise, simply return cached dv_node's. Hotplug code always call * devfs_clean() to invalid the dv_node cache. */ static int devfs_readdir(struct vnode *dvp, struct uio *uiop, struct cred *cred, int *eofp) { struct dv_node *ddv, *dv; struct dirent64 *de, *bufp; offset_t diroff; offset_t soff; size_t reclen, movesz; int error; struct vattr va; size_t bufsz; ddv = VTODV(dvp); dcmn_err2(("devfs_readdir %s: offset %lld len %ld\n", ddv->dv_name, uiop->uio_loffset, uiop->uio_iov->iov_len)); ASSERT(ddv->dv_attr || ddv->dv_attrvp); ASSERT(RW_READ_HELD(&ddv->dv_contents)); if (uiop->uio_loffset >= MAXOFF_T) { if (eofp) *eofp = 1; return (0); } if (uiop->uio_iovcnt != 1) return (EINVAL); if (dvp->v_type != VDIR) return (ENOTDIR); /* Load the initial contents */ if (ddv->dv_flags & DV_BUILD) { if (!rw_tryupgrade(&ddv->dv_contents)) { rw_exit(&ddv->dv_contents); rw_enter(&ddv->dv_contents, RW_WRITER); } /* recheck and fill */ if (ddv->dv_flags & DV_BUILD) dv_filldir(ddv); rw_downgrade(&ddv->dv_contents); } soff = uiop->uio_offset; bufsz = uiop->uio_iov->iov_len; de = bufp = kmem_alloc(bufsz, KM_SLEEP); movesz = 0; dv = (struct dv_node *)-1; /* * Move as many entries into the uio structure as it will take. * Special case "." and "..". */ diroff = 0; if (soff == 0) { /* . */ reclen = DIRENT64_RECLEN(strlen(".")); if ((movesz + reclen) > bufsz) goto full; de->d_ino = (ino64_t)ddv->dv_ino; de->d_off = (off64_t)diroff + 1; de->d_reclen = (ushort_t)reclen; /* use strncpy(9f) to zero out uninitialized bytes */ (void) strncpy(de->d_name, ".", DIRENT64_NAMELEN(reclen)); movesz += reclen; de = (dirent64_t *)((char *)de + reclen); dcmn_err3(("devfs_readdir: A: diroff %lld, soff %lld: '%s' " "reclen %lu\n", diroff, soff, ".", reclen)); } diroff++; if (soff <= 1) { /* .. */ reclen = DIRENT64_RECLEN(strlen("..")); if ((movesz + reclen) > bufsz) goto full; de->d_ino = (ino64_t)ddv->dv_dotdot->dv_ino; de->d_off = (off64_t)diroff + 1; de->d_reclen = (ushort_t)reclen; /* use strncpy(9f) to zero out uninitialized bytes */ (void) strncpy(de->d_name, "..", DIRENT64_NAMELEN(reclen)); movesz += reclen; de = (dirent64_t *)((char *)de + reclen); dcmn_err3(("devfs_readdir: B: diroff %lld, soff %lld: '%s' " "reclen %lu\n", diroff, soff, "..", reclen)); } diroff++; for (dv = ddv->dv_dot; dv; dv = dv->dv_next, diroff++) { /* * although DDM_INTERNAL_PATH minor nodes are skipped for * readdirs outside the kernel, they still occupy directory * offsets */ if (diroff < soff || ((dv->dv_flags & DV_INTERNAL) && (cred != kcred))) continue; reclen = DIRENT64_RECLEN(strlen(dv->dv_name)); if ((movesz + reclen) > bufsz) { dcmn_err3(("devfs_readdir: C: diroff " "%lld, soff %lld: '%s' reclen %lu\n", diroff, soff, dv->dv_name, reclen)); goto full; } de->d_ino = (ino64_t)dv->dv_ino; de->d_off = (off64_t)diroff + 1; de->d_reclen = (ushort_t)reclen; /* use strncpy(9f) to zero out uninitialized bytes */ ASSERT(strlen(dv->dv_name) + 1 <= DIRENT64_NAMELEN(reclen)); (void) strncpy(de->d_name, dv->dv_name, DIRENT64_NAMELEN(reclen)); movesz += reclen; de = (dirent64_t *)((char *)de + reclen); dcmn_err4(("devfs_readdir: D: diroff " "%lld, soff %lld: '%s' reclen %lu\n", diroff, soff, dv->dv_name, reclen)); } /* the buffer is full, or we exhausted everything */ full: dcmn_err3(("devfs_readdir: moving %lu bytes: " "diroff %lld, soff %lld, dv %p\n", movesz, diroff, soff, (void *)dv)); if ((movesz == 0) && dv) error = EINVAL; /* cannot be represented */ else { error = uiomove(bufp, movesz, UIO_READ, uiop); if (error == 0) { if (eofp) *eofp = dv ? 0 : 1; uiop->uio_offset = diroff; } va.va_mask = AT_ATIME; gethrestime(&va.va_atime); rw_exit(&ddv->dv_contents); (void) devfs_setattr(dvp, &va, 0, cred, NULL); rw_enter(&ddv->dv_contents, RW_READER); } kmem_free(bufp, bufsz); return (error); }