smbd: Use share_mode's flags in contend_level2_oplocks
authorVolker Lendecke <vl@samba.org>
Sun, 30 Jun 2019 07:50:46 +0000 (09:50 +0200)
committerRalph Boehme <slow@samba.org>
Thu, 4 Jul 2019 14:03:29 +0000 (14:03 +0000)
Here we traverse the oplocks and leases when breaking read leases. We
find out here whether any of those are still left.

As it's the receivers of the messages that downgrade the database
entries, we might do that more than once. Possible future
optimization?

Signed-off-by: Volker Lendecke <vl@samba.org>
Reviewed-by: Ralph Boehme <slow@samba.org>
source3/smbd/oplock.c

index aad85fbb2d18d626a931cab551ecb6e41d896c52..c1625440fba9edb7a9742cc0a43edc170e4f4223 100644 (file)
@@ -1230,101 +1230,6 @@ static void process_kernel_oplock_break(struct messaging_context *msg_ctx,
        add_oplock_timeout_handler(fsp);
 }
 
-static bool file_has_read_oplocks(struct files_struct *fsp)
-{
-       struct byte_range_lock *brl;
-       uint32_t num_read_oplocks = 0;
-
-       brl = brl_get_locks_readonly(fsp);
-       if (brl == NULL) {
-               return false;
-       }
-
-       num_read_oplocks = brl_num_read_oplocks(brl);
-
-       DBG_DEBUG("num_read_oplocks = %"PRIu32"\n", num_read_oplocks);
-
-       return (num_read_oplocks != 0);
-}
-
-struct break_to_none_state {
-       struct smbd_server_connection *sconn;
-       struct file_id id;
-       struct smb2_lease_key lease_key;
-       struct GUID client_guid;
-};
-static void do_break_to_none(struct tevent_context *ctx,
-                            struct tevent_immediate *im,
-                            void *private_data);
-
-/****************************************************************************
- This function is called on any file modification or lock request. If a file
- is level 2 oplocked then it must tell all other level 2 holders to break to
- none.
-****************************************************************************/
-
-static void contend_level2_oplocks_begin_default(files_struct *fsp,
-                                             enum level2_contention_type type)
-{
-       struct smbd_server_connection *sconn = fsp->conn->sconn;
-       struct tevent_immediate *im;
-       struct break_to_none_state *state;
-       bool has_read_oplocks;
-
-       /*
-        * If this file is level II oplocked then we need
-        * to grab the shared memory lock and inform all
-        * other files with a level II lock that they need
-        * to flush their read caches. We keep the lock over
-        * the shared memory area whilst doing this.
-        */
-
-       if (fsp_lease_type_is_exclusive(fsp)) {
-               /*
-                * There can't be any level2 oplocks, we're alone.
-                */
-               return;
-       }
-
-       has_read_oplocks = file_has_read_oplocks(fsp);
-       if (!has_read_oplocks) {
-               DEBUG(10, ("No read oplocks around\n"));
-               return;
-       }
-
-       /*
-        * When we get here we might have a brlock entry locked. Also
-        * locking the share mode entry would violate the locking
-        * order. Breaking level2 oplocks to none is asynchronous
-        * anyway, so we postpone this into an immediate event.
-        */
-
-       state = talloc(sconn, struct break_to_none_state);
-       if (state == NULL) {
-               DEBUG(1, ("talloc failed\n"));
-               return;
-       }
-       *state = (struct break_to_none_state) {
-               .sconn = sconn, .id = fsp->file_id,
-       };
-
-       if (fsp->oplock_type == LEASE_OPLOCK) {
-               state->client_guid = *fsp_client_guid(fsp);
-               state->lease_key = fsp->lease->lease.lease_key;
-               DEBUG(10, ("Breaking through lease key %"PRIu64"/%"PRIu64"\n",
-                          state->lease_key.data[0],
-                          state->lease_key.data[1]));
-       }
-
-       im = tevent_create_immediate(state);
-       if (im == NULL) {
-               DEBUG(1, ("tevent_create_immediate failed\n"));
-               TALLOC_FREE(state);
-               return;
-       }
-       tevent_schedule_immediate(im, sconn->ev_ctx, do_break_to_none, state);
-}
-
 static void send_break_to_none(struct messaging_context *msg_ctx,
                               const struct file_id *id,
                               const struct share_mode_entry *e)
@@ -1336,13 +1241,19 @@ static void send_break_to_none(struct messaging_context *msg_ctx,
                          nt_errstr(status));
        }
 }
+struct break_to_none_state {
+       struct smbd_server_connection *sconn;
+       struct file_id id;
+       struct smb2_lease_key lease_key;
+       struct GUID client_guid;
+       size_t num_broken;
+};
 
 static bool do_break_lease_to_none(struct share_mode_lock *lck,
                                   struct share_mode_entry *e,
                                   void *private_data)
 {
-       struct break_to_none_state *state = talloc_get_type_abort(
-               private_data, struct break_to_none_state);
+       struct break_to_none_state *state = private_data;
        uint32_t current_state = 0;
        bool our_own;
        NTSTATUS status;
@@ -1385,25 +1296,62 @@ static bool do_break_lease_to_none(struct share_mode_lock *lck,
 
        send_break_to_none(state->sconn->msg_ctx, &state->id, e);
 
+       state->num_broken += 1;
+
        return false;
 }
 
-static void do_break_to_none(struct tevent_context *ctx,
-                            struct tevent_immediate *im,
-                            void *private_data)
+/****************************************************************************
+ This function is called on any file modification or lock request. If a file
+ is level 2 oplocked then it must tell all other level 2 holders to break to
+ none.
+****************************************************************************/
+
+static void contend_level2_oplocks_begin_default(files_struct *fsp,
+                                             enum level2_contention_type type)
 {
-       struct break_to_none_state *state = talloc_get_type_abort(
-               private_data, struct break_to_none_state);
+       struct break_to_none_state state = {
+               .sconn = fsp->conn->sconn, .id = fsp->file_id,
+       };
+       struct share_mode_lock *lck = NULL;
+       struct share_mode_data *d = NULL;
+       bool ok, has_read_lease;
        uint32_t i;
-       struct share_mode_lock *lck;
-       struct share_mode_data *d;
-       bool ok;
 
-       lck = get_existing_share_mode_lock(talloc_tos(), state->id);
+       /*
+        * If this file is level II oplocked then we need
+        * to grab the shared memory lock and inform all
+        * other files with a level II lock that they need
+        * to flush their read caches. We keep the lock over
+        * the shared memory area whilst doing this.
+        */
+
+       if (fsp_lease_type_is_exclusive(fsp)) {
+               /*
+                * There can't be any level2 oplocks, we're alone.
+                */
+               return;
+       }
+
+       has_read_lease = file_has_read_lease(fsp);
+       if (!has_read_lease) {
+               DEBUG(10, ("No read oplocks around\n"));
+               return;
+       }
+
+       if (fsp->oplock_type == LEASE_OPLOCK) {
+               state.client_guid = *fsp_client_guid(fsp);
+               state.lease_key = fsp->lease->lease.lease_key;
+               DEBUG(10, ("Breaking through lease key %"PRIu64"/%"PRIu64"\n",
+                          state.lease_key.data[0],
+                          state.lease_key.data[1]));
+       }
+
+       lck = get_existing_share_mode_lock(talloc_tos(), fsp->file_id);
        if (lck == NULL) {
-               DEBUG(1, ("%s: failed to lock share mode entry for file %s.\n",
-                         __func__, file_id_string_tos(&state->id)));
-               goto done;
+               DBG_WARNING("failed to lock share mode entry for file %s.\n",
+                           file_id_string_tos(&state.id));
+               return;
        }
        d = lck->data;
 
@@ -1414,7 +1362,7 @@ static void do_break_to_none(struct tevent_context *ctx,
         * separately.
         */
 
-       ok = share_mode_forall_leases(lck, do_break_lease_to_none, state);
+       ok = share_mode_forall_leases(lck, do_break_lease_to_none, &state);
        if (!ok) {
                DBG_WARNING("share_mode_forall_leases failed\n");
        }
@@ -1459,16 +1407,20 @@ static void do_break_to_none(struct tevent_context *ctx,
                        abort();
                }
 
-               send_break_to_none(state->sconn->msg_ctx, &state->id, e);
+               send_break_to_none(state.sconn->msg_ctx, &state.id, e);
+               state.num_broken += 1;
        }
 
-       /* We let the message receivers handle removing the oplock state
-          in the share mode lock db. */
+       if (state.num_broken == 0) {
+               /*
+                * Lazy update here. It might be that the read lease
+                * has gone in the meantime.
+                */
+               d->flags &= ~SHARE_MODE_HAS_READ_LEASE;
+               d->modified = true;
+       }
 
        TALLOC_FREE(lck);
-done:
-       TALLOC_FREE(state);
-       return;
 }
 
 void smbd_contend_level2_oplocks_begin(files_struct *fsp,