smbd: Use MSG_SMB_BREAK_REQUEST for async l2 breaks
[metze/samba/wip.git] / source3 / smbd / oplock.c
index ffea57bd1d4869da715a3fe7b90cdfc64274fb58..3eaf1268b87382f35f323dafeb3709fa97c49e8b 100644 (file)
@@ -409,36 +409,6 @@ static void send_break_message_smb1(files_struct *fsp, int level)
        }
 }
 
-static void break_level2_to_none_async(files_struct *fsp)
-{
-       struct smbd_server_connection *sconn = fsp->conn->sconn;
-
-       if (fsp->oplock_type == NO_OPLOCK) {
-               /* We already got a "break to none" message and we've handled
-                * it.  just ignore. */
-               DEBUG(3, ("process_oplock_async_level2_break_message: already "
-                         "broken to none, ignoring.\n"));
-               return;
-       }
-
-       /* 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 %s, file %s\n", fsp_fnum_dbg(fsp),
-                 fsp_str_dbg(fsp)));
-
-       /* Now send a break to none message to our client. */
-       if (sconn->using_smb2) {
-               send_break_message_smb2(fsp, OPLOCKLEVEL_NONE);
-       } else {
-               send_break_message_smb1(fsp, OPLOCKLEVEL_NONE);
-       }
-
-       /* Async level2 request, don't send a reply, just remove the oplock. */
-       remove_oplock(fsp);
-}
-
 /*******************************************************************
  This handles the case of a write triggering a break to none
  message on a level2 oplock.
@@ -458,6 +428,7 @@ static void process_oplock_async_level2_break_message(struct messaging_context *
        struct smbd_server_connection *sconn =
                talloc_get_type_abort(private_data,
                struct smbd_server_connection);
+       struct server_id self = messaging_server_id(sconn->msg_ctx);
 
        if (data->data == NULL) {
                DEBUG(0, ("Got NULL buffer\n"));
@@ -487,7 +458,37 @@ static void process_oplock_async_level2_break_message(struct messaging_context *
                return;
        }
 
-       break_level2_to_none_async(fsp);
+
+       if (fsp->oplock_type == NO_OPLOCK) {
+               /* We already got a "break to none" message and we've handled
+                * it.  just ignore. */
+               DEBUG(3, ("process_oplock_async_level2_break_message: already "
+                         "broken to none, ignoring.\n"));
+               return;
+       }
+
+       /* 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 %s, file %s\n", fsp_fnum_dbg(fsp),
+                 fsp_str_dbg(fsp)));
+
+       /* Need to wait before sending a break
+          message if we sent ourselves this message. */
+       if (serverid_equal(&self, &src)) {
+               wait_before_sending_break();
+       }
+
+       /* Now send a break to none message to our client. */
+       if (sconn->using_smb2) {
+               send_break_message_smb2(fsp, OPLOCKLEVEL_NONE);
+       } else {
+               send_break_message_smb1(fsp, OPLOCKLEVEL_NONE);
+       }
+
+       /* Async level2 request, don't send a reply, just remove the oplock. */
+       remove_oplock(fsp);
 }
 
 /*******************************************************************
@@ -509,6 +510,7 @@ static void process_oplock_break_message(struct messaging_context *msg_ctx,
                struct smbd_server_connection);
        struct server_id self = messaging_server_id(sconn->msg_ctx);
        struct kernel_oplocks *koplocks = sconn->oplocks.kernel_ops;
+       uint16_t break_to;
 
        if (data->data == NULL) {
                DEBUG(0, ("Got NULL buffer\n"));
@@ -522,9 +524,10 @@ static void process_oplock_break_message(struct messaging_context *msg_ctx,
 
        /* De-linearize incoming message. */
        message_to_share_mode_entry(&msg, (char *)data->data);
+       break_to = msg.op_type;
 
-       DEBUG(10, ("Got oplock break message from pid %s: %s/%llu\n",
-                  server_id_str(talloc_tos(), &src),
+       DEBUG(10, ("Got oplock break to %u message from pid %s: %s/%llu\n",
+                  (unsigned)break_to, server_id_str(talloc_tos(), &src),
                   file_id_string_tos(&msg.id),
                   (unsigned long long)msg.share_file_id));
 
@@ -544,8 +547,7 @@ static void process_oplock_break_message(struct messaging_context *msg_ctx,
                return;
        }
 
-       if (EXCLUSIVE_OPLOCK_TYPE(msg.op_type) &&
-           !EXCLUSIVE_OPLOCK_TYPE(fsp->oplock_type)) {
+       if (break_to == fsp->oplock_type) {
                DEBUG(3, ("Already downgraded oplock on %s: %s\n",
                          file_id_string_tos(&fsp->file_id),
                          fsp_str_dbg(fsp)));
@@ -555,6 +557,7 @@ static void process_oplock_break_message(struct messaging_context *msg_ctx,
        use_kernel = lp_kernel_oplocks(SNUM(fsp->conn)) && koplocks;
 
        if ((global_client_caps & CAP_LEVEL_II_OPLOCKS) &&
+           (break_to != NO_OPLOCK) &&
            !(use_kernel && !(koplocks->flags & KOPLOCKS_LEVEL2_SUPPORTED)) &&
            lp_level2_oplocks(SNUM(fsp->conn))) {
                break_to_level2 = True;
@@ -574,8 +577,14 @@ static void process_oplock_break_message(struct messaging_context *msg_ctx,
                        OPLOCKLEVEL_II : OPLOCKLEVEL_NONE);
        }
 
+       if ((fsp->oplock_type == LEVEL_II_OPLOCK) && (break_to == NO_OPLOCK)) {
+               /*
+                * This is an async break without a reply and thus no timeout
+                */
+               remove_oplock(fsp);
+               return;
+       }
        fsp->sent_oplock_break = break_to_level2 ? LEVEL_II_BREAK_SENT:BREAK_TO_NONE_SENT;
-
        add_oplock_timeout_handler(fsp);
 }
 
@@ -713,7 +722,6 @@ static void do_break_to_none(struct tevent_context *ctx,
 {
        struct break_to_none_state *state = talloc_get_type_abort(
                private_data, struct break_to_none_state);
-       struct server_id self = messaging_server_id(state->sconn->msg_ctx);
        int i;
        struct share_mode_lock *lck;
 
@@ -765,37 +773,12 @@ static void do_break_to_none(struct tevent_context *ctx,
                }
 
                share_mode_entry_to_message(msg, share_entry);
+               /* Overload entry->op_type */
+               SSVAL(msg,OP_BREAK_MSG_OP_TYPE_OFFSET, NO_OPLOCK);
 
-               /*
-                * 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 (serverid_equal(&self, &share_entry->pid)) {
-                       struct files_struct *cur_fsp =
-                               initial_break_processing(state->sconn,
-                                       share_entry->id,
-                                       share_entry->share_file_id);
-                       if (cur_fsp != NULL) {
-                               wait_before_sending_break();
-                               break_level2_to_none_async(cur_fsp);
-                       } else {
-                               DEBUG(3, ("release_level_2_oplocks_on_change: "
-                               "Did not find fsp, ignoring\n"));
-                       }
-               } else {
-                       messaging_send_buf(state->sconn->msg_ctx,
-                                       share_entry->pid,
-                                       MSG_SMB_ASYNC_LEVEL2_BREAK,
-                                       (uint8 *)msg, sizeof(msg));
-               }
+               messaging_send_buf(state->sconn->msg_ctx, share_entry->pid,
+                                  MSG_SMB_BREAK_REQUEST,
+                                  (uint8 *)msg, sizeof(msg));
        }
 
        /* We let the message receivers handle removing the oplock state