summaryrefslogtreecommitdiff
path: root/usr
diff options
context:
space:
mode:
authorpraks <none@none>2007-02-28 17:06:58 -0800
committerpraks <none@none>2007-02-28 17:06:58 -0800
commit9f23d599d2e10f1fbd15dae924ba047538390387 (patch)
treeb74f668c4543688317bd1dc953f9153636fce0d2 /usr
parentfe70c9cf90dfc23d18485fb7b4b20a1175d53a8b (diff)
downloadillumos-gate-9f23d599d2e10f1fbd15dae924ba047538390387.tar.gz
6502013 kernel heap corruptions have been seen during io stress test on domain0
6527579 port_dissociate() does not indicate whether fd was associated
Diffstat (limited to 'usr')
-rw-r--r--usr/src/uts/common/fs/portfs/port_fd.c99
-rw-r--r--usr/src/uts/common/os/port_subr.c12
-rw-r--r--usr/src/uts/common/sys/port_impl.h2
3 files changed, 72 insertions, 41 deletions
diff --git a/usr/src/uts/common/fs/portfs/port_fd.c b/usr/src/uts/common/fs/portfs/port_fd.c
index 710b2d27d6..61c0c9df41 100644
--- a/usr/src/uts/common/fs/portfs/port_fd.c
+++ b/usr/src/uts/common/fs/portfs/port_fd.c
@@ -20,7 +20,7 @@
*/
/*
- * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -42,7 +42,6 @@
/* local functions */
static int port_fd_callback(void *, int *, pid_t, int, void *);
static int port_bind_pollhead(pollhead_t **, polldat_t *, short *);
-static void port_remove_fd_local(portfd_t *, port_fdcache_t *);
static void port_close_sourcefd(void *, int, pid_t, int);
static void port_cache_insert_fd(port_fdcache_t *, polldat_t *);
@@ -188,6 +187,7 @@ port_associate_fd(port_t *pp, int source, uintptr_t object, int events,
port_kevent_t *pkevp;
short revents;
int error = 0;
+ int active;
pcp = pp->port_queue.portq_pcp;
if (object > (uintptr_t)INT_MAX)
@@ -323,12 +323,7 @@ port_associate_fd(port_t *pp, int source, uintptr_t object, int events,
* call VOP_POLL() again (see port_bind_pollhead()).
*/
if (error) {
- /* dissociate the fd from the port */
- delfd_port(fd, pfd);
- port_remove_fd_local(pfd, pcp);
- releasef(fd);
- mutex_exit(&pcp->pc_lock);
- return (error);
+ goto errout;
}
if (php != NULL) {
@@ -340,11 +335,7 @@ port_associate_fd(port_t *pp, int source, uintptr_t object, int events,
*/
error = port_bind_pollhead(&php, pdp, &revents);
if (error) {
- delfd_port(fd, pfd);
- port_remove_fd_local(pfd, pcp);
- releasef(fd);
- mutex_exit(&pcp->pc_lock);
- return (error);
+ goto errout;
}
}
@@ -371,6 +362,32 @@ port_associate_fd(port_t *pp, int source, uintptr_t object, int events,
releasef(fd);
mutex_exit(&pcp->pc_lock);
return (error);
+
+errout:
+ delfd_port(fd, pfd);
+ /*
+ * If the portkev is not valid, then an event was
+ * delivered.
+ *
+ * If an event was delivered and got picked up, then
+ * we return error = 0 treating this as a successful
+ * port associate call. The thread which received
+ * the event gets control of the object.
+ */
+ active = 0;
+ mutex_enter(&pkevp->portkev_lock);
+ if (pkevp->portkev_flags & PORT_KEV_VALID) {
+ pkevp->portkev_flags &= ~PORT_KEV_VALID;
+ active = 1;
+ }
+ mutex_enter(&pkevp->portkev_lock);
+
+ if (!port_remove_fd_object(pfd, pp, pcp) && !active) {
+ error = 0;
+ }
+ releasef(fd);
+ mutex_exit(&pcp->pc_lock);
+ return (error);
}
/*
@@ -390,6 +407,8 @@ port_dissociate_fd(port_t *pp, uintptr_t object)
port_fdcache_t *pcp;
portfd_t *pfd;
file_t *fp;
+ int active;
+ port_kevent_t *pkevp;
if (object > (uintptr_t)INT_MAX)
return (EBADFD);
@@ -401,7 +420,7 @@ port_dissociate_fd(port_t *pp, uintptr_t object)
if (pcp->pc_hash == NULL) {
/* no file descriptor cache available */
mutex_exit(&pcp->pc_lock);
- return (0);
+ return (ENOENT);
}
if ((fp = getf(fd)) == NULL) {
mutex_exit(&pcp->pc_lock);
@@ -411,7 +430,7 @@ port_dissociate_fd(port_t *pp, uintptr_t object)
if (pfd == NULL) {
releasef(fd);
mutex_exit(&pcp->pc_lock);
- return (0);
+ return (ENOENT);
}
/* only association owner is allowed to remove the association */
if (curproc->p_pid != PFTOD(pfd)->pd_portev->portkev_pid) {
@@ -424,29 +443,37 @@ port_dissociate_fd(port_t *pp, uintptr_t object)
delfd_port(fd, pfd);
releasef(fd);
+ /*
+ * Deactivate the association. No events get posted after
+ * this.
+ */
+ pkevp = PFTOD(pfd)->pd_portev;
+ mutex_enter(&pkevp->portkev_lock);
+ if (pkevp->portkev_flags & PORT_KEV_VALID) {
+ pkevp->portkev_flags &= ~PORT_KEV_VALID;
+ active = 1;
+ } else {
+ active = 0;
+ }
+ mutex_exit(&pkevp->portkev_lock);
+
/* remove polldat & port event structure */
- port_remove_fd_object(pfd, pp, pcp);
+ if (port_remove_fd_object(pfd, pp, pcp)) {
+ /*
+ * An event was found and removed from the
+ * port done queue. This means the event has not yet
+ * been retrived. In this case we treat this as an active
+ * association.
+ */
+ ASSERT(active == 0);
+ active = 1;
+ }
mutex_exit(&pcp->pc_lock);
- return (0);
-}
-/*
- * Remove the fd from the event port cache.
- */
-static void
-port_remove_fd_local(portfd_t *pfd, port_fdcache_t *pcp)
-{
- polldat_t *pdp = PFTOD(pfd);
-
- ASSERT(MUTEX_HELD(&pcp->pc_lock));
- pdp->pd_fp = NULL;
- if (pdp->pd_php != NULL) {
- pollhead_delete(pdp->pd_php, pdp);
- pdp->pd_php = NULL;
- }
- port_free_event_local(pdp->pd_portev, 0);
- /* remove polldat struct */
- port_pcache_remove_fd(pcp, pfd);
+ /*
+ * Return ENOENT if there was no active association.
+ */
+ return ((active ? 0 : ENOENT));
}
/*
@@ -564,7 +591,7 @@ port_remove_portfd(polldat_t *pdp, port_fdcache_t *pcp)
if (fp != NULL) {
delfd_port(pdp->pd_fd, PDTOF(pdp));
releasef(pdp->pd_fd);
- port_remove_fd_object(PDTOF(pdp), pp, pcp);
+ (void) port_remove_fd_object(PDTOF(pdp), pp, pcp);
}
}
diff --git a/usr/src/uts/common/os/port_subr.c b/usr/src/uts/common/os/port_subr.c
index 03231cd0d8..11a701a78a 100644
--- a/usr/src/uts/common/os/port_subr.c
+++ b/usr/src/uts/common/os/port_subr.c
@@ -20,7 +20,7 @@
*/
/*
- * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -600,15 +600,17 @@ port_push_eventq(port_queue_t *portq)
/*
* The port_remove_fd_object() function frees all resources associated with
- * delivered portfd_t structure.
+ * delivered portfd_t structure. Returns 1 if the port_kevent was found
+ * and removed from the port queue.
*/
-void
+int
port_remove_fd_object(portfd_t *pfd, port_t *pp, port_fdcache_t *pcp)
{
port_queue_t *portq;
polldat_t *pdp = PFTOD(pfd);
port_kevent_t *pkevp;
int error;
+ int removed = 0;
ASSERT(MUTEX_HELD(&pcp->pc_lock));
if (pdp->pd_php != NULL) {
@@ -629,6 +631,7 @@ port_remove_fd_object(portfd_t *pfd, port_t *pp, port_fdcache_t *pcp)
}
/* cleanup merged port queue */
port_remove_event_doneq(pkevp, portq);
+ removed = 1;
}
port_unblock(portq);
mutex_exit(&portq->portq_mutex);
@@ -641,6 +644,7 @@ port_remove_fd_object(portfd_t *pfd, port_t *pp, port_fdcache_t *pcp)
/* remove polldat struct */
port_pcache_remove_fd(pcp, pfd);
+ return (removed);
}
/*
@@ -663,7 +667,7 @@ port_close_pfd(portfd_t *pfd)
pp = PFTOD(pfd)->pd_portev->portkev_port;
pcp = pp->port_queue.portq_pcp;
mutex_enter(&pcp->pc_lock);
- port_remove_fd_object(pfd, pp, pcp);
+ (void) port_remove_fd_object(pfd, pp, pcp);
mutex_exit(&pcp->pc_lock);
}
diff --git a/usr/src/uts/common/sys/port_impl.h b/usr/src/uts/common/sys/port_impl.h
index 58a1653b5f..3b12a20873 100644
--- a/usr/src/uts/common/sys/port_impl.h
+++ b/usr/src/uts/common/sys/port_impl.h
@@ -229,7 +229,7 @@ void port_unblock(port_queue_t *);
/* PORT_SOURCE_FD cache management */
void port_pcache_remove_fd(port_fdcache_t *, portfd_t *);
-void port_remove_fd_object(portfd_t *, struct port *, port_fdcache_t *);
+int port_remove_fd_object(portfd_t *, struct port *, port_fdcache_t *);
/* file close management */
extern void addfd_port(int, portfd_t *);