summaryrefslogtreecommitdiff
path: root/usr/src/uts/common/os/share.c
diff options
context:
space:
mode:
Diffstat (limited to 'usr/src/uts/common/os/share.c')
-rw-r--r--usr/src/uts/common/os/share.c194
1 files changed, 134 insertions, 60 deletions
diff --git a/usr/src/uts/common/os/share.c b/usr/src/uts/common/os/share.c
index 9714e4b01f..5d3bbb1418 100644
--- a/usr/src/uts/common/os/share.c
+++ b/usr/src/uts/common/os/share.c
@@ -2,9 +2,8 @@
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
- * Common Development and Distribution License, Version 1.0 only
- * (the "License"). You may not use this file except in compliance
- * with the License.
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
@@ -20,8 +19,7 @@
* CDDL HEADER END
*/
/*
- * Copyright 1996-1998,2001,2003 Sun Microsystems, Inc.
- * All rights reserved.
+ * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -74,11 +72,11 @@ add_share(struct vnode *vp, struct shrlock *shr)
* Sanity check to make sure we have valid options.
* There is known overlap but it doesn't hurt to be careful.
*/
- if (shr->s_access & ~(F_RDACC|F_WRACC|F_RWACC)) {
+ if (shr->s_access & ~(F_RDACC|F_WRACC|F_RWACC|F_RMACC|F_MDACC)) {
return (EINVAL);
}
if (shr->s_deny & ~(F_NODNY|F_RDDNY|F_WRDNY|F_RWDNY|F_COMPAT|
- F_MANDDNY)) {
+ F_MANDDNY|F_RMDNY)) {
return (EINVAL);
}
@@ -115,7 +113,7 @@ add_share(struct vnode *vp, struct shrlock *shr)
if ((shrl->shr->s_deny & F_COMPAT) &&
(shr->s_deny & F_COMPAT) &&
((shrl->next == NULL) ||
- (shrl->shr->s_access & F_WRACC)))
+ (shrl->shr->s_access & F_WRACC)))
break;
}
@@ -280,13 +278,13 @@ is_match_for_del(struct shrlock *shr, struct shrlock *element)
* and pids.
*/
result = (nlmid1 == nlmid2 &&
- shr->s_pid == element->s_pid);
+ shr->s_pid == element->s_pid);
}
} else { /* not in a cluster */
result = ((shr->s_sysid == 0 &&
- shr->s_pid == element->s_pid) ||
- (shr->s_sysid != 0 &&
- shr->s_sysid == element->s_sysid));
+ shr->s_pid == element->s_pid) ||
+ (shr->s_sysid != 0 &&
+ shr->s_sysid == element->s_sysid));
}
return (result);
}
@@ -315,11 +313,11 @@ del_share(struct vnode *vp, struct shrlock *shr)
shrlp = &vp->v_shrlocks;
while (*shrlp) {
if ((shr->s_own_len == (*shrlp)->shr->s_own_len &&
- (bcmp(shr->s_owner, (*shrlp)->shr->s_owner,
- shr->s_own_len) == 0)) ||
+ (bcmp(shr->s_owner, (*shrlp)->shr->s_owner,
+ shr->s_own_len) == 0)) ||
- (shr->s_own_len == 0 &&
- is_match_for_del(shr, (*shrlp)->shr))) {
+ (shr->s_own_len == 0 &&
+ is_match_for_del(shr, (*shrlp)->shr))) {
shrl = *shrlp;
*shrlp = shrl->next;
@@ -427,7 +425,7 @@ static int
isreadonly(struct vnode *vp)
{
return (vp->v_type != VCHR && vp->v_type != VBLK &&
- vp->v_type != VFIFO && vn_is_readonly(vp));
+ vp->v_type != VFIFO && vn_is_readonly(vp));
}
#ifdef DEBUG
@@ -489,46 +487,117 @@ print_share(struct shrlock *shr)
/*
* Return non-zero if the given I/O request conflicts with a registered
* share reservation.
+ *
+ * A process is identified by the tuple (sysid, pid). When the caller
+ * context is passed to nbl_share_conflict, the sysid and pid in the
+ * caller context are used. Otherwise the sysid is zero, and the pid is
+ * taken from the current process.
+ *
+ * Conflict Algorithm:
+ * 1. An op request of NBL_READ will fail if a different
+ * process has a mandatory share reservation with deny read.
+ *
+ * 2. An op request of NBL_WRITE will fail if a different
+ * process has a mandatory share reservation with deny write.
+ *
+ * 3. An op request of NBL_READWRITE will fail if a different
+ * process has a mandatory share reservation with deny read
+ * or deny write.
+ *
+ * 4. An op request of NBL_REMOVE will fail if there is
+ * a mandatory share reservation with an access of read,
+ * write, or remove. (Anything other than meta data access).
+ *
+ * 5. An op request of NBL_RENAME will fail if there is
+ * a mandatory share reservation with:
+ * a) access write or access remove
+ * or
+ * b) access read and deny remove
+ *
+ * Otherwise there is no conflict and the op request succeeds.
+ *
+ * This behavior is required for interoperability between
+ * the nfs server, cifs server, and local access.
+ * This behavior can result in non-posix semantics.
+ *
+ * When mandatory share reservations are enabled, a process
+ * should call nbl_share_conflict to determine if the
+ * desired operation would conflict with an existing share
+ * reservation.
+ *
+ * The call to nbl_share_conflict may be skipped if the
+ * process has an existing share reservation and the operation
+ * is being performed in the context of that existing share
+ * reservation.
*/
-
int
-nbl_share_conflict(vnode_t *vp, nbl_op_t op)
+nbl_share_conflict(vnode_t *vp, nbl_op_t op, caller_context_t *ct)
{
struct shrlocklist *shrl;
int conflict = 0;
+ pid_t pid;
+ int sysid;
ASSERT(nbl_in_crit(vp));
+ if (ct == NULL) {
+ pid = curproc->p_pid;
+ sysid = 0;
+ } else {
+ pid = ct->cc_pid;
+ sysid = ct->cc_sysid;
+ }
+
mutex_enter(&vp->v_lock);
for (shrl = vp->v_shrlocks; shrl != NULL; shrl = shrl->next) {
- if (shrl->shr->s_sysid == 0 &&
- (shrl->shr->s_deny & F_MANDDNY) &&
- shrl->shr->s_pid != curproc->p_pid) {
- switch (op) {
- case NBL_READ:
- if (shrl->shr->s_deny & F_RDDNY)
- conflict = 1;
- break;
- case NBL_WRITE:
- if (shrl->shr->s_deny & F_WRDNY)
- conflict = 1;
- break;
- case NBL_READWRITE:
- if (shrl->shr->s_deny & F_RWDNY)
- conflict = 1;
- break;
- case NBL_RENAME:
- case NBL_REMOVE:
+ if (!(shrl->shr->s_deny & F_MANDDNY))
+ continue;
+ /*
+ * NBL_READ, NBL_WRITE, and NBL_READWRITE need to
+ * check if the share reservation being examined
+ * belongs to the current process.
+ * NBL_REMOVE and NBL_RENAME do not.
+ * This behavior is required by the conflict
+ * algorithm described above.
+ */
+ switch (op) {
+ case NBL_READ:
+ if ((shrl->shr->s_deny & F_RDDNY) &&
+ (shrl->shr->s_sysid != sysid ||
+ shrl->shr->s_pid != pid))
conflict = 1;
- break;
+ break;
+ case NBL_WRITE:
+ if ((shrl->shr->s_deny & F_WRDNY) &&
+ (shrl->shr->s_sysid != sysid ||
+ shrl->shr->s_pid != pid))
+ conflict = 1;
+ break;
+ case NBL_READWRITE:
+ if ((shrl->shr->s_deny & F_RWDNY) &&
+ (shrl->shr->s_sysid != sysid ||
+ shrl->shr->s_pid != pid))
+ conflict = 1;
+ break;
+ case NBL_REMOVE:
+ if (shrl->shr->s_access & (F_RWACC|F_RMACC))
+ conflict = 1;
+ break;
+ case NBL_RENAME:
+ if (shrl->shr->s_access & (F_WRACC|F_RMACC))
+ conflict = 1;
+
+ else if ((shrl->shr->s_access & F_RDACC) &&
+ (shrl->shr->s_deny & F_RMDNY))
+ conflict = 1;
+ break;
#ifdef DEBUG
- default:
- cmn_err(CE_PANIC,
- "nbl_share_conflict: bogus op (%d)",
- op);
- break;
+ default:
+ cmn_err(CE_PANIC,
+ "nbl_share_conflict: bogus op (%d)",
+ op);
+ break;
#endif
- }
}
if (conflict)
break;
@@ -546,10 +615,16 @@ nbl_share_conflict(vnode_t *vp, nbl_op_t op)
int
share_blocks_lock(vnode_t *vp, flock64_t *flkp)
{
+ caller_context_t ct;
+
ASSERT(nbl_in_crit(vp));
+ ct.cc_pid = flkp->l_pid;
+ ct.cc_sysid = flkp->l_sysid;
+ ct.cc_caller_id = 0;
+
if ((flkp->l_type == F_RDLCK || flkp->l_type == F_WRLCK) &&
- nbl_share_conflict(vp, nbl_lock_to_op(flkp->l_type)))
+ nbl_share_conflict(vp, nbl_lock_to_op(flkp->l_type), &ct))
return (1);
else
return (0);
@@ -602,35 +677,34 @@ lock_blocks_share(vnode_t *vp, struct shrlock *shr)
{
struct flock64 lck;
int error;
-
- /*
- * We don't currently have a good way to match lock
- * ownership with share ownership for remote requests.
- * Fortunately, we know that only local processes (in particular,
- * local CIFS servers) care about conflicts between locks and
- * share reservations, and we can distinguish local processes from
- * each other and from remote processes.
- */
- ASSERT(shr->s_sysid == 0);
+ v_mode_t mode = 0;
if ((shr->s_deny & (F_RWDNY|F_COMPAT)) == 0) {
/* if no deny mode, then there's no conflict */
return (0);
}
- lck.l_type = ((shr->s_deny & F_RDDNY) ? F_WRLCK : F_RDLCK);
+ /* check for conflict with mapped region */
+ if ((shr->s_deny & F_RWDNY) == F_WRDNY) {
+ mode = V_WRITE;
+ } else if ((shr->s_deny & F_RWDNY) == F_RDDNY) {
+ mode = V_READ;
+ } else {
+ mode = V_RDORWR;
+ }
+ if (vn_is_mapped(vp, mode))
+ return (1);
+ lck.l_type = ((shr->s_deny & F_RDDNY) ? F_WRLCK : F_RDLCK);
lck.l_whence = 0;
lck.l_start = 0;
lck.l_len = 0; /* to EOF */
- /* would check here for conflict with mapped region */
-
/* XXX should use non-NULL cred? */
- error = VOP_FRLOCK(vp, F_GETLK, &lck, 0, 0, NULL, NULL);
+ error = VOP_FRLOCK(vp, F_GETLK, &lck, 0, 0, NULL, NULL, NULL);
if (error != 0) {
cmn_err(CE_WARN, "lock_blocks_share: unexpected error (%d)",
- error);
+ error);
return (1);
}