Overly complex but neccessary fix for kernel oplock problems. The issue
authorJeremy Allison <jra@samba.org>
Thu, 6 Jun 2002 23:55:41 +0000 (23:55 +0000)
committerJeremy Allison <jra@samba.org>
Thu, 6 Jun 2002 23:55:41 +0000 (23:55 +0000)
is that there are some times when we should return an EINTR from a select,
some times when we should not. As we can take a signal at any time, we
have to eat EINTR's in some selects. This means we need to check for
kernel oplock breaks more often in the main loop, as well as add the
queuing mechanism needed for the changenotify code (due to the mistake
in understanding POSIX semantics w.r.t. setting a signal mask in a
signal handler). This code now passes all my tests.
However, (and IMHO and I know tridge disagrees) - the correct way to
fix this is to run with RT signals blocked and explicitly unblock
them just before the main select, block them after and then process
them all in one place. Just my 2cents :-).
Jeremy.

source/smbd/oplock.c
source/smbd/oplock_linux.c
source/smbd/process.c

index f20885a7e17f8feb717cad1ab5393b3df6d7f46a..14b243b36edeca15c8b549130d540b1f5da296ac 100644 (file)
@@ -83,6 +83,16 @@ BOOL receive_local_message( char *buffer, int buffer_len, int timeout)
        FD_ZERO(&fds);
        smb_read_error = 0;
 
+       /*
+        * We need to check for kernel oplocks before going into the select
+        * here, as the EINTR generated by the linux kernel oplock may have
+        * already been eaten. JRA.
+        */
+
+       if (koplocks && koplocks->msg_waiting(&fds)) {
+               return koplocks->receive_message(&fds, buffer, buffer_len);
+       }
+
        while (timeout > 0 && selrtn == -1) {
                struct timeval to;
                int maxfd = oplock_sock;
index 372ad857c400e1c48ef48c7abdc0afcdd3e87cf7..459d987272cde86aa48b9d3ac7a9a958fb628833 100644 (file)
@@ -23,8 +23,8 @@
 #if HAVE_KERNEL_OPLOCKS_LINUX
 
 static VOLATILE sig_atomic_t signals_received;
-static VOLATILE sig_atomic_t signals_processed;
-static VOLATILE sig_atomic_t fd_pending; /* the fd of the current pending signal */
+#define FD_PENDING_SIZE 100
+static VOLATILE sig_atomic_t fd_pending_array[FD_PENDING_SIZE];
 
 #ifndef F_SETLEASE
 #define F_SETLEASE     1024
@@ -52,9 +52,10 @@ static VOLATILE sig_atomic_t fd_pending; /* the fd of the current pending signal
 
 static void signal_handler(int sig, siginfo_t *info, void *unused)
 {
-       BlockSignals(True, sig);
-       fd_pending = (sig_atomic_t)info->si_fd;
-       signals_received++;
+       if (signals_received < FD_PENDING_SIZE - 1) {
+               fd_pending_array[signals_received] = (sig_atomic_t)info->si_fd;
+               signals_received++;
+       } /* Else signal is lost. */
        sys_select_signal();
 }
 
@@ -124,20 +125,28 @@ static int linux_setlease(int fd, int leasetype)
 
 static BOOL linux_oplock_receive_message(fd_set *fds, char *buffer, int buffer_len)
 {
-       BOOL ret = True;
+       int fd;
        struct files_struct *fsp;
 
-       if (signals_received == signals_processed)
-               return False;
+       BlockSignals(True, RT_SIGNAL_LEASE);
+       fd = fd_pending_array[0];
+       fsp = file_find_fd(fd);
+       fd_pending_array[0] = (sig_atomic_t)-1;
+       if (signals_received > 1)
+               memmove(&fd_pending_array[0], &fd_pending_array[1],
+                       sizeof(sig_atomic_t)*(signals_received-1));
+       signals_received--;
+       /* now we can receive more signals */
+       BlockSignals(False, RT_SIGNAL_LEASE);
 
-       if ((fsp = file_find_fd(fd_pending)) == NULL) {
-               DEBUG(0,("Invalid file descriptor %d in kernel oplock break!\n", (int)fd_pending));
-               ret = False;
-               goto out;
+       if (fsp == NULL) {
+               DEBUG(0,("Invalid file descriptor %d in kernel oplock break!\n", (int)fd));
+               return False;
        }
 
        DEBUG(3,("linux_oplock_receive_message: kernel oplock break request received for \
-dev = %x, inode = %.0f\n", (unsigned int)fsp->dev, (double)fsp->inode ));
+dev = %x, inode = %.0f fd = %d, fileid = %lu \n", (unsigned int)fsp->dev, (double)fsp->inode,
+                       fd, fsp->file_id));
      
        /*
         * Create a kernel oplock break message.
@@ -155,13 +164,7 @@ dev = %x, inode = %.0f\n", (unsigned int)fsp->dev, (double)fsp->inode ));
        memcpy(buffer + KERNEL_OPLOCK_BREAK_INODE_OFFSET, (char *)&fsp->inode, sizeof(fsp->inode));     
        memcpy(buffer + KERNEL_OPLOCK_BREAK_FILEID_OFFSET, (char *)&fsp->file_id, sizeof(fsp->file_id));        
 
- out:
-       /* now we can receive more signals */
-       fd_pending = (sig_atomic_t)-1;
-       signals_processed++;
-       BlockSignals(False, RT_SIGNAL_LEASE);
-     
-       return ret;
+       return True;
 }
 
 /****************************************************************************
@@ -244,7 +247,7 @@ static BOOL linux_kernel_oplock_parse(char *msg_start, int msg_len, SMB_INO_T *i
 
 static BOOL linux_oplock_msg_waiting(fd_set *fds)
 {
-       return signals_processed != signals_received;
+       return signals_received != 0;
 }
 
 /****************************************************************************
index 010b18870138c5d9903797fc5803f1d00aa9aeb8..0cfb4a626460184a2c0d3221a09072a3c987f5f3 100644 (file)
@@ -195,6 +195,27 @@ static BOOL receive_message_or_smb(char *buffer, int buffer_len, int timeout)
         */
 
        FD_ZERO(&fds);
+
+       /*
+        * Ensure we process oplock break messages by preference.
+        * We have to do this before the select, after the select
+        * and if the select returns EINTR. This is due to the fact
+        * that the selects called from async_processing can eat an EINTR
+        * caused by a signal (we can't take the break message there).
+        * This is hideously complex - *MUST* be simplified for 3.0 ! JRA.
+        */
+
+       if (oplock_message_waiting(&fds)) {
+               DEBUG(10,("receive_message_or_smb: oplock_message is waiting.\n"));
+               async_processing(buffer, buffer_len);
+               /*
+                * After async processing we must go and do the select again, as
+                * the state of the flag in fds for the server file descriptor is
+                * indeterminate - we may have done I/O on it in the oplock processing. JRA.
+                */
+               goto again;
+       }
+       
        FD_SET(smbd_server_fd(),&fds);
        maxfd = setup_oplock_select_set(&fds);