diff -Nru linux-2.4.20-pre10-mjc1/Documentation/filesystems/Locking linux-2.4.20-pre10-mjc2/Documentation/filesystems/Locking
--- linux-2.4.20-pre10-mjc1/Documentation/filesystems/Locking	2002-10-23 22:55:26.000000000 -0400
+++ linux-2.4.20-pre10-mjc2/Documentation/filesystems/Locking	2002-10-25 08:12:37.000000000 -0400
@@ -285,6 +285,8 @@
 
 ->fsync() has i_sem on inode.
 
+->fasync() has i_sem on inode.
+
 --------------------------- dquot_operations -------------------------------
 prototypes:
 	void (*initialize) (struct inode *, short);
diff -Nru linux-2.4.20-pre10-mjc1/fs/fcntl.c linux-2.4.20-pre10-mjc2/fs/fcntl.c
--- linux-2.4.20-pre10-mjc1/fs/fcntl.c	2002-10-23 22:55:43.000000000 -0400
+++ linux-2.4.20-pre10-mjc2/fs/fcntl.c	2002-10-25 08:12:37.000000000 -0400
@@ -213,32 +213,29 @@
 	if (!(arg & O_APPEND) && IS_APPEND(inode))
 		return -EPERM;
 
+	/*
+	 * alloc_kiovec() and ->fasync can sleep, so abuse the i_sem
+	 * to serialize against parallel setfl on the same filp,
+	 * to avoid races with ->f_flags and ->f_iobuf.
+	 */
+	down(&inode->i_sem);
 	/* Did FASYNC state change? */
 	if ((arg ^ filp->f_flags) & FASYNC) {
 		if (filp->f_op && filp->f_op->fasync) {
+			lock_kernel();
 			error = filp->f_op->fasync(fd, filp, (arg & FASYNC) != 0);
+			unlock_kernel();
 			if (error < 0)
-				return error;
+				goto out;
 		}
 	}
 
 	if (arg & O_DIRECT) {
-		/*
-		 * alloc_kiovec() can sleep and we are only serialized by
-		 * the big kernel lock here, so abuse the i_sem to serialize
-		 * this case too. We of course wouldn't need to go deep down
-		 * to the inode layer, we could stay at the file layer, but
-		 * we don't want to pay for the memory of a semaphore in each
-		 * file structure too and we use the inode semaphore that we just
-		 * pay for anyways.
-		 */
-		error = 0;
-		down(&inode->i_sem);
-		if (!filp->f_iobuf)
+		if (!filp->f_iobuf) {
 			error = alloc_kiovec(1, &filp->f_iobuf);
-		up(&inode->i_sem);
-		if (error < 0)
-			return error;
+			if (error < 0)
+				goto out;
+		}
 	}
 
 	/* required for strict SunOS emulation */
@@ -247,7 +244,10 @@
 		   arg |= O_NONBLOCK;
 
 	filp->f_flags = (arg & SETFL_MASK) | (filp->f_flags & ~SETFL_MASK);
-	return 0;
+	error = 0;
+ out:
+	up(&inode->i_sem);
+	return error;
 }
 
 static long do_fcntl(unsigned int fd, unsigned int cmd,
@@ -273,9 +273,7 @@
 			err = filp->f_flags;
 			break;
 		case F_SETFL:
-			lock_kernel();
 			err = setfl(fd, filp, arg);
-			unlock_kernel();
 			break;
 		case F_GETLK:
 			err = fcntl_getlk(fd, (struct flock *) arg);
diff -Nru linux-2.4.20-pre10-mjc1/fs/ioctl.c linux-2.4.20-pre10-mjc2/fs/ioctl.c
--- linux-2.4.20-pre10-mjc1/fs/ioctl.c	2002-10-23 22:55:41.000000000 -0400
+++ linux-2.4.20-pre10-mjc2/fs/ioctl.c	2002-10-25 08:12:37.000000000 -0400
@@ -81,10 +81,13 @@
 				filp->f_flags &= ~flag;
 			break;
 
-		case FIOASYNC:
+		case FIOASYNC: {
+			struct inode * inode;
 			if ((error = get_user(on, (int *)arg)) != 0)
 				break;
 			flag = on ? FASYNC : 0;
+			inode = filp->f_dentry->d_inode;
+			down(&inode->i_sem);
 
 			/* Did FASYNC state change ? */
 			if ((flag ^ filp->f_flags) & FASYNC) {
@@ -93,13 +96,16 @@
 				else error = -ENOTTY;
 			}
 			if (error != 0)
-				break;
+				goto fioasync_out;
 
 			if (on)
 				filp->f_flags |= FASYNC;
 			else
 				filp->f_flags &= ~FASYNC;
+		fioasync_out:
+			up(&inode->i_sem);
 			break;
+		}
 
 		case FIOQSIZE:
 			if (S_ISDIR(filp->f_dentry->d_inode->i_mode) ||
