commit 2af293bca114be0b4660e56320dde7fd4798e14f Author: Mikolaj Golub Date: Sat Feb 18 22:09:38 2012 +0200 Introduce VOP_UNPBIND(), VOP_UNPCONNECT(), and VOP_UNPDETACH() operations for setting and accessing vnode's v_socket field. The operations are necessary to implement proper unix socket handling on layered file systems like nullfs(5). This change fixes the long standing issue with nullfs(5) being in that unix sockets did not work between lower and upper layers: if we bound to a socket on the lower layer we could connect only to the lower path; if we bound to the upper layer we could connect only to the upper path. The new behavior is one can connect to both the lower and the upper paths regardless if we bind to lower or upper fs. Suggested by: kib Reviewed by: arch diff --git a/sys/kern/uipc_usrreq.c b/sys/kern/uipc_usrreq.c index 54b37ec..5165cf2 100644 --- a/sys/kern/uipc_usrreq.c +++ b/sys/kern/uipc_usrreq.c @@ -542,7 +542,7 @@ restart: UNP_LINK_WLOCK(); UNP_PCB_LOCK(unp); - vp->v_socket = unp->unp_socket; + VOP_UNPBIND(vp, unp->unp_socket); unp->unp_vnode = vp; unp->unp_addr = soun; unp->unp_flags &= ~UNP_BINDING; @@ -638,7 +638,7 @@ uipc_detach(struct socket *so) * XXXRW: Should assert vp->v_socket == so. */ if ((vp = unp->unp_vnode) != NULL) { - unp->unp_vnode->v_socket = NULL; + VOP_UNPDETACH(vp); unp->unp_vnode = NULL; } unp2 = unp->unp_conn; @@ -1256,17 +1256,15 @@ unp_prepare_reclaim(struct vnode *vp) active = 0; UNP_LINK_WLOCK(); - so = vp->v_socket; + VOP_UNPCONNECT(vp, &so); if (so == NULL) goto done; unp = sotounpcb(so); if (unp == NULL) goto done; UNP_PCB_LOCK(unp); - if (unp->unp_vnode != NULL) { - KASSERT(unp->unp_vnode == vp, - ("unp_prepare_reclaim: vp != unp->unp_vnode")); - vp->v_socket = NULL; + if (unp->unp_vnode == vp) { + VOP_UNPDETACH(vp); unp->unp_vnode = NULL; active = 1; } @@ -1347,7 +1345,7 @@ unp_connect(struct socket *so, struct sockaddr *nam, struct thread *td) * and to protect simultaneous locking of multiple pcbs. */ UNP_LINK_WLOCK(); - so2 = vp->v_socket; + VOP_UNPCONNECT(vp, &so2); if (so2 == NULL) { error = ECONNREFUSED; goto bad2; diff --git a/sys/kern/vfs_default.c b/sys/kern/vfs_default.c index e47498e..3405311 100644 --- a/sys/kern/vfs_default.c +++ b/sys/kern/vfs_default.c @@ -123,6 +123,9 @@ struct vop_vector default_vnodeops = { .vop_unlock = vop_stdunlock, .vop_vptocnp = vop_stdvptocnp, .vop_vptofh = vop_stdvptofh, + .vop_unpbind = vop_stdunpbind, + .vop_unpconnect = vop_stdunpconnect, + .vop_unpdetach = vop_stdunpdetach, }; /* @@ -1037,6 +1040,30 @@ vop_stdadvise(struct vop_advise_args *ap) return (error); } +int +vop_stdunpbind(struct vop_unpbind_args *ap) +{ + + ap->a_vp->v_socket = ap->a_socket; + return (0); +} + +int +vop_stdunpconnect(struct vop_unpconnect_args *ap) +{ + + *ap->a_socket = ap->a_vp->v_socket; + return (0); +} + +int +vop_stdunpdetach(struct vop_unpdetach_args *ap) +{ + + ap->a_vp->v_socket = NULL; + return (0); +} + /* * vfs default ops * used to fill the vfs function table to get reasonable default return values. diff --git a/sys/kern/vnode_if.src b/sys/kern/vnode_if.src index 5da4c1c..ff8ee8c 100644 --- a/sys/kern/vnode_if.src +++ b/sys/kern/vnode_if.src @@ -640,6 +640,26 @@ vop_advise { IN int advice; }; +%% unpbind vp E E E + +vop_unpbind { + IN struct vnode *vp; + IN struct socket *socket; +}; + +%% unpconnect vp L L L + +vop_unpconnect { + IN struct vnode *vp; + OUT struct socket **socket; +}; + +%% unpdetach vp = = = + +vop_unpdetach { + IN struct vnode *vp; +}; + # The VOPs below are spares at the end of the table to allow new VOPs to be # added in stable branches without breaking the KBI. New VOPs in HEAD should # be added above these spares. When merging a new VOP to a stable branch, diff --git a/sys/sys/vnode.h b/sys/sys/vnode.h index 4fd5a28..e0da44e 100644 --- a/sys/sys/vnode.h +++ b/sys/sys/vnode.h @@ -703,6 +703,9 @@ int vop_stdpathconf(struct vop_pathconf_args *); int vop_stdpoll(struct vop_poll_args *); int vop_stdvptocnp(struct vop_vptocnp_args *ap); int vop_stdvptofh(struct vop_vptofh_args *ap); +int vop_stdunpbind(struct vop_unpbind_args *ap); +int vop_stdunpconnect(struct vop_unpconnect_args *ap); +int vop_stdunpdetach(struct vop_unpdetach_args *ap); int vop_eopnotsupp(struct vop_generic_args *ap); int vop_ebadf(struct vop_generic_args *ap); int vop_einval(struct vop_generic_args *ap);