s3:smbd: make kernel oplocks event driven
[metze/samba/wip.git] / source3 / smbd / oplock.c
index 23411294df508b2bb4af2ae0db1e00c80560ebb0..788d2f7238cbf88a87ec9244d07dd84736f4b520 100644 (file)
 
 #define DBGC_CLASS DBGC_LOCKING
 #include "includes.h"
-
-/* Current number of oplocks we have outstanding. */
-static int32 exclusive_oplocks_open = 0;
-static int32 level_II_oplocks_open = 0;
-bool global_client_failed_oplock_break = False;
-
-extern uint32 global_client_caps;
-
-static struct kernel_oplocks *koplocks;
+#include "smbd/globals.h"
 
 /****************************************************************************
  Get the number of current exclusive oplocks.
@@ -40,61 +32,23 @@ int32 get_number_of_exclusive_open_oplocks(void)
   return exclusive_oplocks_open;
 }
 
-/****************************************************************************
- Return True if an oplock message is pending.
-****************************************************************************/
-
-bool oplock_message_waiting(fd_set *fds)
-{
-       if (koplocks && koplocks->msg_waiting(fds)) {
-               return True;
-       }
-
-       return False;
-}
-
-/****************************************************************************
- Find out if there are any kernel oplock messages waiting and process them
- if so. pfds is the fd_set from the main select loop (which contains any
- kernel oplock fd if that's what the system uses (IRIX). If may be NULL if
- we're calling this in a shutting down state.
-****************************************************************************/
-
-void process_kernel_oplocks(struct messaging_context *msg_ctx, fd_set *pfds)
+/*
+ * helper function used by the kernel oplock backends to post the break message
+ */
+void break_kernel_oplock(struct messaging_context *msg_ctx, files_struct *fsp)
 {
-       /*
-        * 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) {
-               return;
-       }
-
-       while (koplocks->msg_waiting(pfds)) { 
-               files_struct *fsp;
-               char msg[MSG_SMB_KERNEL_BREAK_SIZE];
-
-               fsp = koplocks->receive_message(pfds);
-
-               if (fsp == NULL) {
-                       DEBUG(3, ("Kernel oplock message announced, but none "
-                                 "received\n"));
-                       return;
-               }
+       uint8_t msg[MSG_SMB_KERNEL_BREAK_SIZE];
 
-               /* Put the kernel break info into the message. */
-               push_file_id_16(msg, &fsp->file_id);
-               SIVAL(msg,16,fsp->fh->gen_id);
+       /* Put the kernel break info into the message. */
+       push_file_id_16((char *)msg, &fsp->file_id);
+       SIVAL(msg,16,fsp->fh->gen_id);
 
-               /* Don't need to be root here as we're only ever
-                  sending to ourselves. */
+       /* Don't need to be root here as we're only ever
+          sending to ourselves. */
 
-               messaging_send_buf(msg_ctx, procid_self(),
-                                  MSG_SMB_KERNEL_BREAK,
-                                  (uint8 *)&msg, MSG_SMB_KERNEL_BREAK_SIZE);
-       }
+       messaging_send_buf(msg_ctx, procid_self(),
+                          MSG_SMB_KERNEL_BREAK,
+                          msg, MSG_SMB_KERNEL_BREAK_SIZE);
 }
 
 /****************************************************************************
@@ -104,7 +58,10 @@ void process_kernel_oplocks(struct messaging_context *msg_ctx, fd_set *pfds)
 
 bool set_file_oplock(files_struct *fsp, int oplock_type)
 {
-       if (koplocks && !koplocks->set_oplock(fsp, oplock_type)) {
+       if ((fsp->oplock_type != NO_OPLOCK) &&
+           (fsp->oplock_type != FAKE_LEVEL_II_OPLOCK) &&
+           koplocks &&
+           !koplocks->ops->set_oplock(koplocks, fsp, oplock_type)) {
                return False;
        }
 
@@ -112,7 +69,7 @@ bool set_file_oplock(files_struct *fsp, int oplock_type)
        fsp->sent_oplock_break = NO_BREAK_SENT;
        if (oplock_type == LEVEL_II_OPLOCK) {
                level_II_oplocks_open++;
-       } else {
+       } else if (EXCLUSIVE_OPLOCK_TYPE(fsp->oplock_type)) {
                exclusive_oplocks_open++;
        }
 
@@ -134,7 +91,7 @@ void release_file_oplock(files_struct *fsp)
        if ((fsp->oplock_type != NO_OPLOCK) &&
            (fsp->oplock_type != FAKE_LEVEL_II_OPLOCK) &&
            koplocks) {
-               koplocks->release_oplock(fsp);
+               koplocks->ops->release_oplock(koplocks, fsp);
        }
 
        if (fsp->oplock_type == LEVEL_II_OPLOCK) {
@@ -145,10 +102,15 @@ void release_file_oplock(files_struct *fsp)
 
        SMB_ASSERT(exclusive_oplocks_open>=0);
        SMB_ASSERT(level_II_oplocks_open>=0);
-       
-       fsp->oplock_type = NO_OPLOCK;
+
+       if (EXCLUSIVE_OPLOCK_TYPE(fsp->oplock_type)) {
+               /* This doesn't matter for close. */
+               fsp->oplock_type = FAKE_LEVEL_II_OPLOCK;
+       } else {
+               fsp->oplock_type = NO_OPLOCK;
+       }
        fsp->sent_oplock_break = NO_BREAK_SENT;
-       
+
        flush_write_cache(fsp, OPLOCK_RELEASE_FLUSH);
 
        TALLOC_FREE(fsp->oplock_timeout);
@@ -161,7 +123,7 @@ void release_file_oplock(files_struct *fsp)
 static void downgrade_file_oplock(files_struct *fsp)
 {
        if (koplocks) {
-               koplocks->release_oplock(fsp);
+               koplocks->ops->release_oplock(koplocks, fsp);
        }
        fsp->oplock_type = LEVEL_II_OPLOCK;
        exclusive_oplocks_open--;
@@ -226,19 +188,6 @@ bool downgrade_oplock(files_struct *fsp)
        return ret;
 }
 
-/****************************************************************************
- Return the fd (if any) used for receiving oplock notifications.
-****************************************************************************/
-
-int oplock_notify_fd(void)
-{
-       if (koplocks) {
-               return koplocks->notification_fd;
-       }
-
-       return -1;
-}
-
 /****************************************************************************
  Set up an oplock break message.
 ****************************************************************************/
@@ -338,7 +287,7 @@ static files_struct *initial_break_processing(struct file_id id, unsigned long f
 
 static void oplock_timeout_handler(struct event_context *ctx,
                                   struct timed_event *te,
-                                  const struct timeval *now,
+                                  struct timeval now,
                                   void *private_data)
 {
        files_struct *fsp = (files_struct *)private_data;
@@ -365,7 +314,6 @@ static void add_oplock_timeout_handler(files_struct *fsp)
        fsp->oplock_timeout =
                event_add_timed(smbd_event_context(), NULL,
                                timeval_current_ofs(OPLOCK_BREAK_TIMEOUT, 0),
-                               "oplock_timeout_handler",
                                oplock_timeout_handler, fsp);
 
        if (fsp->oplock_timeout == NULL) {
@@ -435,6 +383,11 @@ static void process_oplock_async_level2_break_message(struct messaging_context *
        /* Ensure we're really at level2 state. */
        SMB_ASSERT(fsp->oplock_type == LEVEL_II_OPLOCK);
 
+       DEBUG(10,("process_oplock_async_level2_break_message: sending break to "
+               "none message for fid %d, file %s\n",
+               fsp->fnum,
+               fsp->fsp_name));
+
        /* Now send a break to none message to our client. */
 
        break_msg = new_break_smb_message(NULL, fsp, OPLOCKLEVEL_NONE);
@@ -808,10 +761,33 @@ void release_level_2_oplocks_on_change(files_struct *fsp)
 
                share_mode_entry_to_message(msg, share_entry);
 
-               messaging_send_buf(smbd_messaging_context(), share_entry->pid,
-                                  MSG_SMB_ASYNC_LEVEL2_BREAK,
-                                  (uint8 *)msg,
-                                  MSG_SMB_SHARE_MODE_ENTRY_SIZE);
+               /*
+                * Deal with a race condition when breaking level2
+                * oplocks. Don't send all the messages and release
+                * the lock, this allows someone else to come in and
+                * get a level2 lock before any of the messages are
+                * processed, and thus miss getting a break message.
+                * Ensure at least one entry (the one we're breaking)
+                * is processed immediately under the lock and becomes
+                * set as NO_OPLOCK to stop any waiter getting a level2.
+                * Bugid #5980.
+                */
+
+               if (procid_is_me(&share_entry->pid)) {
+                       DATA_BLOB blob = data_blob_const(msg,
+                                       MSG_SMB_SHARE_MODE_ENTRY_SIZE);
+                       process_oplock_async_level2_break_message(smbd_messaging_context(),
+                                               NULL,
+                                               MSG_SMB_ASYNC_LEVEL2_BREAK,
+                                               share_entry->pid,
+                                               &blob);
+               } else {
+                       messaging_send_buf(smbd_messaging_context(),
+                                       share_entry->pid,
+                                       MSG_SMB_ASYNC_LEVEL2_BREAK,
+                                       (uint8 *)msg,
+                                       MSG_SMB_SHARE_MODE_ENTRY_SIZE);
+               }
        }
 
        /* We let the message receivers handle removing the oplock state
@@ -887,9 +863,9 @@ bool init_oplocks(struct messaging_context *msg_ctx)
 
        if (lp_kernel_oplocks()) {
 #if HAVE_KERNEL_OPLOCKS_IRIX
-               koplocks = irix_init_kernel_oplocks();
+               koplocks = irix_init_kernel_oplocks(talloc_autofree_context());
 #elif HAVE_KERNEL_OPLOCKS_LINUX
-               koplocks = linux_init_kernel_oplocks();
+               koplocks = linux_init_kernel_oplocks(talloc_autofree_context());
 #endif
        }