smbd: For kernel-oplocked files don't call delay_for_oplock()
authorVolker Lendecke <vl@samba.org>
Sun, 18 Aug 2019 06:57:38 +0000 (08:57 +0200)
committerJeremy Allison <jra@samba.org>
Mon, 19 Aug 2019 23:14:39 +0000 (23:14 +0000)
The kernel has already sent the signal. We don't have to send another
message. Instead, just directly wait for the downgrade to happen via
the watch on the share mode lock assuming it's there. Also setup the
polling interval: I could imagine that in some race situation the file
has already been closed and re-opened by a nonsamba process while we
were waiting.

Signed-off-by: Volker Lendecke <vl@samba.org>
Reviewed-by: Jeremy Allison <jra@samba.org>
source3/smbd/open.c

index fc2df6eb0a02624ad94b8e670c5ab46cd12e70f4..ceb117c924d12afc15ace582369d947eb51cc6bd 100644 (file)
@@ -56,6 +56,16 @@ struct deferred_open_record {
         * opens and just terminates smbd if the async open times out.
         */
        struct tevent_timer *te;
+
+       /*
+        * For the samba kernel oplock case we use both a timeout and
+        * a watch on locking.tdb. This way in case it's smbd holding
+        * the kernel oplock we get directly notified for the retry
+        * once the kernel oplock is properly broken. Store the req
+        * here so that it can be timely discarded once the timer
+        * above fires.
+        */
+       struct tevent_req *watch_req;
 };
 
 /****************************************************************************
@@ -2477,6 +2487,8 @@ static void poll_open_fn(struct tevent_context *ev,
                private_data, struct deferred_open_record);
        bool ok;
 
+       TALLOC_FREE(open_rec->watch_req);
+
        ok = schedule_deferred_open_message_smb(
                open_rec->xconn, open_rec->mid);
        if (!ok) {
@@ -2536,17 +2548,18 @@ static bool setup_poll_open(
        }
 
        if (lck != NULL) {
-               struct tevent_req *watch_req = dbwrap_watched_watch_send(
+               open_rec->watch_req = dbwrap_watched_watch_send(
                        open_rec,
                        req->sconn->ev_ctx,
                        lck->data->record,
                        (struct server_id) {0});
-               if (watch_req == NULL) {
+               if (open_rec->watch_req == NULL) {
                        DBG_WARNING("dbwrap_watched_watch_send failed\n");
                        TALLOC_FREE(open_rec);
                        return false;
                }
-               tevent_req_set_callback(watch_req, poll_open_done, open_rec);
+               tevent_req_set_callback(
+                       open_rec->watch_req, poll_open_done, open_rec);
        }
 
        ok = push_deferred_open_message_smb(req, max_timeout, id, open_rec);
@@ -3326,8 +3339,6 @@ static NTSTATUS open_file_ntcreate(connection_struct *conn,
                             open_access_mask, &new_file_created);
 
        if (NT_STATUS_EQUAL(fsp_open, NT_STATUS_NETWORK_BUSY)) {
-               bool delay;
-
                /*
                 * This handles the kernel oplock case:
                 *
@@ -3351,53 +3362,27 @@ static NTSTATUS open_file_ntcreate(connection_struct *conn,
                 */
 
                lck = get_existing_share_mode_lock(talloc_tos(), fsp->file_id);
-               if (lck == NULL) {
-                       /*
-                        * No oplock from Samba around. Set up a poll every 1
-                        * second to retry a non-blocking open until the time
-                        * expires.
-                        */
-                       setup_poll_open(
-                               req,
-                               NULL,
-                               fsp->file_id,
-                               timeval_set(OPLOCK_BREAK_TIMEOUT*2, 0),
-                               timeval_set(1, 0));
-                       DBG_DEBUG("No Samba oplock around after EWOULDBLOCK. "
-                               "Retrying with poll\n");
-                       return NT_STATUS_SHARING_VIOLATION;
-               }
 
-               if (!validate_oplock_types(lck)) {
+               if ((lck != NULL) && !validate_oplock_types(lck)) {
                        smb_panic("validate_oplock_types failed");
                }
 
-               delay = delay_for_oplock(fsp, 0, lease, lck, false,
-                                        create_disposition,
-                                        first_open_attempt);
-               if (delay) {
-                       schedule_defer_open(lck, fsp->file_id, req);
-                       TALLOC_FREE(lck);
-                       DEBUG(10, ("Sent oplock break request to kernel "
-                                  "oplock holder\n"));
-                       return NT_STATUS_SHARING_VIOLATION;
-               }
-
                /*
-                * No oplock from Samba around. Set up a poll every 1
-                * second to retry a non-blocking open until the time
-                * expires.
+                * Retry once a second. If there's a share_mode_lock
+                * around, also wait for it in case it was smbd
+                * holding that kernel oplock that can quickly tell us
+                * the oplock got removed.
                 */
+
                setup_poll_open(
                        req,
-                       NULL,
+                       lck,
                        fsp->file_id,
                        timeval_set(OPLOCK_BREAK_TIMEOUT*2, 0),
                        timeval_set(1, 0));
 
                TALLOC_FREE(lck);
-               DBG_DEBUG("No Samba oplock around after EWOULDBLOCK. "
-                       "Retrying with poll\n");
+
                return NT_STATUS_SHARING_VIOLATION;
        }