--- odirect/fs/fcntl.c.~1~ Fri Jul 5 12:20:47 2002 +++ odirect/fs/fcntl.c Sat Jul 6 18:53:56 2002 @@ -213,32 +213,29 @@ static int setfl(int fd, struct file * f 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 @@ static int setfl(int fd, struct file * f 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 @@ static long do_fcntl(unsigned int fd, un 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);