s3/locking: add fetch_share_mode_send/recv
authorRalph Boehme <slow@samba.org>
Wed, 4 Jan 2017 07:00:29 +0000 (08:00 +0100)
committerJeremy Allison <jra@samba.org>
Tue, 18 Apr 2017 20:54:16 +0000 (22:54 +0200)
The boolean out parameter "queued" tells the caller whether the
async request is blocked in a full send queue:

false := request is dispatched
true  := send queue is full, request waiting to be dispatched

This is useful in a clustered Samba environment where the async dbwrap
request is sent over a socket to the local ctdbd.

If the send queue is full and the caller was issuing multiple async
dbwrap requests in a loop, the caller knows it's probably time to stop
sending requests for now and try again later.

This will be used in subsequent commits in
smbd_smb2_query_directory_send() when implementing async write time
updates. Directories may contain umpteen files so we send many requests
to ctdb without going through tevent and reading the responses which
has the potential to deadlock.

Signed-off-by: Ralph Boehme <slow@samba.org>
Reviewed-by: Stefan Metzmacher <metze@samba.org>
source3/locking/proto.h
source3/locking/share_mode_lock.c

index 17cb1cdf86b4ef167231ce00500c6f4e2c38266e..967af02bb262ac24edec4284e7a0888fab673c0b 100644 (file)
@@ -153,6 +153,13 @@ struct share_mode_lock *get_share_mode_lock(
        const struct timespec *old_write_time);
 struct share_mode_lock *fetch_share_mode_unlocked(TALLOC_CTX *mem_ctx,
                                                  struct file_id id);
+struct tevent_req *fetch_share_mode_send(TALLOC_CTX *mem_ctx,
+                                        struct tevent_context *ev,
+                                        struct file_id id,
+                                        bool *queued);
+NTSTATUS fetch_share_mode_recv(struct tevent_req *req,
+                              TALLOC_CTX *mem_ctx,
+                              struct share_mode_lock **_lck);
 bool rename_share_filename(struct messaging_context *msg_ctx,
                        struct share_mode_lock *lck,
                        struct file_id id,
index 16d8ed4df951e2f48ea03e9a86cdeadb331f7cb8..0333b0d79659813cc52f18963c6353a8b8c12edd 100644 (file)
@@ -50,6 +50,7 @@
 #include "source3/lib/dbwrap/dbwrap_watch.h"
 #include "locking/leases_db.h"
 #include "../lib/util/memcache.h"
+#include "lib/util/tevent_ntstatus.h"
 
 #undef DBGC_CLASS
 #define DBGC_CLASS DBGC_LOCKING
@@ -667,6 +668,130 @@ struct share_mode_lock *fetch_share_mode_unlocked(TALLOC_CTX *mem_ctx,
        return state.lck;
 }
 
+static void fetch_share_mode_done(struct tevent_req *subreq);
+
+struct fetch_share_mode_state {
+       struct file_id id;
+       TDB_DATA key;
+       struct share_mode_lock *lck;
+       enum dbwrap_req_state req_state;
+};
+
+/**
+ * @brief Get a share_mode_lock without locking or refcounting
+ *
+ * This can be used in a clustered Samba environment where the async dbwrap
+ * request is sent over a socket to the local ctdbd. If the send queue is full
+ * and the caller was issuing multiple async dbwrap requests in a loop, the
+ * caller knows it's probably time to stop sending requests for now and try
+ * again later.
+ *
+ * @param[in]  mem_ctx The talloc memory context to use.
+ *
+ * @param[in]  ev      The event context to work on.
+ *
+ * @param[in]  id      The file id for the locking.tdb key
+ *
+ * @param[out] queued  This boolean out parameter tells the caller whether the
+ *                     async request is blocked in a full send queue:
+ *
+ *                     false := request is dispatched
+ *
+ *                     true  := send queue is full, request waiting to be
+ *                              dispatched
+ *
+ * @return             The new async request, NULL on error.
+ **/
+struct tevent_req *fetch_share_mode_send(TALLOC_CTX *mem_ctx,
+                                        struct tevent_context *ev,
+                                        struct file_id id,
+                                        bool *queued)
+{
+       struct tevent_req *req = NULL;
+       struct fetch_share_mode_state *state = NULL;
+       struct tevent_req *subreq = NULL;
+
+       *queued = false;
+
+       req = tevent_req_create(mem_ctx, &state,
+                               struct fetch_share_mode_state);
+       if (req == NULL) {
+               return NULL;
+       }
+
+       state->id = id;
+       state->key = locking_key(&state->id);
+       state->lck = talloc_zero(state, struct share_mode_lock);
+       if (tevent_req_nomem(state->lck, req)) {
+               return tevent_req_post(req, ev);
+       }
+
+       subreq = dbwrap_parse_record_send(state,
+                                         ev,
+                                         lock_db,
+                                         state->key,
+                                         fetch_share_mode_unlocked_parser,
+                                         state->lck,
+                                         &state->req_state);
+       if (tevent_req_nomem(subreq, req)) {
+               return tevent_req_post(req, ev);
+       }
+       tevent_req_set_callback(subreq, fetch_share_mode_done, req);
+
+       if (state->req_state < DBWRAP_REQ_DISPATCHED) {
+               *queued = true;
+       }
+       return req;
+}
+
+static void fetch_share_mode_done(struct tevent_req *subreq)
+{
+       struct tevent_req *req = tevent_req_callback_data(
+               subreq, struct tevent_req);
+       NTSTATUS status;
+
+       status = dbwrap_parse_record_recv(subreq);
+       TALLOC_FREE(subreq);
+       if (tevent_req_nterror(req, status)) {
+               return;
+       }
+
+       tevent_req_done(req);
+       return;
+}
+
+NTSTATUS fetch_share_mode_recv(struct tevent_req *req,
+                              TALLOC_CTX *mem_ctx,
+                              struct share_mode_lock **_lck)
+{
+       struct fetch_share_mode_state *state = tevent_req_data(
+               req, struct fetch_share_mode_state);
+       struct share_mode_lock *lck = NULL;
+
+       NTSTATUS status;
+
+       if (tevent_req_is_nterror(req, &status)) {
+               tevent_req_received(req);
+               return status;
+       }
+
+       if (state->lck->data == NULL) {
+               tevent_req_received(req);
+               return NT_STATUS_NOT_FOUND;
+       }
+
+       lck = talloc_move(mem_ctx, &state->lck);
+
+       if (DEBUGLEVEL >= 10) {
+               DBG_DEBUG("share_mode_data:\n");
+               NDR_PRINT_DEBUG(share_mode_data, lck->data);
+       }
+
+       *_lck = lck;
+       tevent_req_received(req);
+       return NT_STATUS_OK;
+}
+
 struct share_mode_forall_state {
        int (*fn)(struct file_id fid, const struct share_mode_data *data,
                  void *private_data);