diff -urNX ../kernels/exclude linux-pre8/drivers/char/busmouse.c linux+lock/drivers/char/busmouse.c --- linux-pre8/drivers/char/busmouse.c Tue Apr 25 20:38:33 2000 +++ linux+lock/drivers/char/busmouse.c Fri Jun 2 11:22:57 2000 @@ -109,9 +109,7 @@ if (changed) { wake_up(&mse->wait); - - if (mse->fasyncptr) - kill_fasync(mse->fasyncptr, SIGIO, POLL_IN); + tty_send_fasync(&mse->fasyncptr); } } diff -urNX ../kernels/exclude linux-pre8/drivers/char/dn_keyb.c linux+lock/drivers/char/dn_keyb.c --- linux-pre8/drivers/char/dn_keyb.c Mon Nov 8 14:03:20 1999 +++ linux+lock/drivers/char/dn_keyb.c Fri Jun 2 11:22:57 2000 @@ -353,21 +353,20 @@ if(mouse_update_allowed) { mouse_ready=1; mouse_buttons=(mouse_packet[0] >> 4) & 0x7; - mouse_dx+=mouse_packet[1] == 0xff ? 0 : (signed char)mouse_packet[1]; - mouse_dy+=mouse_packet[2] == 0xff ? 0 : (signed char)mouse_packet[2]; + if (mouse_packet[1] != 0xff) + mouse_dx += (signed char)mouse_packet[1]; + if (mouse_packet[2] != 0xff) + mouse_dy += (signed char)mouse_packet[2]; wake_up_interruptible(&mouse_wait); if (mouse_dx < -2048) - mouse_dx = -2048; - else - if (mouse_dx > 2048) - mouse_dx = 2048; - if (mouse_dy < -2048) - mouse_dy = -2048; - else - if (mouse_dy > 2048) - mouse_dy = 2048; - if (mouse_fasyncptr) - kill_fasync(mouse_fasyncptr, SIGIO, POLL_IN); + mouse_dx = -2048; + else if (mouse_dx > 2048) + mouse_dx = 2048; + if (mouse_dy < -2048) + mouse_dy = -2048; + else if (mouse_dy > 2048) + mouse_dy = 2048; + tty_send_fasync(&mouse_fasyncptr); } mouse_byte_count=0; /* printk("mouse: %d, %d, %x\n",mouse_x,mouse_y,buttons); */ diff -urNX ../kernels/exclude linux-pre8/drivers/char/drm/fops.c linux+lock/drivers/char/drm/fops.c --- linux-pre8/drivers/char/drm/fops.c Tue Mar 14 12:27:31 2000 +++ linux+lock/drivers/char/drm/fops.c Fri Jun 2 11:22:57 2000 @@ -216,7 +216,7 @@ if (dev->buf_async) kill_fasync(dev->buf_async, SIGIO); #else /* Parameter added in 2.3.21 */ - if (dev->buf_async) kill_fasync(dev->buf_async, SIGIO, POLL_IN); + tty_send_fasync(&dev->buf_async); #endif DRM_DEBUG("waking\n"); wake_up_interruptible(&dev->buf_readers); diff -urNX ../kernels/exclude linux-pre8/drivers/char/n_hdlc.c linux+lock/drivers/char/n_hdlc.c --- linux-pre8/drivers/char/n_hdlc.c Fri Apr 21 16:17:03 2000 +++ linux+lock/drivers/char/n_hdlc.c Fri Jun 2 11:22:57 2000 @@ -659,12 +659,7 @@ /* wake up any blocked reads and perform async signalling */ wake_up_interruptible (&n_hdlc->read_wait); - if (n_hdlc->tty->fasync != NULL) -#if LINUX_VERSION_CODE < VERSION(2,3,0) - kill_fasync (n_hdlc->tty->fasync, SIGIO); -#else - kill_fasync (n_hdlc->tty->fasync, SIGIO, POLL_IN); -#endif + tty_send_fasync(&n_hdlc->tty->fasync); } /* end of n_hdlc_tty_receive() */ /* n_hdlc_tty_read() diff -urNX ../kernels/exclude linux-pre8/drivers/char/n_tty.c linux+lock/drivers/char/n_tty.c --- linux-pre8/drivers/char/n_tty.c Fri Apr 21 18:21:18 2000 +++ linux+lock/drivers/char/n_tty.c Fri Jun 2 11:22:57 2000 @@ -630,8 +630,7 @@ put_tty_queue(c, tty); tty->canon_head = tty->read_head; tty->canon_data++; - if (tty->fasync) - kill_fasync(tty->fasync, SIGIO, POLL_IN); + tty_send_fasync(&tty->fasync); if (waitqueue_active(&tty->read_wait)) wake_up_interruptible(&tty->read_wait); return; @@ -735,8 +734,7 @@ } if (!tty->icanon && (tty->read_cnt >= tty->minimum_to_wake)) { - if (tty->fasync) - kill_fasync(tty->fasync, SIGIO, POLL_IN); + tty_send_fasync(&tty->fasync); if (waitqueue_active(&tty->read_wait)) wake_up_interruptible(&tty->read_wait); } diff -urNX ../kernels/exclude linux-pre8/drivers/char/pc110pad.c linux+lock/drivers/char/pc110pad.c --- linux-pre8/drivers/char/pc110pad.c Wed May 3 04:45:18 2000 +++ linux+lock/drivers/char/pc110pad.c Fri Jun 2 11:22:57 2000 @@ -83,8 +83,7 @@ static void wake_readers(void) { wake_up_interruptible(&queue); - if(asyncptr) - kill_fasync(asyncptr, SIGIO, POLL_IN); + tty_send_fasync(&asyncptr); } diff -urNX ../kernels/exclude linux-pre8/drivers/char/pc_keyb.c linux+lock/drivers/char/pc_keyb.c --- linux-pre8/drivers/char/pc_keyb.c Fri Apr 21 16:12:24 2000 +++ linux+lock/drivers/char/pc_keyb.c Fri Jun 2 11:22:57 2000 @@ -415,9 +415,8 @@ head = (head + 1) & (AUX_BUF_SIZE-1); if (head != queue->tail) { queue->head = head; - if (queue->fasync) - kill_fasync(queue->fasync, SIGIO, POLL_IN); wake_up_interruptible(&queue->proc_list); + tty_send_fasync(&queue->fasync); } } #endif diff -urNX ../kernels/exclude linux-pre8/drivers/char/qpmouse.c linux+lock/drivers/char/qpmouse.c --- linux-pre8/drivers/char/qpmouse.c Wed Feb 9 14:42:35 2000 +++ linux+lock/drivers/char/qpmouse.c Fri Jun 2 11:22:57 2000 @@ -133,8 +133,7 @@ head &= QP_BUF_SIZE-1; } queue->head = head; - if (queue->fasync) - kill_fasync(queue->fasync, SIGIO, POLL_IN); + tty_send_fasync(&queue->fasync); wake_up_interruptible(&queue->proc_list); } diff -urNX ../kernels/exclude linux-pre8/drivers/char/rtc.c linux+lock/drivers/char/rtc.c --- linux-pre8/drivers/char/rtc.c Tue May 2 15:44:05 2000 +++ linux+lock/drivers/char/rtc.c Fri Jun 2 11:22:57 2000 @@ -178,9 +178,7 @@ /* Now do the rest of the actions */ wake_up_interruptible(&rtc_wait); - - if (rtc_async_queue) - kill_fasync (rtc_async_queue, SIGIO, POLL_IN); + tty_send_fasync(&rtc_async_queue); } #endif @@ -775,9 +773,7 @@ /* Now we have new data */ wake_up_interruptible(&rtc_wait); - - if (rtc_async_queue) - kill_fasync (rtc_async_queue, SIGIO, POLL_IN); + tty_kill_fasync(rtc_async_queue); } #endif diff -urNX ../kernels/exclude linux-pre8/drivers/char/tty_io.c linux+lock/drivers/char/tty_io.c --- linux-pre8/drivers/char/tty_io.c Tue May 9 19:18:28 2000 +++ linux+lock/drivers/char/tty_io.c Fri Jun 2 11:22:57 2000 @@ -1427,49 +1427,6 @@ return 0; } -/* - * fasync_helper() is used by some character device drivers (mainly mice) - * to set up the fasync queue. It returns negative on error, 0 if it did - * no changes and positive if it added/deleted the entry. - */ -int fasync_helper(int fd, struct file * filp, int on, struct fasync_struct **fapp) -{ - struct fasync_struct *fa, **fp; - unsigned long flags; - - for (fp = fapp; (fa = *fp) != NULL; fp = &fa->fa_next) { - if (fa->fa_file == filp) - break; - } - - if (on) { - if (fa) { - fa->fa_fd = fd; - return 0; - } - fa = (struct fasync_struct *)kmalloc(sizeof(struct fasync_struct), GFP_KERNEL); - if (!fa) - return -ENOMEM; - fa->magic = FASYNC_MAGIC; - fa->fa_file = filp; - fa->fa_fd = fd; - save_flags(flags); - cli(); - fa->fa_next = *fapp; - *fapp = fa; - restore_flags(flags); - return 1; - } - if (!fa) - return 0; - save_flags(flags); - cli(); - *fp = fa->fa_next; - restore_flags(flags); - kfree(fa); - return 1; -} - static int tty_fasync(int fd, struct file * filp, int on) { struct tty_struct * tty; diff -urNX ../kernels/exclude linux-pre8/drivers/i2o/i2o_config.c linux+lock/drivers/i2o/i2o_config.c --- linux-pre8/drivers/i2o/i2o_config.c Wed May 10 19:56:42 2000 +++ linux+lock/drivers/i2o/i2o_config.c Fri Jun 2 11:22:57 2000 @@ -161,8 +161,7 @@ // printk(KERN_INFO "File %p w/id %d has %d events\n", // inf->fp, inf->q_id, inf->q_len); - if(inf->fasync) - kill_fasync(inf->fasync, SIGIO, POLL_IN); + tty_send_fasync(&inf->fasync); } return; Binary files linux-pre8/drivers/pci/gen-devlist and linux+lock/drivers/pci/gen-devlist differ diff -urNX ../kernels/exclude linux-pre8/drivers/sbus/char/pcikbd.c linux+lock/drivers/sbus/char/pcikbd.c --- linux-pre8/drivers/sbus/char/pcikbd.c Wed May 3 04:47:57 2000 +++ linux+lock/drivers/sbus/char/pcikbd.c Fri Jun 2 11:22:57 2000 @@ -737,8 +737,7 @@ spin_unlock_irqrestore(&pcikbd_lock, flags); - if (queue->fasync) - kill_fasync(queue->fasync, SIGIO, POLL_IN); + tty_send_fasync(&queue->fasync); wake_up_interruptible(&queue->proc_list); } diff -urNX ../kernels/exclude linux-pre8/drivers/sbus/char/sunkbd.c linux+lock/drivers/sbus/char/sunkbd.c --- linux-pre8/drivers/sbus/char/sunkbd.c Wed May 3 04:47:57 2000 +++ linux+lock/drivers/sbus/char/sunkbd.c Fri Jun 2 11:22:57 2000 @@ -1310,8 +1310,7 @@ } spin_unlock_irqrestore(&kbd_queue_lock, flags); - if (kb_fasync) - kill_fasync (kb_fasync, SIGIO, POLL_IN); + tty_send_fasync(&kb_fasync); wake_up_interruptible (&kbd_wait); } diff -urNX ../kernels/exclude linux-pre8/drivers/sbus/char/sunmouse.c linux+lock/drivers/sbus/char/sunmouse.c --- linux-pre8/drivers/sbus/char/sunmouse.c Wed May 3 04:47:57 2000 +++ linux+lock/drivers/sbus/char/sunmouse.c Fri Jun 2 11:22:57 2000 @@ -147,8 +147,7 @@ spin_unlock_irqrestore(&sunmouse.lock, flags); - if (sunmouse.fasync) - kill_fasync (sunmouse.fasync, SIGIO, POLL_IN); + tty_send_fasync(&sunmouse.fasync); wake_up_interruptible (&sunmouse.proc_list); } @@ -382,8 +381,7 @@ /* We just completed a transaction, wake up whoever is awaiting * this event. */ - if (sunmouse.fasync) - kill_fasync (sunmouse.fasync, SIGIO, POLL_IN); + tty_send_fasync(&sunmouse.fasync); wake_up_interruptible(&sunmouse.proc_list); } return; diff -urNX ../kernels/exclude linux-pre8/drivers/scsi/sg.c linux+lock/drivers/scsi/sg.c --- linux-pre8/drivers/scsi/sg.c Mon Mar 27 13:48:23 2000 +++ linux+lock/drivers/scsi/sg.c Fri Jun 2 11:22:57 2000 @@ -1052,8 +1052,7 @@ if (sfp && srp) { /* Now wake up any sg_read() that is waiting for this packet. */ wake_up_interruptible(&sfp->read_wait); - if (sfp->async_qp) - kill_fasync(sfp->async_qp, SIGPOLL, POLL_IN); + kill_fasync(&sfp->async_qp, SIGPOLL, POLL_IN); } } diff -urNX ../kernels/exclude linux-pre8/drivers/sgi/char/shmiq.c linux+lock/drivers/sgi/char/shmiq.c --- linux-pre8/drivers/sgi/char/shmiq.c Fri Feb 25 01:51:08 2000 +++ linux+lock/drivers/sgi/char/shmiq.c Fri Jun 2 11:22:57 2000 @@ -118,8 +118,7 @@ e->data.device, e->data.which, e->data.type, e->data.flags); s->tail = tail_next; shmiqs [device].tail = tail_next; - if (shmiqs [device].fasync) - kill_fasync (shmiqs [device].fasync, SIGIO, POLL_IN); + tty_send_fasync(&shmiqs[device].fasync); wake_up_interruptible (&shmiqs [device].proc_list); } diff -urNX ../kernels/exclude linux-pre8/drivers/telephony/ixj.c linux+lock/drivers/telephony/ixj.c --- linux-pre8/drivers/telephony/ixj.c Sun Mar 12 22:18:55 2000 +++ linux+lock/drivers/telephony/ixj.c Fri Jun 2 11:22:57 2000 @@ -446,8 +446,7 @@ { j->m_hook = 0; j->ex.bits.hookstate = 1; - if (j->async_queue) - kill_fasync(j->async_queue, SIGIO, POLL_IN); // Send apps notice of change + tty_send_fasync(&j->async_queue); // Send apps notice of change } goto timer_end; } @@ -536,8 +535,7 @@ j->proc_load = j->ssr.high << 8 | j->ssr.low; if (!j->m_hook) { j->m_hook = j->ex.bits.hookstate = 1; - if (j->async_queue) - kill_fasync(j->async_queue, SIGIO, POLL_IN); // Send apps notice of change + tty_send_fasync(&j->async_queue); // Send apps notice of change } } else { if (j->dsp.low == 0x21 && @@ -552,8 +550,7 @@ if (j->m_hook) { j->m_hook = 0; j->ex.bits.hookstate = 1; - if (j->async_queue) - kill_fasync(j->async_queue, SIGIO, POLL_IN); // Send apps notice of change + tty_send_fasync(&j->async_queue); // Send apps notice of change } } } @@ -642,8 +639,7 @@ } if (j->ex.bytes) { wake_up_interruptible(&j->poll_q); // Wake any blocked selects - if (j->async_queue) - kill_fasync(j->async_queue, SIGIO, POLL_IN); // Send apps notice of change + tty_send_fasync(&j->async_queue); // Send apps notice of change } } else { break; @@ -917,8 +913,7 @@ j->r_hook = fOffHook; if (j->port != PORT_POTS) { j->ex.bits.hookstate = 1; - if (j->async_queue) - kill_fasync(j->async_queue, SIGIO, POLL_IN); // Send apps notice of change + tty_send_fasync(&j->async_queue); // Send apps notice of change } } @@ -1471,8 +1466,7 @@ wake_up_interruptible(&j->poll_q); // Wake any blocked selects - if (j->async_queue) - kill_fasync(j->async_queue, SIGIO, POLL_IN); // Send apps notice of frame + tty_send_fasync(&j->async_queue); // Send apps notice of frame } } @@ -1557,8 +1551,7 @@ wake_up_interruptible(&j->poll_q); // Wake any blocked selects - if (j->async_queue) - kill_fasync(j->async_queue, SIGIO, POLL_IN); // Send apps notice of empty buffer + tty_send_fasync(&j->async_queue); // Send apps notice of empty buffer #ifdef PERFMON_STATS ++j->frameswritten; #endif diff -urNX ../kernels/exclude linux-pre8/drivers/usb/evdev.c linux+lock/drivers/usb/evdev.c --- linux-pre8/drivers/usb/evdev.c Fri Apr 21 16:22:23 2000 +++ linux+lock/drivers/usb/evdev.c Fri Jun 2 11:22:57 2000 @@ -72,8 +72,7 @@ list->buffer[list->head].value = value; list->head = (list->head + 1) & (EVDEV_BUFFER_SIZE - 1); - if (list->fasync) - kill_fasync(list->fasync, SIGIO, POLL_IN); + tty_send_fasync(&list->fasync); list = list->next; } diff -urNX ../kernels/exclude linux-pre8/drivers/usb/joydev.c linux+lock/drivers/usb/joydev.c --- linux-pre8/drivers/usb/joydev.c Fri Apr 21 16:22:42 2000 +++ linux+lock/drivers/usb/joydev.c Fri Jun 2 11:22:57 2000 @@ -139,8 +139,7 @@ if (list->tail == (list->head = (list->head + 1) & (JOYDEV_BUFFER_SIZE - 1))) list->startup = 0; - if (list->fasync) - kill_fasync(list->fasync, SIGIO, POLL_IN); + tty_send_fasync(&list->fasync); list = list->next; } diff -urNX ../kernels/exclude linux-pre8/drivers/usb/mousedev.c linux+lock/drivers/usb/mousedev.c --- linux-pre8/drivers/usb/mousedev.c Tue May 2 18:01:24 2000 +++ linux+lock/drivers/usb/mousedev.c Fri Jun 2 11:22:57 2000 @@ -138,8 +138,7 @@ list->ready = 1; - if (list->fasync) - kill_fasync(list->fasync, SIGIO, POLL_IN); + tty_send_fasync(&list->fasync); list = list->next; } @@ -311,8 +310,7 @@ list->buffer = list->bufsiz; } - if (list->fasync) - kill_fasync(list->fasync, SIGIO, POLL_IN); + tty_send_fasync(&list->fasync); wake_up_interruptible(&list->mousedev->wait); Binary files linux-pre8/fs/.locks.c.swp and linux+lock/fs/.locks.c.swp differ diff -urNX ../kernels/exclude linux-pre8/fs/adfs/dir.c linux+lock/fs/adfs/dir.c --- linux-pre8/fs/adfs/dir.c Sun Apr 2 18:53:28 2000 +++ linux+lock/fs/adfs/dir.c Fri Jun 2 11:22:57 2000 @@ -197,6 +197,7 @@ read: generic_read_dir, readdir: adfs_readdir, fsync: file_fsync, + fasync: file_fasync, }; static int diff -urNX ../kernels/exclude linux-pre8/fs/affs/dir.c linux+lock/fs/affs/dir.c --- linux-pre8/fs/affs/dir.c Sat Feb 26 23:33:42 2000 +++ linux+lock/fs/affs/dir.c Fri Jun 2 11:22:57 2000 @@ -30,6 +30,7 @@ read: generic_read_dir, readdir: affs_readdir, fsync: file_fsync, + fasync: file_fasync, }; /* diff -urNX ../kernels/exclude linux-pre8/fs/autofs/dir.c linux+lock/fs/autofs/dir.c --- linux-pre8/fs/autofs/dir.c Sat Feb 26 23:33:04 2000 +++ linux+lock/fs/autofs/dir.c Fri Jun 2 11:22:57 2000 @@ -45,6 +45,7 @@ struct file_operations autofs_dir_operations = { read: generic_read_dir, readdir: autofs_dir_readdir, + fasync: file_fasync, }; struct inode_operations autofs_dir_inode_operations = { diff -urNX ../kernels/exclude linux-pre8/fs/autofs/root.c linux+lock/fs/autofs/root.c --- linux-pre8/fs/autofs/root.c Tue May 2 15:36:09 2000 +++ linux+lock/fs/autofs/root.c Fri Jun 2 11:22:58 2000 @@ -27,6 +27,7 @@ read: generic_read_dir, readdir: autofs_root_readdir, ioctl: autofs_root_ioctl, + fasync: file_fasync, }; struct inode_operations autofs_root_inode_operations = { diff -urNX ../kernels/exclude linux-pre8/fs/autofs4/root.c linux+lock/fs/autofs4/root.c --- linux-pre8/fs/autofs4/root.c Mon May 8 14:17:47 2000 +++ linux+lock/fs/autofs4/root.c Fri Jun 2 11:22:58 2000 @@ -29,11 +29,13 @@ read: generic_read_dir, readdir: autofs4_dir_readdir, ioctl: autofs4_root_ioctl, + fasync: file_fasync, }; struct file_operations autofs4_dir_operations = { read: generic_read_dir, readdir: autofs4_dir_readdir, + fasync: file_fasync, }; struct inode_operations autofs4_root_inode_operations = { diff -urNX ../kernels/exclude linux-pre8/fs/bfs/dir.c linux+lock/fs/bfs/dir.c --- linux-pre8/fs/bfs/dir.c Fri Mar 10 13:48:53 2000 +++ linux+lock/fs/bfs/dir.c Fri Jun 2 11:22:58 2000 @@ -74,6 +74,7 @@ read: generic_read_dir, readdir: bfs_readdir, fsync: file_fsync, + fasync: file_fasync, }; extern void dump_imap(const char *, struct super_block *); diff -urNX ../kernels/exclude linux-pre8/fs/coda/dir.c linux+lock/fs/coda/dir.c --- linux-pre8/fs/coda/dir.c Thu Mar 23 01:15:57 2000 +++ linux+lock/fs/coda/dir.c Fri Jun 2 11:22:58 2000 @@ -83,6 +83,7 @@ open: coda_open, release: coda_release, fsync: coda_fsync, + fasync: file_fasync, }; diff -urNX ../kernels/exclude linux-pre8/fs/cramfs/inode.c linux+lock/fs/cramfs/inode.c --- linux-pre8/fs/cramfs/inode.c Mon May 8 17:23:03 2000 +++ linux+lock/fs/cramfs/inode.c Fri Jun 2 11:22:58 2000 @@ -348,6 +348,7 @@ static struct file_operations cramfs_directory_operations = { read: generic_read_dir, readdir: cramfs_readdir, + fasync: file_fasync, }; static struct inode_operations cramfs_dir_inode_operations = { diff -urNX ../kernels/exclude linux-pre8/fs/devfs/base.c linux+lock/fs/devfs/base.c --- linux-pre8/fs/devfs/base.c Tue May 9 01:00:41 2000 +++ linux+lock/fs/devfs/base.c Fri Jun 2 11:22:58 2000 @@ -724,6 +724,7 @@ read: devfsd_read, ioctl: devfsd_ioctl, release: devfsd_close, + fasync: file_fasync, }; @@ -2546,6 +2547,7 @@ read: devfs_read, readdir: devfs_readdir, open: devfs_open, + fasync: file_fasync, }; diff -urNX ../kernels/exclude linux-pre8/fs/devpts/root.c linux+lock/fs/devpts/root.c --- linux-pre8/fs/devpts/root.c Mon May 8 18:56:08 2000 +++ linux+lock/fs/devpts/root.c Fri Jun 2 11:22:58 2000 @@ -23,6 +23,7 @@ struct file_operations devpts_root_operations = { read: generic_read_dir, readdir: devpts_root_readdir, + fasync: file_fasync, }; struct inode_operations devpts_root_inode_operations = { diff -urNX ../kernels/exclude linux-pre8/fs/efs/dir.c linux+lock/fs/efs/dir.c --- linux-pre8/fs/efs/dir.c Sat Feb 26 23:33:05 2000 +++ linux+lock/fs/efs/dir.c Fri Jun 2 11:22:58 2000 @@ -11,6 +11,7 @@ struct file_operations efs_dir_operations = { read: generic_read_dir, readdir: efs_readdir, + fasync: file_fasync, }; struct inode_operations efs_dir_inode_operations = { diff -urNX ../kernels/exclude linux-pre8/fs/ext2/dir.c linux+lock/fs/ext2/dir.c --- linux-pre8/fs/ext2/dir.c Fri Mar 17 23:49:19 2000 +++ linux+lock/fs/ext2/dir.c Fri Jun 2 11:22:58 2000 @@ -27,6 +27,7 @@ readdir: ext2_readdir, ioctl: ext2_ioctl, fsync: ext2_sync_file, + fasync: file_fasync, }; int ext2_check_dir_entry (const char * function, struct inode * dir, diff -urNX ../kernels/exclude linux-pre8/fs/fat/dir.c linux+lock/fs/fat/dir.c --- linux-pre8/fs/fat/dir.c Sat Feb 26 23:33:06 2000 +++ linux+lock/fs/fat/dir.c Fri Jun 2 11:22:58 2000 @@ -39,6 +39,7 @@ readdir: fat_readdir, ioctl: fat_dir_ioctl, fsync: file_fsync, + fasync: file__fasync, }; /* diff -urNX ../kernels/exclude linux-pre8/fs/fcntl.c linux+lock/fs/fcntl.c --- linux-pre8/fs/fcntl.c Sat Feb 26 23:46:44 2000 +++ linux+lock/fs/fcntl.c Fri Jun 2 16:02:21 2000 @@ -4,8 +4,10 @@ * Copyright (C) 1991, 1992 Linus Torvalds */ +#include #include #include +#include #include #include @@ -13,6 +15,8 @@ #include extern int sock_fcntl (struct file *, unsigned int cmd, unsigned long arg); +extern int fcntl_setlease(unsigned int fd, long arg); +extern int fcntl_getlease(unsigned int fd); /* * locate_fd finds a free file descriptor in the open_fds fdset, @@ -181,11 +185,9 @@ filp = fget(fd); if (!filp) goto out; - err = 0; - lock_kernel(); + err = -EINVAL; switch (cmd) { case F_DUPFD: - err = -EINVAL; if (arg < NR_OPEN) { get_file(filp); err = dupfd(filp, arg); @@ -195,6 +197,7 @@ err = FD_ISSET(fd, current->files->close_on_exec); break; case F_SETFD: + err = 0; if (arg&1) FD_SET(fd, current->files->close_on_exec); else @@ -204,14 +207,14 @@ 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); break; case F_SETLK: - err = fcntl_setlk(fd, cmd, (struct flock *) arg); - break; case F_SETLKW: err = fcntl_setlk(fd, cmd, (struct flock *) arg); break; @@ -226,11 +229,14 @@ err = filp->f_owner.pid; break; case F_SETOWN: + lock_kernel(); filp->f_owner.pid = arg; filp->f_owner.uid = current->uid; filp->f_owner.euid = current->euid; + err = 0; if (S_ISSOCK (filp->f_dentry->d_inode->i_mode)) err = sock_fcntl (filp, F_SETOWN, arg); + unlock_kernel(); break; case F_GETSIG: err = filp->f_owner.signum; @@ -238,21 +244,19 @@ case F_SETSIG: /* arg == 0 restores default behaviour. */ if (arg < 0 || arg > _NSIG) { - err = -EINVAL; break; } err = 0; filp->f_owner.signum = arg; break; - default: - /* sockets need a few special fcntls. */ - err = -EINVAL; - if (S_ISSOCK (filp->f_dentry->d_inode->i_mode)) - err = sock_fcntl (filp, cmd, arg); + case F_GETLEASE: + err = fcntl_getlease(fd); + break; + case F_SETLEASE: + err = fcntl_setlease(fd, arg); break; } fput(filp); - unlock_kernel(); out: return err; } @@ -295,6 +299,7 @@ else si.si_band = band_table[reason]; si.si_fd = fa->fa_fd; +printk("si_fd = %d\n", si.si_fd); if (!send_sig_info(fown->signum, &si, p)) break; /* fall-through: fall back on the old plain SIGIO signal */ @@ -326,7 +331,7 @@ read_unlock(&tasklist_lock); } -void kill_fasync(struct fasync_struct *fa, int sig, int band) +void __kill_fasync(struct fasync_struct *fa, int sig, int band) { while (fa) { struct fown_struct * fown; @@ -343,4 +348,98 @@ send_sigio(fown, fa, band); fa = fa->fa_next; } +} + +static rwlock_t fasync_lock = RW_LOCK_UNLOCKED; +static kmem_cache_t *fasync_cache; + +void kill_fasync(struct fasync_struct **fa, int sig, int band) +{ + read_lock(&fasync_lock); + __kill_fasync(*fa, sig, band); + read_unlock(&fasync_lock); +} + +static int fasync_on(int fd, struct file *filp, struct fasync_struct **prev) +{ + struct fasync_struct *fa, *fna = NULL; + + fna = kmem_cache_alloc(fasync_cache, SLAB_KERNEL); + /* kmalloc(sizeof(struct fasync_struct), GFP_KERNEL); */ + if (fna == NULL) + return -ENOMEM; + + for (fa = *prev; fa != NULL; prev = &fa->fa_next, fa = *prev) + if (fa->fa_file == filp) + break; + + if (fa != NULL) { + fa->fa_fd = fd; + kmem_cache_free(fasync_cache, fna); + /* kfree_s(fna,sizeof(struct fasync_struct)); */ + goto out; + } else { + fna->fa_file = filp; + fna->fa_fd = fd; + fna->magic = FASYNC_MAGIC; + fna->fa_next = *prev; + *prev = fna; + } + +out: + return 0; +} + +static int fasync_off(struct file *filp, struct fasync_struct **prev) +{ + struct fasync_struct *fa; + + for (fa = *prev; fa != NULL; prev = &fa->fa_next, fa = *prev) + if (fa->fa_file == filp) + break; + + if (fa != NULL) { + *prev = fa->fa_next; + kmem_cache_free(fasync_cache, fa); + /* kfree_s(fa, sizeof(struct fasync_struct)); */ + } + + return 0; +} + +int file_fasync(int fd, struct file *filp, int on) +{ + int error; + struct fasync_struct **fa; + + write_lock_irq(&fasync_lock); + fa = &filp->f_dentry->d_inode->i_fasync; + error = on ? fasync_on(fd, filp, fa) : fasync_off(filp, fa); + write_unlock_irq(&fasync_lock); + + return error; +} + +/* + * fasync_helper() is used by some character device drivers (mainly mice) + * to set up the fasync queue. It returns negative on error, 0 if it did + * no changes XXX: and positive if it added/deleted the entry. + */ +int fasync_helper(int fd, struct file * filp, int on, struct fasync_struct **fa) +{ + int error; + + write_lock_irq(&fasync_lock); + error = on ? fasync_on(fd, filp, fa) : fasync_off(filp, fa); + write_unlock_irq(&fasync_lock); + + return error; +} + +void __init fasync_init(void) +{ + fasync_cache = kmem_cache_create("fasync cache", + sizeof(struct fasync_struct), 0, 0, NULL, NULL); + if (!fasync_cache) + panic("cannot create fasync slab cache"); } diff -urNX ../kernels/exclude linux-pre8/fs/hfs/dir_cap.c linux+lock/fs/hfs/dir_cap.c --- linux-pre8/fs/hfs/dir_cap.c Sat Feb 26 23:33:42 2000 +++ linux+lock/fs/hfs/dir_cap.c Fri Jun 2 11:22:58 2000 @@ -61,6 +61,7 @@ read: generic_read_dir, readdir: cap_readdir, fsync: file_fsync, + fasync: file_fasync, }; struct inode_operations hfs_cap_ndir_inode_operations = { diff -urNX ../kernels/exclude linux-pre8/fs/hfs/dir_dbl.c linux+lock/fs/hfs/dir_dbl.c --- linux-pre8/fs/hfs/dir_dbl.c Sat Feb 26 23:33:42 2000 +++ linux+lock/fs/hfs/dir_dbl.c Fri Jun 2 11:22:58 2000 @@ -60,6 +60,7 @@ read: generic_read_dir, readdir: dbl_readdir, fsync: file_fsync, + fasync: file_fasync, }; struct inode_operations hfs_dbl_dir_inode_operations = { diff -urNX ../kernels/exclude linux-pre8/fs/hfs/dir_nat.c linux+lock/fs/hfs/dir_nat.c --- linux-pre8/fs/hfs/dir_nat.c Sat Feb 26 23:33:42 2000 +++ linux+lock/fs/hfs/dir_nat.c Fri Jun 2 11:22:58 2000 @@ -66,6 +66,7 @@ read: generic_read_dir, readdir: nat_readdir, fsync: file_fsync, + fasync: file_fasync, }; struct inode_operations hfs_nat_ndir_inode_operations = { diff -urNX ../kernels/exclude linux-pre8/fs/hpfs/inode.c linux+lock/fs/hpfs/inode.c --- linux-pre8/fs/hpfs/inode.c Mon Mar 13 15:27:51 2000 +++ linux+lock/fs/hpfs/inode.c Fri Jun 2 11:22:58 2000 @@ -32,6 +32,7 @@ open: hpfs_open, release: hpfs_dir_release, fsync: hpfs_file_fsync, + fasync: file_fasync, }; static struct inode_operations hpfs_dir_iops = diff -urNX ../kernels/exclude linux-pre8/fs/isofs/dir.c linux+lock/fs/isofs/dir.c --- linux-pre8/fs/isofs/dir.c Tue May 2 17:26:03 2000 +++ linux+lock/fs/isofs/dir.c Fri Jun 2 11:22:58 2000 @@ -30,6 +30,7 @@ { read: generic_read_dir, readdir: isofs_readdir, + fasync: file_fasync, }; /* diff -urNX ../kernels/exclude linux-pre8/fs/lockd/clntlock.c linux+lock/fs/lockd/clntlock.c --- linux-pre8/fs/lockd/clntlock.c Sun Apr 2 18:31:32 2000 +++ linux+lock/fs/lockd/clntlock.c Fri Jun 2 11:22:58 2000 @@ -157,33 +157,38 @@ } } +/* lockd is way too incestuous with fs/locks.c */ + static int reclaimer(void *ptr) { struct nlm_host *host = (struct nlm_host *) ptr; struct nlm_wait *block; - struct file_lock *fl; - struct inode *inode; + struct list_head *tmp; /* This one ensures that our parent doesn't terminate while the * reclaim is in progress */ lock_kernel(); lockd_up(); + down(&file_lock_sem); /* First, reclaim all locks that have been granted previously. */ - do { - for (fl = file_lock_table; fl; fl = fl->fl_nextlink) { - inode = fl->fl_file->f_dentry->d_inode; - if (inode->i_sb->s_magic == NFS_SUPER_MAGIC - && nlm_cmp_addr(NFS_ADDR(inode), &host->h_addr) - && fl->fl_u.nfs_fl.state != host->h_state - && (fl->fl_u.nfs_fl.flags & NFS_LCK_GRANTED)) { - fl->fl_u.nfs_fl.flags &= ~ NFS_LCK_GRANTED; - nlmclnt_reclaim(host, fl); - break; - } +restart: + tmp = file_lock_list.next; + while (tmp != &file_lock_list) { + struct file_lock *fl = list_entry(tmp, struct file_lock, fl_link); + struct inode *inode = fl->fl_file->f_dentry->d_inode; + if (inode->i_sb->s_magic == NFS_SUPER_MAGIC && + nlm_cmp_addr(NFS_ADDR(inode), &host->h_addr) && + fl->fl_u.nfs_fl.state != host->h_state && + (fl->fl_u.nfs_fl.flags & NFS_LCK_GRANTED)) { + fl->fl_u.nfs_fl.flags &= ~ NFS_LCK_GRANTED; + nlmclnt_reclaim(host, fl); + goto restart; } - } while (fl); + tmp = tmp->next; + } + up(&file_lock_sem); host->h_reclaiming = 0; wake_up(&host->h_gracewait); diff -urNX ../kernels/exclude linux-pre8/fs/lockd/svclock.c linux+lock/fs/lockd/svclock.c --- linux-pre8/fs/lockd/svclock.c Mon Apr 3 16:24:05 2000 +++ linux+lock/fs/lockd/svclock.c Fri Jun 2 11:22:58 2000 @@ -347,7 +347,7 @@ /* Append to list of blocked */ nlmsvc_insert_block(block, NLM_NEVER); - if (!block->b_call.a_args.lock.fl.fl_prevblock) { + if (!list_empty(&block->b_call.a_args.lock.fl.fl_block)) { /* Now add block to block list of the conflicting lock if we haven't done so. */ dprintk("lockd: blocking on this lock.\n"); diff -urNX ../kernels/exclude linux-pre8/fs/locks.c linux+lock/fs/locks.c --- linux-pre8/fs/locks.c Fri Apr 21 18:42:34 2000 +++ linux+lock/fs/locks.c Mon Jun 5 03:39:57 2000 @@ -101,72 +101,232 @@ * Some adaptations for NFS support. * Olaf Kirch (okir@monad.swb.de), Dec 1996, * - * Fixed /proc/locks interface so that we can't overrun the buffer we are handed. + * Fixed /proc/locks interface so that we can't overrun the buffer we are + * handed. * Andy Walker (andy@lysaker.kvaerner.no), May 12, 1997. + * + * Added process limits to prevent attacks. + * Use slab allocator instead of kmalloc/kfree. + * Use generic list implementation from . + * Ensure the lock notifier method is always called when a lock is deleted. + * Sped up posix_locks_deadlock by only considering blocked locks. + * No longer require kernel_lock -- file_lock_sem protects us instead. + * Matthew Wilcox , March, 2000. */ #include #include #include +#include +#include #include -static int flock_make_lock(struct file *filp, struct file_lock *fl, - unsigned int cmd); -static int posix_make_lock(struct file *filp, struct file_lock *fl, - struct flock *l); -static int flock_locks_conflict(struct file_lock *caller_fl, - struct file_lock *sys_fl); -static int posix_locks_conflict(struct file_lock *caller_fl, - struct file_lock *sys_fl); -static int locks_conflict(struct file_lock *caller_fl, struct file_lock *sys_fl); -static int flock_lock_file(struct file *filp, struct file_lock *caller, - unsigned int wait); -static int posix_locks_deadlock(struct file_lock *caller, - struct file_lock *blocker); - -static struct file_lock *locks_empty_lock(void); -static struct file_lock *locks_init_lock(struct file_lock *, - struct file_lock *); -static void locks_insert_lock(struct file_lock **pos, struct file_lock *fl); -static void locks_delete_lock(struct file_lock **thisfl_p, unsigned int wait); -static void lock_get_status(char* out, struct file_lock *fl, int id, char *pfx); - -static void locks_insert_block(struct file_lock *blocker, struct file_lock *waiter); -static void locks_delete_block(struct file_lock *blocker, struct file_lock *waiter); -static void locks_wake_up_blocks(struct file_lock *blocker, unsigned int wait); - -struct file_lock *file_lock_table = NULL; - -/* Allocate a new lock, and initialize its fields from fl. - * The lock is not inserted into any lists until locks_insert_lock() or - * locks_insert_block() are called. - */ -static inline struct file_lock *locks_alloc_lock(struct file_lock *fl) +DECLARE_MUTEX(file_lock_sem); + +#define acquire_fl_sem() do { \ +/* printk(KERN_DEBUG __FUNCTION__ ": acquiring file_lock_sem\n");*/\ + down(&file_lock_sem); \ +/* printk(KERN_DEBUG __FUNCTION__ ": acquired file_lock_sem\n");*/ \ +} while (0) + +#define release_fl_sem() up(&file_lock_sem) + +LIST_HEAD(file_lock_list); +static LIST_HEAD(blocked_list); + +static kmem_cache_t *filelock_cache; + +/* Allocate an empty lock structure. */ +static struct file_lock *locks_alloc_lock(int account) { - return locks_init_lock(locks_empty_lock(), fl); + struct file_lock *fl; + if (account && current->locks >= current->rlim[RLIMIT_LOCKS].rlim_cur) + return NULL; + fl = kmem_cache_alloc(filelock_cache, SLAB_KERNEL); + if (fl) { + current->locks += 1; + } + + return fl; } -/* Free lock not inserted in any queue. - */ -static inline void locks_free_lock(struct file_lock *fl) +/* Free an unused lock */ +static void locks_free_lock(struct file_lock *fl) { + if (fl == NULL) { + BUG(); + return; + } + current->locks -= 1; if (waitqueue_active(&fl->fl_wait)) panic("Attempting to free lock with active wait queue"); - if (fl->fl_nextblock != NULL || fl->fl_prevblock != NULL) + if (!list_empty(&fl->fl_block)) panic("Attempting to free lock with active block list"); - - kfree(fl); - return; + + if (!list_empty(&fl->fl_link)) + panic("Attempting to free lock on active lock list"); + + kmem_cache_free(filelock_cache, fl); +} + +/* + * Initialises the fields of the file lock which are invariant for + * free file_locks. + */ +static void init_once(void *foo, kmem_cache_t *cache, unsigned long flags) +{ + struct file_lock *lock = (struct file_lock *) foo; + + if ((flags & (SLAB_CTOR_VERIFY|SLAB_CTOR_CONSTRUCTOR)) != + SLAB_CTOR_CONSTRUCTOR) + return; + + lock->fl_next = NULL; + INIT_LIST_HEAD(&lock->fl_link); + INIT_LIST_HEAD(&lock->fl_block); + init_waitqueue_head(&lock->fl_wait); +} + +/* Initialize a new lock from an existing file_lock structure. */ +static struct file_lock *locks_copy_lock(struct file_lock *new, + struct file_lock *fl) +{ + new->fl_owner = fl->fl_owner; + new->fl_pid = fl->fl_pid; + new->fl_file = fl->fl_file; + new->fl_flags = fl->fl_flags; + new->fl_type = fl->fl_type; + new->fl_start = fl->fl_start; + new->fl_end = fl->fl_end; + new->fl_notify = fl->fl_notify; + new->fl_insert = fl->fl_insert; + new->fl_remove = fl->fl_remove; + new->fl_u = fl->fl_u; + return new; +} + +static inline int flock_translate_cmd(int cmd) { + switch (cmd &~ LOCK_NB) { + case LOCK_SH: + return F_RDLCK; + case LOCK_EX: + return F_WRLCK; + case LOCK_UN: + return F_UNLCK; + } + return -EINVAL; +} + +/* Fill in a file_lock structure with an appropriate FLOCK lock. */ +static struct file_lock *flock_make_lock(struct file *filp, unsigned int type) +{ + struct file_lock *fl = locks_alloc_lock(1); + if (fl == NULL) + return NULL; + + fl->fl_owner = NULL; + fl->fl_file = filp; + fl->fl_flags = FL_FLOCK; + fl->fl_type = type; + fl->fl_start = 0; + fl->fl_end = OFFSET_MAX; + fl->fl_notify = NULL; + fl->fl_insert = NULL; + fl->fl_remove = NULL; + + return fl; +} + +static int assign_type(struct file_lock *fl, int type) +{ + switch (type) { + case F_RDLCK: + case F_WRLCK: + case F_UNLCK: + fl->fl_type = type; + break; + default: + return -EINVAL; + } + return 0; } -/* Check if two locks overlap each other. +/* Verify a "struct flock" and copy it to a "struct file_lock" as a POSIX + * style lock. */ -static inline int locks_overlap(struct file_lock *fl1, struct file_lock *fl2) +static int posix_make_lock(struct file *filp, struct file_lock *fl, + struct flock *l) +{ + loff_t start; + + switch (l->l_whence) { + case 0: /*SEEK_SET*/ + start = 0; + break; + case 1: /*SEEK_CUR*/ + start = filp->f_pos; + break; + case 2: /*SEEK_END*/ + start = filp->f_dentry->d_inode->i_size; + break; + default: + return 0; + } + + start += l->l_start; + if ((start < 0) || (l->l_len < 0)) + return 0; + fl->fl_start = start; /* we record the absolute position */ + + fl->fl_end = start + l->l_len - 1; + if (l->l_len == 0) + fl->fl_end = OFFSET_MAX; + + fl->fl_owner = current->files; + fl->fl_pid = current->pid; + fl->fl_file = filp; + fl->fl_flags = FL_POSIX; + fl->fl_notify = NULL; + fl->fl_insert = NULL; + fl->fl_remove = NULL; + + return (assign_type(fl, l->l_type) == 0); +} + +/* Allocate a file_lock initialised to this type of lease */ +static struct file_lock *lease_alloc(struct file *filp, int type) { - return ((fl1->fl_end >= fl2->fl_start) && - (fl2->fl_end >= fl1->fl_start)); + struct file_lock *fl = locks_alloc_lock(1); + if (fl == NULL) + return NULL; + + fl->fl_owner = current->files; + fl->fl_pid = current->pid; + + fl->fl_file = filp; + fl->fl_flags = FL_LEASE; + if (assign_type(fl, type) != 0) + goto bad; + fl->fl_start = 0; + fl->fl_end = OFFSET_MAX; + fl->fl_notify = NULL; + fl->fl_insert = NULL; + fl->fl_remove = NULL; + + return fl; + +bad: + locks_free_lock(fl); + return NULL; +} + +/* Check if two locks overlap each other. */ +static int locks_overlap(struct file_lock *fl1, struct file_lock *fl2) +{ + return (fl1->fl_end >= fl2->fl_start) && + (fl2->fl_end >= fl1->fl_start); } /* @@ -174,86 +334,108 @@ * N.B. Do we need the test on PID as well as owner? * (Clone tasks should be considered as one "owner".) */ -static inline int -locks_same_owner(struct file_lock *fl1, struct file_lock *fl2) +static int locks_same_owner(struct file_lock *fl1, struct file_lock *fl2) { return (fl1->fl_owner == fl2->fl_owner) && (fl1->fl_pid == fl2->fl_pid); } -/* Insert waiter into blocker's block list. - * We use a circular list so that processes can be easily woken up in - * the order they blocked. The documentation doesn't require this but - * it seems like the reasonable thing to do. +/* Determine if lock sys_fl blocks lock caller_fl. Common functionality + * checks for overlapping locks and shared/exclusive status. */ -static void locks_insert_block(struct file_lock *blocker, - struct file_lock *waiter) +static int locks_conflict(struct file_lock *caller_fl, struct file_lock *sys_fl) { - struct file_lock *prevblock; + if (!locks_overlap(caller_fl, sys_fl)) + return 0; - if (waiter->fl_prevblock) { - printk(KERN_ERR "locks_insert_block: remove duplicated lock " - "(pid=%d %Ld-%Ld type=%d)\n", - waiter->fl_pid, (long long)waiter->fl_start, - (long long)waiter->fl_end, waiter->fl_type); - locks_delete_block(waiter->fl_prevblock, waiter); - } - - if (blocker->fl_prevblock == NULL) - /* No previous waiters - list is empty */ - prevblock = blocker; - else - /* Previous waiters exist - add to end of list */ - prevblock = blocker->fl_prevblock; - - prevblock->fl_nextblock = waiter; - blocker->fl_prevblock = waiter; - waiter->fl_nextblock = blocker; - waiter->fl_prevblock = prevblock; - - return; + switch (caller_fl->fl_type) { + case F_RDLCK: + return (sys_fl->fl_type == F_WRLCK); + + case F_WRLCK: + return 1; + + default: + printk("locks_conflict(): impossible lock type - %d\n", + caller_fl->fl_type); + break; + } + return 0; /* This should never happen */ } -/* Remove waiter from blocker's block list. - * When blocker ends up pointing to itself then the list is empty. +/* Determine if lock sys_fl blocks lock caller_fl. POSIX specific + * checking before calling the locks_conflict(). */ -static void locks_delete_block(struct file_lock *blocker, - struct file_lock *waiter) +static int posix_locks_conflict(struct file_lock *caller_fl, struct file_lock *sys_fl) { - struct file_lock *nextblock; - struct file_lock *prevblock; - - nextblock = waiter->fl_nextblock; - prevblock = waiter->fl_prevblock; + /* POSIX locks owned by the same process do not conflict with + * each other. + */ + if (!(sys_fl->fl_flags & FL_POSIX) || + locks_same_owner(caller_fl, sys_fl)) + return 0; - if (nextblock == NULL) - return; - - nextblock->fl_prevblock = prevblock; - prevblock->fl_nextblock = nextblock; + return locks_conflict(caller_fl, sys_fl); +} - waiter->fl_prevblock = waiter->fl_nextblock = NULL; - if (blocker->fl_nextblock == blocker) - /* No more locks on blocker's blocked list */ - blocker->fl_prevblock = blocker->fl_nextblock = NULL; - return; +/* Determine if lock sys_fl blocks lock caller_fl. FLOCK specific + * checking before calling the locks_conflict(). + */ +static int flock_locks_conflict(struct file_lock *caller_fl, struct file_lock *sys_fl) +{ + /* FLOCK locks referring to the same filp do not conflict with + * each other. + */ + if (!(sys_fl->fl_flags & FL_FLOCK) || + (caller_fl->fl_file == sys_fl->fl_file)) + return 0; + + return locks_conflict(caller_fl, sys_fl); } -/* The following two are for the benefit of lockd. +/* Insert file lock fl into an inode's lock list at the position indicated + * by pos. At the same time add the lock to the global file lock list. */ -void -posix_block_lock(struct file_lock *blocker, struct file_lock *waiter) +static void locks_insert_lock(struct file_lock **pos, struct file_lock *fl) { - locks_insert_block(blocker, waiter); - return; + list_add(&fl->fl_link, &file_lock_list); + + /* insert into file's list */ + fl->fl_next = *pos; + *pos = fl; + + if (fl->fl_insert) + fl->fl_insert(fl); } -void -posix_unblock_lock(struct file_lock *waiter) +/* Remove waiter from blocker's block list. + * When blocker ends up pointing to itself then the list is empty. + */ +static inline void locks_delete_block(struct file_lock *waiter) { - if (waiter->fl_prevblock) - locks_delete_block(waiter->fl_prevblock, waiter); - return; + list_del(&waiter->fl_block); + INIT_LIST_HEAD(&waiter->fl_block); + list_del(&waiter->fl_link); + INIT_LIST_HEAD(&waiter->fl_link); +} + +/* Insert waiter into blocker's block list. + * We use a circular list so that processes can be easily woken up in + * the order they blocked. The documentation doesn't require this but + * it seems like the reasonable thing to do. + */ +static inline void locks_insert_block(struct file_lock *blocker, + struct file_lock *waiter) +{ + if (!list_empty(&waiter->fl_block)) { + printk(KERN_ERR "locks_insert_block: removing duplicated lock " + "(pid=%d %Ld-%Ld type=%d)\n", waiter->fl_pid, + waiter->fl_start, waiter->fl_end, waiter->fl_type); + locks_delete_block(waiter); + } + list_add_tail(&waiter->fl_block, &blocker->fl_block); + list_add(&waiter->fl_link, &blocked_list); + waiter->fl_next = blocker; } /* Wake up processes blocked waiting for blocker. @@ -262,56 +444,141 @@ */ static void locks_wake_up_blocks(struct file_lock *blocker, unsigned int wait) { - struct file_lock *waiter; - - while ((waiter = blocker->fl_nextblock) != NULL) { + while (!list_empty(&blocker->fl_block)) { + struct file_lock *waiter = list_entry(&blocker->fl_block, + struct file_lock, fl_block); /* N.B. Is it possible for the notify function to block?? */ - if (waiter->fl_notify) + if (waiter->fl_notify) { + printk(KERN_EMERG "fl_notify = %p\n", waiter->fl_notify); waiter->fl_notify(waiter); + } else { + printk(KERN_EMERG "fl_notify = NULL\n"); + } wake_up(&waiter->fl_wait); if (wait) { /* Let the blocked process remove waiter from the * block list when it gets scheduled. */ +printk("locks_wake_up_blocks: scheduling\n"); current->policy |= SCHED_YIELD; schedule(); } else { /* Remove waiter from the block list, because by the * time it wakes up blocker won't exist any more. */ - locks_delete_block(blocker, waiter); + locks_delete_block(waiter); } } - return; } -/* flock() system call entry point. Apply a FL_FLOCK style lock to - * an open file descriptor. +/* Delete a lock and then free it. + * Remove our lock from the lock lists, wake up processes that are blocked + * waiting for this lock, notify the FS that the lock has been cleared and + * finally free the lock. */ -asmlinkage long sys_flock(unsigned int fd, unsigned int cmd) +static void locks_delete_lock(struct file_lock **thisfl_p, unsigned int wait) { - struct file_lock file_lock; - struct file *filp; - int error; + int (*lock)(struct file *, int, struct file_lock *); + struct file_lock *fl = *thisfl_p; - lock_kernel(); - error = -EBADF; - filp = fget(fd); - if (!filp) - goto out; - error = -EINVAL; - if (!flock_make_lock(filp, &file_lock, cmd)) - goto out_putf; - error = -EBADF; - if ((file_lock.fl_type != F_UNLCK) && !(filp->f_mode & 3)) - goto out_putf; - error = flock_lock_file(filp, &file_lock, - (cmd & (LOCK_UN | LOCK_NB)) ? 0 : 1); -out_putf: - fput(filp); -out: - unlock_kernel(); - return (error); + *thisfl_p = fl->fl_next; + fl->fl_next = NULL; + + list_del(&fl->fl_link); + INIT_LIST_HEAD(&fl->fl_link); + + if (fl->fl_remove) + fl->fl_remove(fl); + + locks_wake_up_blocks(fl, wait); + lock = fl->fl_file->f_op->lock; + if (lock) { + fl->fl_type = F_UNLCK; + lock(fl->fl_file, F_SETLK, fl); + } + locks_free_lock(fl); +} + +int interruptible_sleep_on_locked(wait_queue_head_t *fl_wait, struct semaphore *sem, int timeout) +{ + int result = 0; + wait_queue_t wait; + init_waitqueue_entry(&wait, current); + + __add_wait_queue(fl_wait, &wait); + current->state = TASK_INTERRUPTIBLE; + up(sem); + if (timeout == 0) { + schedule(); + } else { + result = schedule_timeout(timeout); + } + if (signal_pending(current)) { + result = -ERESTARTSYS; + } + down(sem); + remove_wait_queue(fl_wait, &wait); + current->state = TASK_RUNNING; + return result; +} + +static int locks_block_on(struct file_lock *blocker, struct file_lock *waiter) +{ + int result; + locks_insert_block(blocker, waiter); + result = interruptible_sleep_on_locked(&waiter->fl_wait, &file_lock_sem, 0); + locks_delete_block(waiter); + return result; +} + +static int locks_block_on_timeout(struct file_lock *blocker, struct file_lock *waiter, int time) +{ + int result; + locks_insert_block(blocker, waiter); + result = interruptible_sleep_on_locked(&waiter->fl_wait, &file_lock_sem, time); + locks_delete_block(waiter); + return result; +} + +/* This function tests for deadlock condition before putting a process to + * sleep. The detection scheme is no longer recursive. Recursive was neat, + * but dangerous - we risked stack corruption if the lock data was bad, or + * if the recursion was too deep for any other reason. + * + * We rely on the fact that a task can only be on one lock's wait queue + * at a time. When we find blocked_task on a wait queue we can re-search + * with blocked_task equal to that queue's owner, until either blocked_task + * isn't found, or blocked_task is found on a queue owned by my_task. + * + * Note: the above assumption may not be true when handling lock requests + * from a broken NFS client. But broken NFS clients have a lot more to + * worry about than proper deadlock detection anyway... --okir + */ +static int posix_locks_deadlock(struct file_lock *caller_fl, + struct file_lock *block_fl) +{ + struct file_lock *fl = block_fl; + struct list_head *tmp = blocked_list.next; + fl_owner_t caller_owner, blocked_owner; + unsigned int caller_pid, blocked_pid; + + caller_owner = caller_fl->fl_owner; + caller_pid = caller_fl->fl_pid; + +next_task: + blocked_owner = fl->fl_owner; + blocked_pid = fl->fl_pid; + if (caller_owner == blocked_owner && caller_pid == blocked_pid) + return 1; + while (tmp != &blocked_list) { + fl = list_entry(tmp, struct file_lock, fl_link); + tmp = tmp->next; + if (fl->fl_owner != blocked_owner || fl->fl_pid != blocked_pid) + continue; + fl = fl->fl_next; + goto next_task; + } + return 0; } /* Report the first existing lock that would conflict with l. @@ -320,7 +587,7 @@ int fcntl_getlk(unsigned int fd, struct flock *l) { struct file *filp; - struct file_lock *fl,file_lock; + struct file_lock *fl, file_lock; struct flock flock; int error; @@ -343,11 +610,13 @@ error = filp->f_op->lock(filp, F_GETLK, &file_lock); if (error < 0) goto out_putf; - else if (error == LOCK_USE_CLNT) - /* Bypass for NFS with no locking - 2.0.36 compat */ - fl = posix_test_lock(filp, &file_lock); - else - fl = (file_lock.fl_type == F_UNLCK ? NULL : &file_lock); + + if (error == LOCK_USE_CLNT) { + /* Bypass for NFS with no locking - 2.0.36 compat */ + fl = posix_test_lock(filp, &file_lock); + } else { + fl = (file_lock.fl_type == F_UNLCK ? NULL : &file_lock); + } } else { fl = posix_test_lock(filp, &file_lock); } @@ -377,7 +646,7 @@ int fcntl_setlk(unsigned int fd, unsigned int cmd, struct flock *l) { struct file *filp; - struct file_lock file_lock; + struct file_lock *fl = locks_alloc_lock(0); struct flock flock; struct inode *inode; int error; @@ -389,8 +658,7 @@ if (copy_from_user(&flock, l, sizeof(flock))) goto out; - /* Get arguments and validate them ... - */ + /* Get arguments and validate them ... */ error = -EBADF; filp = fget(fd); @@ -419,7 +687,7 @@ } error = -EINVAL; - if (!posix_make_lock(filp, &file_lock, &flock)) + if (!posix_make_lock(filp, fl, &flock)) goto out_putf; error = -EBADF; @@ -442,8 +710,8 @@ static int count = 0; if (!count) { count=1; - printk(KERN_WARNING - "fcntl_setlk() called by process %d (%s) with broken flock() emulation\n", + printk(KERN_WARNING "fcntl_setlk() called by process %d (%s) " + "with broken flock() emulation\n", current->pid, current->comm); } } @@ -457,409 +725,190 @@ } if (filp->f_op->lock != NULL) { - error = filp->f_op->lock(filp, cmd, &file_lock); + error = filp->f_op->lock(filp, cmd, fl); if (error < 0) goto out_putf; } - error = posix_lock_file(filp, &file_lock, cmd == F_SETLKW); + error = posix_lock_file(filp, fl, cmd == F_SETLKW); out_putf: fput(filp); out: + locks_free_lock(fl); return error; } /* * This function is called when the file is being removed - * from the task's fd array. + * from the task's fd array. For POSIX locks we free all + * locks on this file for the given task. */ void locks_remove_posix(struct file *filp, fl_owner_t owner) { - struct inode * inode = filp->f_dentry->d_inode; - struct file_lock file_lock, *fl; - struct file_lock **before; - - /* - * For POSIX locks we free all locks on this file for the given task. - */ -repeat: - before = &inode->i_flock; - while ((fl = *before) != NULL) { - if ((fl->fl_flags & FL_POSIX) && fl->fl_owner == owner) { - int (*lock)(struct file *, int, struct file_lock *); - lock = filp->f_op->lock; - if (lock) { - file_lock = *fl; - file_lock.fl_type = F_UNLCK; - } - locks_delete_lock(before, 0); - if (lock) { - lock(filp, F_SETLK, &file_lock); - /* List may have changed: */ - goto repeat; - } - continue; - } - before = &fl->fl_next; - } -} - -/* - * This function is called on the last close of an open file. - */ -void locks_remove_flock(struct file *filp) -{ - struct inode * inode = filp->f_dentry->d_inode; - struct file_lock file_lock, *fl; - struct file_lock **before; - -repeat: + struct inode *inode = filp->f_dentry->d_inode; + struct file_lock *fl, **before; + acquire_fl_sem(); before = &inode->i_flock; while ((fl = *before) != NULL) { - if ((fl->fl_flags & FL_FLOCK) && fl->fl_file == filp) { - int (*lock)(struct file *, int, struct file_lock *); - lock = NULL; - if (filp->f_op) - lock = filp->f_op->lock; - if (lock) { - file_lock = *fl; - file_lock.fl_type = F_UNLCK; - } - locks_delete_lock(before, 0); - if (lock) { - lock(filp, F_SETLK, &file_lock); - /* List may have changed: */ - goto repeat; - } - continue; - } - before = &fl->fl_next; - } -} - -struct file_lock * -posix_test_lock(struct file *filp, struct file_lock *fl) -{ - struct file_lock *cfl; - - for (cfl = filp->f_dentry->d_inode->i_flock; cfl; cfl = cfl->fl_next) { - if (!(cfl->fl_flags & FL_POSIX)) - continue; - if (posix_locks_conflict(cfl, fl)) - break; - } - - return (cfl); -} - -int locks_mandatory_locked(struct inode *inode) -{ - fl_owner_t owner = current->files; - struct file_lock *fl; - - /* - * Search the lock list for this inode for any POSIX locks. - */ - lock_kernel(); - for (fl = inode->i_flock; fl != NULL; fl = fl->fl_next) { - if (!(fl->fl_flags & FL_POSIX)) - continue; - if (fl->fl_owner != owner) - break; - } - unlock_kernel(); - return fl ? -EAGAIN : 0; -} - -int locks_mandatory_area(int read_write, struct inode *inode, - struct file *filp, loff_t offset, - size_t count) -{ - struct file_lock *fl; - struct file_lock tfl; - int error; - - memset(&tfl, 0, sizeof(tfl)); - - tfl.fl_file = filp; - tfl.fl_flags = FL_POSIX | FL_ACCESS; - tfl.fl_owner = current->files; - tfl.fl_pid = current->pid; - init_waitqueue_head(&tfl.fl_wait); - tfl.fl_type = (read_write == FLOCK_VERIFY_WRITE) ? F_WRLCK : F_RDLCK; - tfl.fl_start = offset; - tfl.fl_end = offset + count - 1; - - error = 0; - lock_kernel(); - -repeat: - /* Search the lock list for this inode for locks that conflict with - * the proposed read/write. - */ - for (fl = inode->i_flock; ; fl = fl->fl_next) { - error = 0; - if (!fl) - break; - if (!(fl->fl_flags & FL_POSIX)) - continue; - /* Block for writes against a "read" lock, - * and both reads and writes against a "write" lock. - */ - if (posix_locks_conflict(&tfl, fl)) { - error = -EAGAIN; - if (filp && (filp->f_flags & O_NONBLOCK)) - break; - error = -ERESTARTSYS; - if (signal_pending(current)) - break; - error = -EDEADLK; - if (posix_locks_deadlock(&tfl, fl)) - break; - - locks_insert_block(fl, &tfl); - interruptible_sleep_on(&tfl.fl_wait); - locks_delete_block(fl, &tfl); - - /* - * If we've been sleeping someone might have - * changed the permissions behind our back. - */ - if ((inode->i_mode & (S_ISGID | S_IXGRP)) != S_ISGID) - break; - goto repeat; - } - } - unlock_kernel(); - return error; -} - -/* Verify a "struct flock" and copy it to a "struct file_lock" as a POSIX - * style lock. - */ -static int posix_make_lock(struct file *filp, struct file_lock *fl, - struct flock *l) -{ - loff_t start; - - memset(fl, 0, sizeof(*fl)); - - init_waitqueue_head(&fl->fl_wait); - fl->fl_flags = FL_POSIX; - - switch (l->l_type) { - case F_RDLCK: - case F_WRLCK: - case F_UNLCK: - fl->fl_type = l->l_type; - break; - default: - return (0); - } - - switch (l->l_whence) { - case 0: /*SEEK_SET*/ - start = 0; - break; - case 1: /*SEEK_CUR*/ - start = filp->f_pos; - break; - case 2: /*SEEK_END*/ - start = filp->f_dentry->d_inode->i_size; - break; - default: - return (0); - } - - if (((start += l->l_start) < 0) || (l->l_len < 0)) - return (0); - fl->fl_end = start + l->l_len - 1; - if (l->l_len > 0 && fl->fl_end < 0) - return (0); - fl->fl_start = start; /* we record the absolute position */ - if (l->l_len == 0) - fl->fl_end = OFFSET_MAX; - - fl->fl_file = filp; - fl->fl_owner = current->files; - fl->fl_pid = current->pid; - - return (1); -} - -/* Verify a call to flock() and fill in a file_lock structure with - * an appropriate FLOCK lock. - */ -static int flock_make_lock(struct file *filp, struct file_lock *fl, - unsigned int cmd) -{ - memset(fl, 0, sizeof(*fl)); - - init_waitqueue_head(&fl->fl_wait); - - switch (cmd & ~LOCK_NB) { - case LOCK_SH: - fl->fl_type = F_RDLCK; - break; - case LOCK_EX: - fl->fl_type = F_WRLCK; - break; - case LOCK_UN: - fl->fl_type = F_UNLCK; - break; - default: - return (0); - } - - fl->fl_flags = FL_FLOCK; - fl->fl_start = 0; - fl->fl_end = OFFSET_MAX; - fl->fl_file = filp; - fl->fl_owner = NULL; - - return (1); + if ((fl->fl_flags & FL_POSIX) && fl->fl_owner == owner) { + locks_delete_lock(before, 0); + } + before = &fl->fl_next; + } + release_fl_sem(); } -/* Determine if lock sys_fl blocks lock caller_fl. POSIX specific - * checking before calling the locks_conflict(). +/* + * This function is called on the last close of an open file. */ -static int posix_locks_conflict(struct file_lock *caller_fl, struct file_lock *sys_fl) +void locks_remove_flock(struct file *filp) { - /* POSIX locks owned by the same process do not conflict with - * each other. - */ - if (!(sys_fl->fl_flags & FL_POSIX) || - locks_same_owner(caller_fl, sys_fl)) - return (0); + struct inode * inode = filp->f_dentry->d_inode; + struct file_lock *fl, **before; - return (locks_conflict(caller_fl, sys_fl)); + acquire_fl_sem(); + before = &inode->i_flock; + + while ((fl = *before) != NULL) { +printk("fl->fl_flags = 0x%x\n", fl->fl_flags); + if ((fl->fl_flags & (FL_FLOCK | FL_LEASE)) && + (fl->fl_file == filp)) { + locks_delete_lock(before, 0); + } + before = &fl->fl_next; + } + release_fl_sem(); } -/* Determine if lock sys_fl blocks lock caller_fl. FLOCK specific - * checking before calling the locks_conflict(). - */ -static int flock_locks_conflict(struct file_lock *caller_fl, struct file_lock *sys_fl) +struct file_lock * +posix_test_lock(struct file *filp, struct file_lock *fl) { - /* FLOCK locks referring to the same filp do not conflict with - * each other. - */ - if (!(sys_fl->fl_flags & FL_FLOCK) || - (caller_fl->fl_file == sys_fl->fl_file)) - return (0); + struct file_lock *cfl; + + acquire_fl_sem(); + for (cfl = filp->f_dentry->d_inode->i_flock; cfl; cfl = cfl->fl_next) { + if (!(cfl->fl_flags & FL_POSIX)) + continue; + if (posix_locks_conflict(cfl, fl)) + break; + } + release_fl_sem(); - return (locks_conflict(caller_fl, sys_fl)); + return cfl; } -/* Determine if lock sys_fl blocks lock caller_fl. Common functionality - * checks for overlapping locks and shared/exclusive status. - */ -static int locks_conflict(struct file_lock *caller_fl, struct file_lock *sys_fl) +int locks_mandatory_locked(struct inode *inode) { - if (!locks_overlap(caller_fl, sys_fl)) - return (0); - - switch (caller_fl->fl_type) { - case F_RDLCK: - return (sys_fl->fl_type == F_WRLCK); - - case F_WRLCK: - return (1); + fl_owner_t owner = current->files; + struct file_lock *fl; - default: - printk("locks_conflict(): impossible lock type - %d\n", - caller_fl->fl_type); - break; + /* + * Search the lock list for this inode for any POSIX locks. + */ + acquire_fl_sem(); + for (fl = inode->i_flock; fl != NULL; fl = fl->fl_next) { + if (!(fl->fl_flags & FL_POSIX)) + continue; + if (fl->fl_owner != owner) + break; } - return (0); /* This should never happen */ + release_fl_sem(); + return fl ? -EAGAIN : 0; } -/* This function tests for deadlock condition before putting a process to - * sleep. The detection scheme is no longer recursive. Recursive was neat, - * but dangerous - we risked stack corruption if the lock data was bad, or - * if the recursion was too deep for any other reason. - * - * We rely on the fact that a task can only be on one lock's wait queue - * at a time. When we find blocked_task on a wait queue we can re-search - * with blocked_task equal to that queue's owner, until either blocked_task - * isn't found, or blocked_task is found on a queue owned by my_task. - * - * Note: the above assumption may not be true when handling lock requests - * from a broken NFS client. But broken NFS clients have a lot more to - * worry about than proper deadlock detection anyway... --okir - */ -static int posix_locks_deadlock(struct file_lock *caller_fl, - struct file_lock *block_fl) +int locks_mandatory_area(int read_write, struct inode *inode, + struct file *filp, loff_t offset, + size_t count) { struct file_lock *fl; - struct file_lock *bfl; - void *caller_owner, *blocked_owner; - unsigned int caller_pid, blocked_pid; + struct file_lock *tfl = locks_alloc_lock(0); + int error; - caller_owner = caller_fl->fl_owner; - caller_pid = caller_fl->fl_pid; - blocked_owner = block_fl->fl_owner; - blocked_pid = block_fl->fl_pid; + tfl->fl_owner = current->files; + tfl->fl_pid = current->pid; + tfl->fl_file = filp; + tfl->fl_flags = FL_POSIX | FL_ACCESS; + tfl->fl_type = (read_write == FLOCK_VERIFY_WRITE) ? F_WRLCK : F_RDLCK; + tfl->fl_start = offset; + tfl->fl_end = offset + count - 1; -next_task: - if (caller_owner == blocked_owner && caller_pid == blocked_pid) - return (1); - for (fl = file_lock_table; fl != NULL; fl = fl->fl_nextlink) { - if (fl->fl_owner == NULL || fl->fl_nextblock == NULL) + error = 0; + acquire_fl_sem(); + + /* Search the lock list for this inode for locks that conflict with + * the proposed read/write. + */ + for (fl = inode->i_flock; fl != NULL; fl = fl->fl_next) { + if (!(fl->fl_flags & FL_POSIX)) continue; - for (bfl = fl->fl_nextblock; bfl != fl; bfl = bfl->fl_nextblock) { - if (bfl->fl_owner == blocked_owner && - bfl->fl_pid == blocked_pid) { - if (fl->fl_owner == caller_owner && - fl->fl_pid == caller_pid) { - return (1); - } - blocked_owner = fl->fl_owner; - blocked_pid = fl->fl_pid; - goto next_task; - } - } + if (fl->fl_start > tfl->fl_end) + break; + error = 0; + if (!posix_locks_conflict(tfl, fl)) + continue; + + error = -EAGAIN; + if (filp && (filp->f_flags & O_NONBLOCK)) + break; + error = -EDEADLK; + if (posix_locks_deadlock(tfl, fl)) + break; + + error = locks_block_on(fl, tfl); + if (error != 0) + break; + + /* + * If we've been sleeping someone might have + * changed the permissions behind our back. + */ + if ((inode->i_mode & (S_ISGID | S_IXGRP)) != S_ISGID) + break; + fl = inode->i_flock; } - return (0); + release_fl_sem(); + locks_free_lock(tfl); + return error; } -/* Try to create a FLOCK lock on filp. We always insert new FLOCK locks at - * the head of the list, but that's secret knowledge known only to the next - * two functions. +/* Try to create a FLOCK lock on filp. We always insert new FLOCK locks + * at the head of the list, but that's secret knowledge known only to + * flock_lock_file and posix_lock_file. */ -static int flock_lock_file(struct file *filp, struct file_lock *caller, +static int flock_lock_file(struct file *filp, unsigned int lock_type, unsigned int wait) { struct file_lock *fl; struct file_lock *new_fl = NULL; struct file_lock **before; - struct inode * inode = filp->f_dentry->d_inode; + struct inode *inode = filp->f_dentry->d_inode; int error, change; - int unlock = (caller->fl_type == F_UNLCK); + int unlock = (lock_type == F_UNLCK); - /* - * If we need a new lock, get it in advance to avoid races. - */ if (!unlock) { error = -ENOLCK; - new_fl = locks_alloc_lock(caller); - if (!new_fl) - goto out; + new_fl = flock_make_lock(filp, lock_type); + if (new_fl == NULL) + return error; } error = 0; search: change = 0; before = &inode->i_flock; - while (((fl = *before) != NULL) && (fl->fl_flags & FL_FLOCK)) { - if (caller->fl_file == fl->fl_file) { - if (caller->fl_type == fl->fl_type) + for (;;) { + fl = *before; + if ((fl == NULL) || ((fl->fl_flags & FL_FLOCK) == 0)) + break; + if (filp == fl->fl_file) { + if (lock_type == fl->fl_type) goto out; change = 1; break; } before = &fl->fl_next; } - /* change means that we are changing the type of an existing lock, or + /* change means that we are changing the type of an existing lock, * or else unlocking it. */ if (change) { @@ -874,11 +923,6 @@ if (unlock) goto out; -repeat: - /* Check signals each time we start */ - error = -ERESTARTSYS; - if (signal_pending(current)) - goto out; for (fl = inode->i_flock; (fl != NULL) && (fl->fl_flags & FL_FLOCK); fl = fl->fl_next) { if (!flock_locks_conflict(new_fl, fl)) @@ -886,10 +930,12 @@ error = -EAGAIN; if (!wait) goto out; - locks_insert_block(fl, new_fl); - interruptible_sleep_on(&new_fl->fl_wait); - locks_delete_block(fl, new_fl); - goto repeat; + + error = locks_block_on(fl, new_fl); + if (error != 0) + goto out; + + fl = inode->i_flock; } locks_insert_lock(&inode->i_flock, new_fl); new_fl = NULL; @@ -901,7 +947,13 @@ return error; } -/* Add a POSIX style lock to a file. +/** + * posix_lock_file: + * @filp: The file to apply the lock to + * @caller: The lock to be applied + * @wait: 1 to retry automatically, 0 to return -EAGAIN + * + * Add a POSIX style lock to a file. * We merge adjacent locks whenever possible. POSIX locks are sorted by owner * task, then by starting address * @@ -924,18 +976,21 @@ struct inode * inode = filp->f_dentry->d_inode; int error, added = 0; + if (caller->fl_notify != NULL) + printk("notify = %p\n", caller->fl_notify); + + acquire_fl_sem(); /* * We may need two file_lock structures for this operation, * so we get them in advance to avoid races. */ - new_fl = locks_empty_lock(); - new_fl2 = locks_empty_lock(); + new_fl = locks_alloc_lock(0); + new_fl2 = locks_alloc_lock(0); error = -ENOLCK; /* "no luck" */ if (!(new_fl && new_fl2)) goto out; if (caller->fl_type != F_UNLCK) { - repeat: for (fl = inode->i_flock; fl != NULL; fl = fl->fl_next) { if (!(fl->fl_flags & FL_POSIX)) continue; @@ -947,13 +1002,12 @@ error = -EDEADLK; if (posix_locks_deadlock(caller, fl)) goto out; - error = -ERESTARTSYS; - if (signal_pending(current)) + + error = locks_block_on(fl, caller); + if (error != 0) goto out; - locks_insert_block(fl, caller); - interruptible_sleep_on(&caller->fl_wait); - locks_delete_block(fl, caller); - goto repeat; + + fl = inode->i_flock; } } @@ -972,7 +1026,6 @@ !locks_same_owner(caller, fl))) { before = &fl->fl_next; } - /* Process locks with this owner. */ while ((fl = *before) && locks_same_owner(caller, fl)) { @@ -992,22 +1045,23 @@ * lock yielding from the lower start address of both * locks to the higher end address. */ - if (fl->fl_start > caller->fl_start) + if (fl->fl_start > caller->fl_start) { fl->fl_start = caller->fl_start; - else + } else { caller->fl_start = fl->fl_start; - if (fl->fl_end < caller->fl_end) + } + if (fl->fl_end < caller->fl_end) { fl->fl_end = caller->fl_end; - else + } else { caller->fl_end = fl->fl_end; + } if (added) { locks_delete_lock(before, 0); continue; } caller = fl; added = 1; - } - else { + } else { /* Processing for different lock types is a bit * more complex. */ @@ -1048,9 +1102,8 @@ added = 1; } } - /* Go on to next lock. - */ - next_lock: + /* Go on to next lock. */ +next_lock: before = &fl->fl_next; } @@ -1058,7 +1111,7 @@ if (!added) { if (caller->fl_type == F_UNLCK) goto out; - locks_init_lock(new_fl, caller); + locks_copy_lock(new_fl, caller); locks_insert_lock(before, new_fl); new_fl = NULL; } @@ -1068,7 +1121,7 @@ * so we have to use the second new lock (in this * case, even F_UNLCK may fail!). */ - left = locks_init_lock(new_fl2, right); + left = locks_copy_lock(new_fl2, right); locks_insert_lock(before, left); new_fl2 = NULL; } @@ -1080,106 +1133,219 @@ locks_wake_up_blocks(left, 0); } out: - /* - * Free any unused locks. (They haven't - * ever been used, so we use kfree().) - */ + release_fl_sem(); + /* Free any unused locks. */ if (new_fl) - kfree(new_fl); + locks_free_lock(new_fl); if (new_fl2) - kfree(new_fl2); + locks_free_lock(new_fl2); return error; } /* - * Allocate an empty lock structure. We can use GFP_KERNEL now that - * all allocations are done in advance. + * get_lease has checked there _is_ a lease on this */ -static struct file_lock *locks_empty_lock(void) +int __get_lease(struct inode *inode, unsigned int mode) { - /* Okay, let's make a new file_lock structure... */ - return ((struct file_lock *) kmalloc(sizeof(struct file_lock), - GFP_KERNEL)); + int error = 0, future; + struct file_lock *new_fl, *flock = inode->i_flock; + + if (flock->fl_owner == current->files) + return 0; + + new_fl = lease_alloc(NULL, 0); + + acquire_fl_sem(); + + while (flock->fl_type & F_INPROGRESS) { + error = locks_block_on(flock, new_fl); + if (error != 0) + goto out; + + flock = inode->i_flock; + if ((flock == NULL) || (flock->fl_flags != FL_LEASE)) + goto out; + } + + if (mode & FMODE_WRITE) { + /* If we want write access, we have to revoke any lease. */ + future = F_UNLCK | F_INPROGRESS; + } else if (flock->fl_type & F_WRLCK) { + /* Downgrade the exclusive lease to a read-only lease. */ + future = F_RDLCK | F_INPROGRESS; + } else { + /* the existing lease was read-only, so we can read too. */ + goto out; + } + + { + struct file_lock *fl = flock; + do { + fl->fl_type = future; + fl = fl->fl_next; + } while (fl != NULL && (fl->fl_flags & FL_LEASE)); + } + + inode_send_fasync(inode); + + error = 30 * HZ; +restart: + error = locks_block_on_timeout(flock, new_fl, error); + if (error == 0) { + /* We timed out. Unilaterally break the lease. */ + printk(KERN_DEBUG "lease timed out"); + locks_delete_lock(&inode->i_flock, 0); + } else if (error > 0) { + flock = inode->i_flock; +// if (flock->fl_flags & FL_LEASE) +// goto restart; + error = 0; + } + +out: + release_fl_sem(); + return error; } -/* - * Initialize a new lock from an existing file_lock structure. - */ -static struct file_lock *locks_init_lock(struct file_lock *new, - struct file_lock *fl) +int fcntl_getlease(unsigned int fd) { - if (new) { - memset(new, 0, sizeof(*new)); - new->fl_owner = fl->fl_owner; - new->fl_pid = fl->fl_pid; - init_waitqueue_head(&new->fl_wait); - new->fl_file = fl->fl_file; - new->fl_flags = fl->fl_flags; - new->fl_type = fl->fl_type; - new->fl_start = fl->fl_start; - new->fl_end = fl->fl_end; - new->fl_notify = fl->fl_notify; - new->fl_insert = fl->fl_insert; - new->fl_remove = fl->fl_remove; - new->fl_u = fl->fl_u; + struct file_lock *fl; + struct file *filp; + int error; + + error = -EBADF; + filp = fget(fd); + if (!filp) + goto out; + + fl = filp->f_dentry->d_inode->i_flock; + if ((fl == NULL) || ((fl->fl_flags & FL_LEASE) == 0)) { + error = F_UNLCK; + } else { + error = fl->fl_type; } - return new; +out: + fput(filp); + return error; } -/* Insert file lock fl into an inode's lock list at the position indicated - * by pos. At the same time add the lock to the global file lock list. - */ -static void locks_insert_lock(struct file_lock **pos, struct file_lock *fl) +/* We already had a lease on this file; just change its type */ +static int lease_modify(struct file_lock **before, int arg, int fd, struct file *filp) { - fl->fl_nextlink = file_lock_table; - fl->fl_prevlink = NULL; - if (file_lock_table != NULL) - file_lock_table->fl_prevlink = fl; - file_lock_table = fl; - fl->fl_next = *pos; /* insert into file's list */ - *pos = fl; + struct file_lock *fl = *before; + int error = assign_type(fl, arg); + if (error < 0) + goto out; - if (fl->fl_insert) - fl->fl_insert(fl); + locks_wake_up_blocks(fl, 0); + + if (arg == F_UNLCK) { + locks_delete_lock(before, 0); + file_fasync(fd, filp, 0); + } - return; +out: + return error; } -/* Delete a lock and free it. - * First remove our lock from the active lock lists. Then call - * locks_wake_up_blocks() to wake up processes that are blocked - * waiting for this lock. Finally free the lock structure. - */ -static void locks_delete_lock(struct file_lock **thisfl_p, unsigned int wait) +int fcntl_setlease(unsigned int fd, long arg) { - struct file_lock *thisfl; - struct file_lock *prevfl; - struct file_lock *nextfl; - - thisfl = *thisfl_p; - *thisfl_p = thisfl->fl_next; + struct file_lock *fl, **before, **my_before = NULL; + struct file *filp; + struct inode *inode; + int error, read_allowed = 1, write_allowed = 1; + + error = -EBADF; + filp = fget(fd); + if (!filp) + goto out; - prevfl = thisfl->fl_prevlink; - nextfl = thisfl->fl_nextlink; + inode = filp->f_dentry->d_inode; + before = &inode->i_flock; - if (nextfl != NULL) - nextfl->fl_prevlink = prevfl; + acquire_fl_sem(); + + while ((fl = *before) != NULL) { + if (fl->fl_flags != FL_LEASE) + break; + if (fl->fl_owner == current->files) + my_before = before; + write_allowed = 0; + if ((fl->fl_type & F_RDLCK) == 0) + read_allowed = 0; + before = &fl->fl_next; + } - if (prevfl != NULL) - prevfl->fl_nextlink = nextfl; - else - file_lock_table = nextfl; + if (my_before != NULL) { + error = lease_modify(my_before, arg, fd, filp); + goto out_unlock; + } - if (thisfl->fl_remove) - thisfl->fl_remove(thisfl); + fl = lease_alloc(filp, arg); - locks_wake_up_blocks(thisfl, wait); - locks_free_lock(thisfl); + error = -EINVAL; + if (fl == NULL) + goto out_unlock; + + error = -EAGAIN; + if ((fl->fl_type == F_RDLCK && !read_allowed) + || (fl->fl_type == F_WRLCK && !write_allowed)) + goto out_freelock; + + error = file_fasync(fd, filp, 1); + if (error < 0) + goto out_freelock; + + fl->fl_next = *before; + *before = fl; + +out_unlock: + release_fl_sem(); +out: + fput(filp); + return error; + +out_freelock: + *before = fl->fl_next; + locks_free_lock(fl); + goto out_unlock; +} + +/* flock() system call entry point. Apply a FL_FLOCK style lock to + * an open file descriptor. + */ +asmlinkage long sys_flock(unsigned int fd, unsigned int cmd) +{ + struct file *filp; + int error, type; + + error = -EBADF; + filp = fget(fd); + if (!filp) + goto out; + + error = flock_translate_cmd(cmd); + if (error < 0) + goto out; + type = error; + + error = -EBADF; + if ((type != F_UNLCK) && !(filp->f_mode & 3)) + goto out_putf; - return; + acquire_fl_sem(); + error = flock_lock_file(filp, type, + (cmd & (LOCK_UN | LOCK_NB)) ? 0 : 1); + release_fl_sem(); +out_putf: + fput(filp); +out: + return error; } -static void lock_get_status(char* out, struct file_lock *fl, int id, char *pfx) +/* Functions to report lock usage in /proc/locks */ + +static void lock_get_status(char * out, struct file_lock *fl, int id, char *pfx) { struct inode *inode; @@ -1192,21 +1358,20 @@ (IS_MANDLOCK(inode) && (inode->i_mode & (S_IXGRP | S_ISGID)) == S_ISGID) ? "MANDATORY" : "ADVISORY "); - } - else { + } else { out += sprintf(out, "FLOCK ADVISORY "); } out += sprintf(out, "%s ", (fl->fl_type == F_RDLCK) ? "READ " : "WRITE"); out += sprintf(out, "%d %s:%ld %Ld %Ld ", fl->fl_pid, kdevname(inode->i_dev), inode->i_ino, - (long long)fl->fl_start, (long long)fl->fl_end); + fl->fl_start, fl->fl_end); sprintf(out, "%08lx %08lx %08lx %08lx %08lx\n", - (long)fl, (long)fl->fl_prevlink, (long)fl->fl_nextlink, - (long)fl->fl_next, (long)fl->fl_nextblock); + (long)fl, (long)fl->fl_link.prev, (long)fl->fl_link.next, + (long)fl->fl_next, (long)fl->fl_block.next); } -static void move_lock_status(char **p, off_t* pos, off_t offset) +static void move_lock_status(char **p, off_t *pos, off_t offset) { int len; len = strlen(*p); @@ -1230,35 +1395,65 @@ int get_locks_status(char *buffer, char **start, off_t offset, int length) { - struct file_lock *fl; - struct file_lock *bfl; + struct list_head *tmp; char *q = buffer; off_t pos = 0; - int i; + int i = 0; + + acquire_fl_sem(); + tmp = file_lock_list.next; + while (tmp != &file_lock_list) { + struct file_lock *fl = list_entry(tmp, struct file_lock, fl_link); + struct list_head *btmp = fl->fl_block.next; - for (fl = file_lock_table, i = 1; fl != NULL; fl = fl->fl_nextlink, i++) { - lock_get_status(q, fl, i, ""); + lock_get_status(q, fl, ++i, ""); move_lock_status(&q, &pos, offset); - if(pos >= offset+length) + if (pos >= offset+length) goto done; - if ((bfl = fl->fl_nextblock) == NULL) - continue; - do { + while (btmp != &fl->fl_block) { + struct file_lock *bfl = list_entry(btmp, + struct file_lock, fl_block); lock_get_status(q, bfl, i, " ->"); move_lock_status(&q, &pos, offset); if(pos >= offset+length) goto done; - } while ((bfl = bfl->fl_nextblock) != fl); + btmp = btmp->next; + } + tmp = tmp->next; } done: + release_fl_sem(); *start = buffer; - if(q-buffer < length) + if (q - buffer < length) return (q-buffer); return length; } +/* The following two are for the benefit of lockd. + */ +void +posix_block_lock(struct file_lock *blocker, struct file_lock *waiter) +{ + acquire_fl_sem(); + locks_insert_block(blocker, waiter); + release_fl_sem(); +} +void +posix_unblock_lock(struct file_lock *waiter) +{ + acquire_fl_sem(); + locks_delete_block(waiter); + release_fl_sem(); +} +void __init filelock_init(void) +{ + filelock_cache = kmem_cache_create("file lock cache", + sizeof(struct file_lock), 0, 0, init_once, NULL); + if (!filelock_cache) + panic("cannot create file lock slab cache"); +} diff -urNX ../kernels/exclude linux-pre8/fs/minix/dir.c linux+lock/fs/minix/dir.c --- linux-pre8/fs/minix/dir.c Sat Feb 26 23:33:06 2000 +++ linux+lock/fs/minix/dir.c Fri Jun 2 11:22:58 2000 @@ -18,6 +18,7 @@ read: generic_read_dir, readdir: minix_readdir, fsync: file_fsync, + fasync: file_fasync; }; static int minix_readdir(struct file * filp, diff -urNX ../kernels/exclude linux-pre8/fs/namei.c linux+lock/fs/namei.c --- linux-pre8/fs/namei.c Mon May 8 18:27:12 2000 +++ linux+lock/fs/namei.c Fri Jun 2 11:22:58 2000 @@ -851,6 +851,9 @@ error = dir->i_op->create(dir, dentry, mode); exit_lock: up(&dir->i_zombie); + if (!error) { + inode_send_fasync(dir); + } return error; } @@ -997,6 +1000,11 @@ goto exit; } + /* + * Ensure there are no outstanding leases on the file. + */ + get_lease(inode, flag); + if (flag & O_TRUNC) { error = get_write_access(inode); if (error) @@ -1068,8 +1076,12 @@ DQUOT_INIT(dir); error = dir->i_op->mknod(dir, dentry, mode, dev); + exit_lock: up(&dir->i_zombie); + if (!error) { + inode_send_fasync(dir); + } return error; } @@ -1137,6 +1149,9 @@ exit_lock: up(&dir->i_zombie); + if (!error) { + inode_send_fasync(dir); + } return error; } @@ -1220,6 +1235,9 @@ dentry->d_inode->i_flags |= S_DEAD; double_up(&dir->i_zombie, &dentry->d_inode->i_zombie); dput(dentry); + if (!error) { + inode_send_fasync(dir); + } return error; } @@ -1279,6 +1297,9 @@ } } up(&dir->i_zombie); + if (!error) { + inode_send_fasync(dir); + } return error; } @@ -1345,6 +1366,9 @@ exit_lock: up(&dir->i_zombie); + if (!error) { + inode_send_fasync(dir); + } return error; } @@ -1417,6 +1441,9 @@ exit_lock: up(&dir->i_zombie); + if (!error) { + inode_send_fasync(dir); + } return error; } @@ -1615,10 +1642,16 @@ int vfs_rename(struct inode *old_dir, struct dentry *old_dentry, struct inode *new_dir, struct dentry *new_dentry) { + int error; if (S_ISDIR(old_dentry->d_inode->i_mode)) - return vfs_rename_dir(old_dir,old_dentry,new_dir,new_dentry); + error = vfs_rename_dir(old_dir,old_dentry,new_dir,new_dentry); else - return vfs_rename_other(old_dir,old_dentry,new_dir,new_dentry); + error = vfs_rename_other(old_dir,old_dentry,new_dir,new_dentry); + if (!error) { + inode_send_fasync(old_dir); + inode_send_fasync(new_dir); + } + return error; } static inline int do_rename(const char * oldname, const char * newname) diff -urNX ../kernels/exclude linux-pre8/fs/ncpfs/dir.c linux+lock/fs/ncpfs/dir.c --- linux-pre8/fs/ncpfs/dir.c Wed Mar 8 12:04:09 2000 +++ linux+lock/fs/ncpfs/dir.c Fri Jun 2 11:22:58 2000 @@ -49,6 +49,7 @@ read: generic_read_dir, readdir: ncp_readdir, ioctl: ncp_ioctl, + fasync: file_fasync, }; struct inode_operations ncp_dir_inode_operations = diff -urNX ../kernels/exclude linux-pre8/fs/nfs/dir.c linux+lock/fs/nfs/dir.c --- linux-pre8/fs/nfs/dir.c Fri Apr 21 16:36:39 2000 +++ linux+lock/fs/nfs/dir.c Fri Jun 2 11:22:58 2000 @@ -54,6 +54,7 @@ readdir: nfs_readdir, open: nfs_open, release: nfs_release, + fasync: file_fasync, }; struct inode_operations nfs_dir_inode_operations = { diff -urNX ../kernels/exclude linux-pre8/fs/ntfs/fs.c linux+lock/fs/ntfs/fs.c --- linux-pre8/fs/ntfs/fs.c Mon May 8 16:51:01 2000 +++ linux+lock/fs/ntfs/fs.c Fri Jun 2 11:22:58 2000 @@ -2,9 +2,9 @@ * fs.c * NTFS driver for Linux 2.3.x * - * Copyright (C) 1995-1997, 1999 Martin von Löwis + * Copyright (C) 1995-1997, 1999 Martin von L€wis * Copyright (C) 1996 Richard Russon - * Copyright (C) 1996-1997 Régis Duchesne + * Copyright (C) 1996-1997 R€gis Duchesne * Copyright (C) 2000, Anton Altaparmakov */ @@ -577,6 +577,7 @@ static struct file_operations ntfs_dir_operations = { read: generic_read_dir, readdir: ntfs_readdir, + fasync: file_fasync, }; static struct inode_operations ntfs_dir_inode_operations = { @@ -945,7 +946,7 @@ } EXPORT_NO_SYMBOLS; -MODULE_AUTHOR("Martin von Löwis"); +MODULE_AUTHOR("Martin von L€wis"); MODULE_DESCRIPTION("NTFS driver"); MODULE_PARM(ntdebug, "i"); MODULE_PARM_DESC(ntdebug, "Debug level"); diff -urNX ../kernels/exclude linux-pre8/fs/open.c linux+lock/fs/open.c --- linux-pre8/fs/open.c Mon May 8 16:31:40 2000 +++ linux+lock/fs/open.c Fri Jun 2 11:22:58 2000 @@ -661,6 +661,7 @@ if (error) goto cleanup_all; } + inode_send_fasync(dentry->d_parent->d_inode); f->f_flags &= ~(O_CREAT | O_EXCL | O_NOCTTY | O_TRUNC); return f; @@ -817,6 +818,7 @@ retval = 0; if (filp->f_op && filp->f_op->flush) retval = filp->f_op->flush(filp); + file_fasync(-1, filp, 0); locks_remove_posix(filp, id); fput(filp); return retval; diff -urNX ../kernels/exclude linux-pre8/fs/openpromfs/inode.c linux+lock/fs/openpromfs/inode.c --- linux-pre8/fs/openpromfs/inode.c Fri Mar 24 02:36:10 2000 +++ linux+lock/fs/openpromfs/inode.c Fri Jun 2 11:22:58 2000 @@ -565,6 +565,7 @@ static struct file_operations openprom_operations = { read: generic_read_dir, readdir: openpromfs_readdir, + fasync: file_fasync, }; static struct inode_operations openprom_alias_inode_operations = { diff -urNX ../kernels/exclude linux-pre8/fs/pipe.c linux+lock/fs/pipe.c --- linux-pre8/fs/pipe.c Mon May 8 14:17:47 2000 +++ linux+lock/fs/pipe.c Fri Jun 2 11:22:58 2000 @@ -388,6 +388,7 @@ ioctl: pipe_ioctl, open: pipe_read_open, release: pipe_read_release, + fasync: file_fasync, }; struct file_operations write_fifo_fops = { @@ -398,6 +399,7 @@ ioctl: pipe_ioctl, open: pipe_write_open, release: pipe_write_release, + fasync: file_fasync, }; struct file_operations rdwr_fifo_fops = { @@ -408,6 +410,7 @@ ioctl: pipe_ioctl, open: pipe_rdwr_open, release: pipe_rdwr_release, + fasync: file_fasync, }; struct file_operations read_pipe_fops = { @@ -418,6 +421,7 @@ ioctl: pipe_ioctl, open: pipe_read_open, release: pipe_read_release, + fasync: file_fasync, }; struct file_operations write_pipe_fops = { @@ -438,6 +442,7 @@ ioctl: pipe_ioctl, open: pipe_rdwr_open, release: pipe_rdwr_release, + fasync: file_fasync, }; struct inode* pipe_new(struct inode* inode) diff -urNX ../kernels/exclude linux-pre8/fs/proc/base.c linux+lock/fs/proc/base.c --- linux-pre8/fs/proc/base.c Tue May 9 19:19:04 2000 +++ linux+lock/fs/proc/base.c Fri Jun 2 11:22:58 2000 @@ -855,6 +855,7 @@ static struct file_operations proc_base_operations = { read: generic_read_dir, readdir: proc_base_readdir, + fasync: file_fasync, }; static struct inode_operations proc_base_inode_operations = { diff -urNX ../kernels/exclude linux-pre8/fs/proc/generic.c linux+lock/fs/proc/generic.c --- linux-pre8/fs/proc/generic.c Mon May 8 14:17:47 2000 +++ linux+lock/fs/proc/generic.c Fri Jun 2 11:22:58 2000 @@ -331,6 +331,7 @@ */ static struct file_operations proc_dir_operations = { readdir: proc_readdir, + fasync: file_fasync, }; /* diff -urNX ../kernels/exclude linux-pre8/fs/proc/root.c linux+lock/fs/proc/root.c --- linux-pre8/fs/proc/root.c Thu Apr 27 18:43:15 2000 +++ linux+lock/fs/proc/root.c Fri Jun 2 11:22:58 2000 @@ -111,7 +111,8 @@ * directory handling functions for that.. */ static struct file_operations proc_root_operations = { - readdir: proc_root_readdir, + readdir: proc_root_readdir, + fasync: file_fasync, }; /* diff -urNX ../kernels/exclude linux-pre8/fs/qnx4/dir.c linux+lock/fs/qnx4/dir.c --- linux-pre8/fs/qnx4/dir.c Sat Feb 26 23:33:07 2000 +++ linux+lock/fs/qnx4/dir.c Fri Jun 2 11:22:58 2000 @@ -83,6 +83,7 @@ read: generic_read_dir, readdir: qnx4_readdir, fsync: file_fsync, + fasync: file_fasync, }; struct inode_operations qnx4_dir_inode_operations = diff -urNX ../kernels/exclude linux-pre8/fs/ramfs/inode.c linux+lock/fs/ramfs/inode.c --- linux-pre8/fs/ramfs/inode.c Mon May 8 16:51:01 2000 +++ linux+lock/fs/ramfs/inode.c Fri Jun 2 11:22:59 2000 @@ -336,6 +336,7 @@ static struct file_operations ramfs_dir_operations = { read: generic_read_dir, readdir: ramfs_readdir, + fasync: file_fasync, }; static struct inode_operations ramfs_dir_inode_operations = { diff -urNX ../kernels/exclude linux-pre8/fs/read_write.c linux+lock/fs/read_write.c --- linux-pre8/fs/read_write.c Sun Apr 2 20:41:03 2000 +++ linux+lock/fs/read_write.c Fri Jun 2 11:22:59 2000 @@ -132,6 +132,8 @@ ret = read(file, buf, count, &file->f_pos); } } + if (!ret) + inode_send_fasync(file->f_dentry->d_parent->d_inode); fput(file); } return ret; @@ -156,6 +158,8 @@ ret = write(file, buf, count, &file->f_pos); } } + if (!ret) + inode_send_fasync(file->f_dentry->d_parent->d_inode); fput(file); } return ret; @@ -264,6 +268,8 @@ if (file->f_op && (file->f_mode & FMODE_READ) && (file->f_op->readv || file->f_op->read)) ret = do_readv_writev(VERIFY_WRITE, file, vector, count); + if (!ret) + inode_send_fasync(file->f_dentry->d_parent->d_inode); fput(file); bad_file: @@ -284,6 +290,8 @@ if (file->f_op && (file->f_mode & FMODE_WRITE) && (file->f_op->writev || file->f_op->write)) ret = do_readv_writev(VERIFY_READ, file, vector, count); + if (!ret) + inode_send_fasync(file->f_dentry->d_parent->d_inode); fput(file); bad_file: @@ -317,6 +325,8 @@ if (pos < 0) goto out; ret = read(file, buf, count, &pos); + if (!ret) + inode_send_fasync(file->f_dentry->d_parent->d_inode); out: fput(file); bad_file: @@ -347,6 +357,8 @@ goto out; ret = write(file, buf, count, &pos); + if (!ret) + inode_send_fasync(file->f_dentry->d_parent->d_inode); out: fput(file); bad_file: diff -urNX ../kernels/exclude linux-pre8/fs/romfs/inode.c linux+lock/fs/romfs/inode.c --- linux-pre8/fs/romfs/inode.c Mon May 8 17:26:13 2000 +++ linux+lock/fs/romfs/inode.c Fri Jun 2 11:22:59 2000 @@ -434,6 +434,7 @@ static struct file_operations romfs_dir_operations = { read: generic_read_dir, readdir: romfs_readdir, + fasync: file_fasync, }; static struct inode_operations romfs_dir_inode_operations = { diff -urNX ../kernels/exclude linux-pre8/fs/smbfs/dir.c linux+lock/fs/smbfs/dir.c --- linux-pre8/fs/smbfs/dir.c Wed Mar 8 12:04:09 2000 +++ linux+lock/fs/smbfs/dir.c Fri Jun 2 11:22:59 2000 @@ -35,6 +35,7 @@ readdir: smb_readdir, ioctl: smb_ioctl, open: smb_dir_open, + fasync: file_fasync, }; struct inode_operations smb_dir_inode_operations = diff -urNX ../kernels/exclude linux-pre8/fs/sysv/dir.c linux+lock/fs/sysv/dir.c --- linux-pre8/fs/sysv/dir.c Sat Feb 26 23:33:08 2000 +++ linux+lock/fs/sysv/dir.c Fri Jun 2 11:22:59 2000 @@ -25,6 +25,7 @@ read: generic_read_dir, readdir: sysv_readdir, fsync: file_fsync, + fasync: file_fasync, }; static int sysv_readdir(struct file * filp, void * dirent, filldir_t filldir) diff -urNX ../kernels/exclude linux-pre8/fs/udf/dir.c linux+lock/fs/udf/dir.c --- linux-pre8/fs/udf/dir.c Thu Mar 2 14:17:32 2000 +++ linux+lock/fs/udf/dir.c Fri Jun 2 11:22:59 2000 @@ -53,6 +53,7 @@ readdir: udf_readdir, ioctl: udf_ioctl, fsync: udf_sync_file, + fasync: file_fasync, }; /* diff -urNX ../kernels/exclude linux-pre8/fs/ufs/dir.c linux+lock/fs/ufs/dir.c --- linux-pre8/fs/ufs/dir.c Fri Mar 17 23:49:19 2000 +++ linux+lock/fs/ufs/dir.c Fri Jun 2 11:22:59 2000 @@ -181,4 +181,5 @@ read: generic_read_dir, readdir: ufs_readdir, fsync: file_fsync, + fasync: file_fasync, }; diff -urNX ../kernels/exclude linux-pre8/fs/umsdos/dir.c linux+lock/fs/umsdos/dir.c --- linux-pre8/fs/umsdos/dir.c Sun Apr 2 18:53:28 2000 +++ linux+lock/fs/umsdos/dir.c Fri Jun 2 11:22:59 2000 @@ -789,6 +789,7 @@ read: generic_read_dir, readdir: UMSDOS_readdir, ioctl: UMSDOS_ioctl_dir, + fasync: file_fasync, }; struct inode_operations umsdos_dir_inode_operations = diff -urNX ../kernels/exclude linux-pre8/fs/umsdos/rdir.c linux+lock/fs/umsdos/rdir.c --- linux-pre8/fs/umsdos/rdir.c Wed Mar 8 12:04:09 2000 +++ linux+lock/fs/umsdos/rdir.c Fri Jun 2 11:22:59 2000 @@ -223,6 +223,7 @@ read: generic_read_dir, readdir: UMSDOS_rreaddir, ioctl: UMSDOS_ioctl_dir, + fasync: file_fasync, }; struct inode_operations umsdos_rdir_inode_operations = diff -urNX ../kernels/exclude linux-pre8/include/asm-alpha/resource.h linux+lock/include/asm-alpha/resource.h --- linux-pre8/include/asm-alpha/resource.h Thu Feb 17 12:35:07 2000 +++ linux+lock/include/asm-alpha/resource.h Fri Jun 2 11:22:59 2000 @@ -15,8 +15,9 @@ #define RLIMIT_AS 7 /* address space limit(?) */ #define RLIMIT_NPROC 8 /* max number of processes */ #define RLIMIT_MEMLOCK 9 /* max locked-in-memory address space */ +#define RLIMIT_LOCKS 10 /* maximum file locks held */ -#define RLIM_NLIMITS 10 +#define RLIM_NLIMITS 11 /* * SuS says limits have to be unsigned. Fine, it's unsigned, but @@ -39,6 +40,7 @@ {LONG_MAX, LONG_MAX}, /* RLIMIT_AS */ \ {LONG_MAX, LONG_MAX}, /* RLIMIT_NPROC */ \ {LONG_MAX, LONG_MAX}, /* RLIMIT_MEMLOCK */ \ + { 100, LONG_MAX}, /* RLIMIT_LOCKS */ \ } #endif /* __KERNEL__ */ diff -urNX ../kernels/exclude linux-pre8/include/asm-arm/resource.h linux+lock/include/asm-arm/resource.h --- linux-pre8/include/asm-arm/resource.h Thu Feb 17 12:35:07 2000 +++ linux+lock/include/asm-arm/resource.h Fri Jun 2 11:22:59 2000 @@ -15,8 +15,9 @@ #define RLIMIT_NOFILE 7 /* max number of open files */ #define RLIMIT_MEMLOCK 8 /* max locked-in-memory address space */ #define RLIMIT_AS 9 /* address space limit */ +#define RLIMIT_LOCKS 10 /* maximum file locks held */ -#define RLIM_NLIMITS 10 +#define RLIM_NLIMITS 11 /* * SuS says limits have to be unsigned. @@ -38,6 +39,7 @@ { INR_OPEN, INR_OPEN }, \ { LONG_MAX, LONG_MAX }, \ { LONG_MAX, LONG_MAX }, \ + { 100, LONG_MAX}, /* RLIMIT_LOCKS */ \ } #endif /* __KERNEL__ */ diff -urNX ../kernels/exclude linux-pre8/include/asm-i386/fcntl.h linux+lock/include/asm-i386/fcntl.h --- linux-pre8/include/asm-i386/fcntl.h Tue Dec 14 03:53:27 1999 +++ linux+lock/include/asm-i386/fcntl.h Fri Jun 2 11:22:59 2000 @@ -34,6 +34,9 @@ #define F_GETOWN 9 /* for sockets. */ #define F_SETSIG 10 /* for sockets. */ #define F_GETSIG 11 /* for sockets. */ +#define F_SETLEASE 12 +#define F_GETLEASE 13 + /* for F_[GET|SET]FL */ #define FD_CLOEXEC 1 /* actually anything with low bit set goes */ @@ -46,6 +49,9 @@ /* for old implementation of bsd flock () */ #define F_EXLCK 4 /* or 3 */ #define F_SHLCK 8 /* or 4 */ + +/* for leases */ +#define F_INPROGRESS 16 /* operations for bsd flock(), also used by the kernel implementation */ #define LOCK_SH 1 /* shared lock */ diff -urNX ../kernels/exclude linux-pre8/include/asm-i386/resource.h linux+lock/include/asm-i386/resource.h --- linux-pre8/include/asm-i386/resource.h Thu Feb 17 12:35:07 2000 +++ linux+lock/include/asm-i386/resource.h Fri Jun 2 11:22:59 2000 @@ -15,8 +15,9 @@ #define RLIMIT_NOFILE 7 /* max number of open files */ #define RLIMIT_MEMLOCK 8 /* max locked-in-memory address space */ #define RLIMIT_AS 9 /* address space limit */ +#define RLIMIT_LOCKS 10 /* maximum file locks held */ -#define RLIM_NLIMITS 10 +#define RLIM_NLIMITS 11 /* * SuS says limits have to be unsigned. @@ -38,6 +39,7 @@ { INR_OPEN, INR_OPEN }, \ { RLIM_INFINITY, RLIM_INFINITY }, \ { RLIM_INFINITY, RLIM_INFINITY }, \ + { 100, RLIM_INFINITY }, /* RLIMIT_LOCKS */ \ } #endif /* __KERNEL__ */ diff -urNX ../kernels/exclude linux-pre8/include/asm-m68k/resource.h linux+lock/include/asm-m68k/resource.h --- linux-pre8/include/asm-m68k/resource.h Thu Feb 17 12:35:07 2000 +++ linux+lock/include/asm-m68k/resource.h Fri Jun 2 11:22:59 2000 @@ -15,8 +15,9 @@ #define RLIMIT_NOFILE 7 /* max number of open files */ #define RLIMIT_MEMLOCK 8 /* max locked-in-memory address space*/ #define RLIMIT_AS 9 /* address space limit */ +#define RLIMIT_LOCKS 10 /* maximum file locks held */ -#define RLIM_NLIMITS 10 +#define RLIM_NLIMITS 11 /* * SuS says limits have to be unsigned. @@ -38,6 +39,7 @@ {INR_OPEN, INR_OPEN}, \ {LONG_MAX, LONG_MAX}, \ {LONG_MAX, LONG_MAX} \ + { 100, LONG_MAX}, /* RLIMIT_LOCKS */ \ } #endif /* __KERNEL__ */ diff -urNX ../kernels/exclude linux-pre8/include/asm-mips/resource.h linux+lock/include/asm-mips/resource.h --- linux-pre8/include/asm-mips/resource.h Fri Feb 25 01:52:30 2000 +++ linux+lock/include/asm-mips/resource.h Fri Jun 2 11:22:59 2000 @@ -22,8 +22,9 @@ #define RLIMIT_RSS 7 /* max resident set size */ #define RLIMIT_NPROC 8 /* max number of processes */ #define RLIMIT_MEMLOCK 9 /* max locked-in-memory address space */ +#define RLIMIT_LOCKS 10 /* maximum file locks held */ -#define RLIM_NLIMITS 10 /* Number of limit flavors. */ +#define RLIM_NLIMITS 11 /* Number of limit flavors. */ /* * SuS says limits have to be unsigned. @@ -45,6 +46,7 @@ { RLIM_INFINITY, RLIM_INFINITY }, \ { 0, 0 }, \ { RLIM_INFINITY, RLIM_INFINITY }, \ + { 100, RLIM_INFINITY }, \ } #endif /* __KERNEL__ */ diff -urNX ../kernels/exclude linux-pre8/include/asm-ppc/resource.h linux+lock/include/asm-ppc/resource.h --- linux-pre8/include/asm-ppc/resource.h Tue May 2 16:05:40 2000 +++ linux+lock/include/asm-ppc/resource.h Fri Jun 2 11:22:59 2000 @@ -11,8 +11,9 @@ #define RLIMIT_NOFILE 7 /* max number of open files */ #define RLIMIT_MEMLOCK 8 /* max locked-in-memory address space */ #define RLIMIT_AS 9 /* address space limit(?) */ +#define RLIMIT_LOCKS 10 /* maximum file locks held */ -#define RLIM_NLIMITS 10 +#define RLIM_NLIMITS 11 #ifdef __KERNEL__ @@ -35,6 +36,7 @@ { INR_OPEN, INR_OPEN }, \ { RLIM_INFINITY, RLIM_INFINITY }, \ { RLIM_INFINITY, RLIM_INFINITY }, \ + { 100, RLIM_INFINITY}, /* RLIMIT_LOCKS */ \ } #endif /* __KERNEL__ */ diff -urNX ../kernels/exclude linux-pre8/include/asm-sh/resource.h linux+lock/include/asm-sh/resource.h --- linux-pre8/include/asm-sh/resource.h Sun Mar 5 12:33:55 2000 +++ linux+lock/include/asm-sh/resource.h Fri Jun 2 11:22:59 2000 @@ -15,8 +15,9 @@ #define RLIMIT_NOFILE 7 /* max number of open files */ #define RLIMIT_MEMLOCK 8 /* max locked-in-memory address space */ #define RLIMIT_AS 9 /* address space limit */ +#define RLIMIT_LOCKS 10 /* maximum file locks held */ -#define RLIM_NLIMITS 10 +#define RLIM_NLIMITS 11 #ifdef __KERNEL__ @@ -38,6 +39,7 @@ { INR_OPEN, INR_OPEN }, \ { RLIM_INFINITY, RLIM_INFINITY }, \ { RLIM_INFINITY, RLIM_INFINITY }, \ + { 100, RLIM_INFINITY }, /* RLIMIT_LOCKS */ \ } #endif /* __KERNEL__ */ diff -urNX ../kernels/exclude linux-pre8/include/asm-sparc/resource.h linux+lock/include/asm-sparc/resource.h --- linux-pre8/include/asm-sparc/resource.h Thu Feb 17 12:35:07 2000 +++ linux+lock/include/asm-sparc/resource.h Fri Jun 2 11:22:59 2000 @@ -21,8 +21,9 @@ #define RLIMIT_NPROC 7 /* max number of processes */ #define RLIMIT_MEMLOCK 8 /* max locked-in-memory address space */ #define RLIMIT_AS 9 /* address space limit */ +#define RLIMIT_LOCKS 10 /* maximum file locks held */ -#define RLIM_NLIMITS 10 +#define RLIM_NLIMITS 11 /* * SuS says limits have to be unsigned. @@ -43,6 +44,7 @@ {INR_OPEN, INR_OPEN}, {0, 0}, \ {RLIM_INFINITY, RLIM_INFINITY}, \ {RLIM_INFINITY, RLIM_INFINITY} \ + { 100, RLIM_INFINITY}, /* RLIMIT_LOCKS */ \ } #endif /* __KERNEL__ */ diff -urNX ../kernels/exclude linux-pre8/include/asm-sparc64/resource.h linux+lock/include/asm-sparc64/resource.h --- linux-pre8/include/asm-sparc64/resource.h Thu Feb 17 12:35:07 2000 +++ linux+lock/include/asm-sparc64/resource.h Fri Jun 2 11:22:59 2000 @@ -21,8 +21,9 @@ #define RLIMIT_NPROC 7 /* max number of processes */ #define RLIMIT_MEMLOCK 8 /* max locked-in-memory address space */ #define RLIMIT_AS 9 /* address space limit */ +#define RLIMIT_LOCKS 10 /* maximum file locks held */ -#define RLIM_NLIMITS 10 +#define RLIM_NLIMITS 11 /* * SuS says limits have to be unsigned. @@ -42,6 +43,7 @@ {INR_OPEN, INR_OPEN}, {0, 0}, \ {RLIM_INFINITY, RLIM_INFINITY}, \ {RLIM_INFINITY, RLIM_INFINITY} \ + { 100, RLIM_INFINITY}, /* RLIMIT_LOCKS */ \ } #endif /* __KERNEL__ */ diff -urNX ../kernels/exclude linux-pre8/include/linux/fs.h linux+lock/include/linux/fs.h --- linux-pre8/include/linux/fs.h Fri May 12 15:16:13 2000 +++ linux+lock/include/linux/fs.h Fri Jun 2 11:36:32 2000 @@ -187,6 +187,7 @@ extern void buffer_init(unsigned long); extern void inode_init(unsigned long); extern void file_table_init(void); +extern void filelock_init(void); extern void dcache_init(unsigned long); /* bh state bits */ @@ -402,6 +403,7 @@ struct dquot *i_dquot[MAXQUOTAS]; struct pipe_inode_info *i_pipe; struct block_device *i_bdev; + struct fasync_struct *i_fasync; unsigned long i_state; @@ -492,6 +494,7 @@ #define FL_BROKEN 4 /* broken flock() emulation */ #define FL_ACCESS 8 /* for processes suspended by mandatory locking */ #define FL_LOCKD 16 /* lock held by rpc.lockd */ +#define FL_LEASE 32 /* lease held on this file */ /* * The POSIX file lock owner is determined by @@ -503,19 +506,17 @@ typedef struct files_struct *fl_owner_t; struct file_lock { - struct file_lock *fl_next; /* singly linked list for this inode */ - struct file_lock *fl_nextlink; /* doubly linked list of all locks */ - struct file_lock *fl_prevlink; /* used to simplify lock removal */ - struct file_lock *fl_nextblock; /* circular list of blocked processes */ - struct file_lock *fl_prevblock; - fl_owner_t fl_owner; - unsigned int fl_pid; - wait_queue_head_t fl_wait; - struct file *fl_file; - unsigned char fl_flags; - unsigned char fl_type; - loff_t fl_start; - loff_t fl_end; + struct file_lock * fl_next; /* Single list for this inode */ + struct list_head fl_link; /* Circular list of all locks */ + struct list_head fl_block; /* Circular list of blocked locks */ + fl_owner_t fl_owner; /* Thread group for POSIX locks */ + unsigned int fl_pid; + wait_queue_head_t fl_wait; /* Blocked locks wait on this */ + struct file * fl_file; /* File this lock belongs to */ + unsigned char fl_flags; /* See above for flag values */ + unsigned char fl_type; /* POSIX or BSD lock */ + loff_t fl_start; /* First byte locked */ + loff_t fl_end; /* Last byte locked */ void (*fl_notify)(struct file_lock *); /* unblock callback */ void (*fl_insert)(struct file_lock *); /* lock insertion callback */ @@ -532,20 +533,24 @@ #define OFFSET_MAX INT_LIMIT(loff_t) #endif -extern struct file_lock *file_lock_table; - #include extern int fcntl_getlk(unsigned int, struct flock *); extern int fcntl_setlk(unsigned int, unsigned int, struct flock *); /* fs/locks.c */ +extern struct semaphore file_lock_sem; +extern struct list_head file_lock_list; + extern void locks_remove_posix(struct file *, fl_owner_t); extern void locks_remove_flock(struct file *); extern struct file_lock *posix_test_lock(struct file *, struct file_lock *); extern int posix_lock_file(struct file *, struct file_lock *, unsigned int); extern void posix_block_lock(struct file_lock *, struct file_lock *); extern void posix_unblock_lock(struct file_lock *); +extern int __get_lease(struct inode *inode, unsigned int flags); + +#define FASYNC_MAGIC 0x4601 struct fasync_struct { int magic; @@ -554,6 +559,16 @@ struct file *fa_file; }; +extern void __kill_fasync(struct fasync_struct *, int, int); +extern void kill_fasync(struct fasync_struct **, int, int); +extern int file_fasync(int, struct file *, int); +#define inode_send_fasync(inode) \ + kill_fasync(&inode->i_fasync, SIGIO, POLL_MSG) +#define tty_send_fasync(fa) \ + kill_fasync(fa, SIGIO, POLL_IN) + +extern int fasync_helper(int, struct file *, int, struct fasync_struct **); + struct nameidata { struct dentry *dentry; struct vfsmount *mnt; @@ -562,10 +577,6 @@ int last_type; }; -#define FASYNC_MAGIC 0x4601 - -extern int fasync_helper(int, struct file *, int, struct fasync_struct **); - #define DQUOT_USR_ENABLED 0x01 /* User diskquotas enabled */ #define DQUOT_GRP_ENABLED 0x02 /* Group diskquotas enabled */ @@ -815,7 +826,7 @@ #define MANDATORY_LOCK(inode) \ (IS_MANDLOCK(inode) && ((inode)->i_mode & (S_ISGID | S_IXGRP)) == S_ISGID) -static inline int locks_verify_locked(struct inode *inode) +extern inline int locks_verify_locked(struct inode *inode) { if (MANDATORY_LOCK(inode)) return locks_mandatory_locked(inode); @@ -844,6 +855,12 @@ return 0; } +extern inline int get_lease(struct inode *inode, unsigned int mode) +{ + if (inode->i_flock && (inode->i_flock->fl_flags & FL_LEASE)) + return __get_lease(inode, mode); + return 0; +} /* fs/open.c */ @@ -863,7 +880,6 @@ #define putname(name) free_page((unsigned long)(name)) enum {BDEV_FILE, BDEV_SWAP, BDEV_FS, BDEV_RAW}; -extern void kill_fasync(struct fasync_struct *, int, int); extern int register_blkdev(unsigned int, const char *, struct block_device_operations *); extern int unregister_blkdev(unsigned int, const char *); extern struct block_device *bdget(dev_t); diff -urNX ../kernels/exclude linux-pre8/include/linux/sched.h linux+lock/include/linux/sched.h --- linux-pre8/include/linux/sched.h Fri May 12 15:16:14 2000 +++ linux+lock/include/linux/sched.h Fri Jun 2 11:36:32 2000 @@ -325,6 +325,7 @@ /* file system info */ int link_count; struct tty_struct *tty; /* NULL if no tty */ + unsigned int locks; /* How many file locks are being held */ /* ipc stuff */ struct sem_undo *semundo; struct sem_queue *semsleeping; diff -urNX ../kernels/exclude linux-pre8/include/linux/version.h linux+lock/include/linux/version.h --- linux-pre8/include/linux/version.h Wed Dec 31 19:00:00 1969 +++ linux+lock/include/linux/version.h Fri Jun 2 11:23:15 2000 @@ -0,0 +1,3 @@ +#define UTS_RELEASE "2.3.99-pre8" +#define LINUX_VERSION_CODE 131939 +#define KERNEL_VERSION(a,b,c) (((a) << 16) + ((b) << 8) + (c)) diff -urNX ../kernels/exclude linux-pre8/init/main.c linux+lock/init/main.c --- linux-pre8/init/main.c Fri May 12 14:21:20 2000 +++ linux+lock/init/main.c Fri Jun 2 11:23:09 2000 @@ -93,6 +93,7 @@ extern void ppc_init(void); extern void sysctl_init(void); extern void filescache_init(void); +extern void fasync_init(void); extern void signals_init(void); extern void bdev_init(void); extern int init_pcmcia_ds(void); @@ -577,6 +578,8 @@ bdev_init(); inode_init(mempages); file_table_init(); + filelock_init(); + fasync_init(); #if defined(CONFIG_SYSVIPC) ipc_init(); #endif diff -urNX ../kernels/exclude linux-pre8/kernel/ksyms.c linux+lock/kernel/ksyms.c --- linux-pre8/kernel/ksyms.c Tue May 9 01:21:57 2000 +++ linux+lock/kernel/ksyms.c Fri Jun 2 11:23:09 2000 @@ -210,7 +210,8 @@ EXPORT_SYMBOL(generic_buffer_fdatasync); EXPORT_SYMBOL(page_hash_bits); EXPORT_SYMBOL(page_hash_table); -EXPORT_SYMBOL(file_lock_table); +EXPORT_SYMBOL(file_lock_list); +EXPORT_SYMBOL(file_lock_sem); EXPORT_SYMBOL(posix_lock_file); EXPORT_SYMBOL(posix_test_lock); EXPORT_SYMBOL(posix_block_lock); .