changeset 3734:cc8896fe34a1

6502013 kernel heap corruptions have been seen during io stress test on domain0 6527579 port_dissociate() does not indicate whether fd was associated
author praks
date Wed, 28 Feb 2007 17:06:58 -0800
parents f1b41f5144cc
children 74d8d0dc0268
files usr/src/uts/common/fs/portfs/port_fd.c usr/src/uts/common/os/port_subr.c usr/src/uts/common/sys/port_impl.h
diffstat 3 files changed, 73 insertions(+), 42 deletions(-) [+]
line wrap: on
line diff
--- a/usr/src/uts/common/fs/portfs/port_fd.c	Wed Feb 28 16:55:11 2007 -0800
+++ b/usr/src/uts/common/fs/portfs/port_fd.c	Wed Feb 28 17:06:58 2007 -0800
@@ -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_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 @@
 	 * 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 @@
 		 */
 		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 @@
 	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_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 @@
 	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 @@
 	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 @@
 	delfd_port(fd, pfd);
 	releasef(fd);
 
-	/* remove polldat & port event structure */
-	port_remove_fd_object(pfd, pp, pcp);
-	mutex_exit(&pcp->pc_lock);
-	return (0);
-}
+	/*
+	 * 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 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);
+	/* remove polldat & port event structure */
+	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);
 
-	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 @@
 	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);
 	}
 }
 
--- a/usr/src/uts/common/os/port_subr.c	Wed Feb 28 16:55:11 2007 -0800
+++ b/usr/src/uts/common/os/port_subr.c	Wed Feb 28 17:06:58 2007 -0800
@@ -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 @@
 
 /*
  * 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 @@
 		}
 		/* cleanup merged port queue */
 		port_remove_event_doneq(pkevp, portq);
+		removed = 1;
 	}
 	port_unblock(portq);
 	mutex_exit(&portq->portq_mutex);
@@ -641,6 +644,7 @@
 
 	/* remove polldat struct */
 	port_pcache_remove_fd(pcp, pfd);
+	return (removed);
 }
 
 /*
@@ -663,7 +667,7 @@
 	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);
 }
 
--- a/usr/src/uts/common/sys/port_impl.h	Wed Feb 28 16:55:11 2007 -0800
+++ b/usr/src/uts/common/sys/port_impl.h	Wed Feb 28 17:06:58 2007 -0800
@@ -229,7 +229,7 @@
 
 /* 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 *);