2b5518eeaacc6245cfa77ee4a7086f16208060fcJakub Hrozek KCM Server - the KCM operations wait queue
2b5518eeaacc6245cfa77ee4a7086f16208060fcJakub Hrozek Copyright (C) Red Hat, 2017
2b5518eeaacc6245cfa77ee4a7086f16208060fcJakub Hrozek This program is free software; you can redistribute it and/or modify
2b5518eeaacc6245cfa77ee4a7086f16208060fcJakub Hrozek it under the terms of the GNU General Public License as published by
2b5518eeaacc6245cfa77ee4a7086f16208060fcJakub Hrozek the Free Software Foundation; either version 3 of the License, or
2b5518eeaacc6245cfa77ee4a7086f16208060fcJakub Hrozek (at your option) any later version.
2b5518eeaacc6245cfa77ee4a7086f16208060fcJakub Hrozek This program is distributed in the hope that it will be useful,
2b5518eeaacc6245cfa77ee4a7086f16208060fcJakub Hrozek but WITHOUT ANY WARRANTY; without even the implied warranty of
2b5518eeaacc6245cfa77ee4a7086f16208060fcJakub Hrozek MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2b5518eeaacc6245cfa77ee4a7086f16208060fcJakub Hrozek GNU General Public License for more details.
2b5518eeaacc6245cfa77ee4a7086f16208060fcJakub Hrozek You should have received a copy of the GNU General Public License
2b5518eeaacc6245cfa77ee4a7086f16208060fcJakub Hrozek along with this program. If not, see <http://www.gnu.org/licenses/>.
fb51bb68e62de7bb8542f5d224994eb7143040a6Jakub Hrozek /* UID:kcm_ops_queue */
2b5518eeaacc6245cfa77ee4a7086f16208060fcJakub Hrozek * Per-UID wait queue
2b5518eeaacc6245cfa77ee4a7086f16208060fcJakub Hrozek * They key in the hash table is the UID of the peer. The value of each
fb51bb68e62de7bb8542f5d224994eb7143040a6Jakub Hrozek * hash table entry is kcm_ops_queue structure which in turn contains a
fb51bb68e62de7bb8542f5d224994eb7143040a6Jakub Hrozek * linked list of kcm_ops_queue_entry structures * which primarily hold the
fb51bb68e62de7bb8542f5d224994eb7143040a6Jakub Hrozek * tevent request being queued.
2b5518eeaacc6245cfa77ee4a7086f16208060fcJakub Hrozekstruct kcm_ops_queue_ctx *kcm_ops_queue_create(TALLOC_CTX *mem_ctx)
2b5518eeaacc6245cfa77ee4a7086f16208060fcJakub Hrozek queue_ctx = talloc_zero(mem_ctx, struct kcm_ops_queue_ctx);
2b5518eeaacc6245cfa77ee4a7086f16208060fcJakub Hrozek ret = sss_hash_create_ex(mem_ctx, QUEUE_HASH_SIZE,
2b5518eeaacc6245cfa77ee4a7086f16208060fcJakub Hrozek "sss_hash_create failed [%d]: %s\n", ret, sss_strerror(ret));
fb51bb68e62de7bb8542f5d224994eb7143040a6Jakub Hrozekvoid queue_removal_cb(struct tevent_context *ctx,
fb51bb68e62de7bb8542f5d224994eb7143040a6Jakub Hrozek struct kcm_ops_queue *kq = talloc_get_type(private_data,
fb51bb68e62de7bb8542f5d224994eb7143040a6Jakub Hrozek DEBUG(SSSDBG_TRACE_LIBS, "The queue is no longer empty\n");
fb51bb68e62de7bb8542f5d224994eb7143040a6Jakub Hrozek /* If this was the last entry, remove the key (the UID) from the
fb51bb68e62de7bb8542f5d224994eb7143040a6Jakub Hrozek * hash table to signal the queue is empty
fb51bb68e62de7bb8542f5d224994eb7143040a6Jakub Hrozek ret = hash_delete(kq->qctx->wait_queue_hash, &key);
fb51bb68e62de7bb8542f5d224994eb7143040a6Jakub Hrozek "Failed to remove wait queue for user %"SPRIuid"\n",
fb51bb68e62de7bb8542f5d224994eb7143040a6Jakub Hrozekstatic int kcm_op_queue_entry_destructor(struct kcm_ops_queue_entry *entry)
2b5518eeaacc6245cfa77ee4a7086f16208060fcJakub Hrozek /* Take the next entry from the queue */
2b5518eeaacc6245cfa77ee4a7086f16208060fcJakub Hrozek /* Remove the current entry from the queue */
fb51bb68e62de7bb8542f5d224994eb7143040a6Jakub Hrozek /* If there was no other entry, schedule removal of the queue. Do it
fb51bb68e62de7bb8542f5d224994eb7143040a6Jakub Hrozek * in another tevent tick to avoid issues with callbacks invoking
fb51bb68e62de7bb8542f5d224994eb7143040a6Jakub Hrozek * the descructor while another request is touching the queue
fb51bb68e62de7bb8542f5d224994eb7143040a6Jakub Hrozek tevent_schedule_immediate(imm, entry->queue->ev, queue_removal_cb, entry->queue);
2b5518eeaacc6245cfa77ee4a7086f16208060fcJakub Hrozek /* Otherwise, mark the current head as done to run the next request */
fb51bb68e62de7bb8542f5d224994eb7143040a6Jakub Hrozekstatic struct kcm_ops_queue *kcm_op_queue_get(struct kcm_ops_queue_ctx *qctx,
fb51bb68e62de7bb8542f5d224994eb7143040a6Jakub Hrozek ret = hash_lookup(qctx->wait_queue_hash, &key, &value);
2b5518eeaacc6245cfa77ee4a7086f16208060fcJakub Hrozek DEBUG(SSSDBG_CRIT_FAILURE, "Unexpected hash value type.\n");
fb51bb68e62de7bb8542f5d224994eb7143040a6Jakub Hrozek kq = talloc_get_type(value.ptr, struct kcm_ops_queue);
2b5518eeaacc6245cfa77ee4a7086f16208060fcJakub Hrozek DEBUG(SSSDBG_CRIT_FAILURE, "Invalid queue pointer\n");
fb51bb68e62de7bb8542f5d224994eb7143040a6Jakub Hrozek DEBUG(SSSDBG_TRACE_LIBS, "Found existing queue for this ID\n");
2b5518eeaacc6245cfa77ee4a7086f16208060fcJakub Hrozek /* No request for this UID yet. Enqueue this request in case
2b5518eeaacc6245cfa77ee4a7086f16208060fcJakub Hrozek * another one comes in and return EOK to run the current request
a02a5ed51178b2cbede0396d66aed716b8898096René Genz * immediately
fb51bb68e62de7bb8542f5d224994eb7143040a6Jakub Hrozek DEBUG(SSSDBG_TRACE_LIBS, "No existing queue for this ID\n");
fb51bb68e62de7bb8542f5d224994eb7143040a6Jakub Hrozek kq = talloc_zero(qctx->wait_queue_hash, struct kcm_ops_queue);
fb51bb68e62de7bb8542f5d224994eb7143040a6Jakub Hrozek ret = hash_enter(qctx->wait_queue_hash, &key, &value);
2b5518eeaacc6245cfa77ee4a7086f16208060fcJakub Hrozek DEBUG(SSSDBG_CRIT_FAILURE, "hash_enter failed.\n");
2b5518eeaacc6245cfa77ee4a7086f16208060fcJakub Hrozek DEBUG(SSSDBG_CRIT_FAILURE, "hash_lookup failed.\n");
fb51bb68e62de7bb8542f5d224994eb7143040a6Jakub Hrozekstatic errno_t kcm_op_queue_add_req(struct kcm_ops_queue *kq,
2b5518eeaacc6245cfa77ee4a7086f16208060fcJakub Hrozek * Enqueue a request.
2b5518eeaacc6245cfa77ee4a7086f16208060fcJakub Hrozek * If the request queue /for the given ID/ is empty, that is, if this
a02a5ed51178b2cbede0396d66aed716b8898096René Genz * request is the first one in the queue, run the request immediately.
2b5518eeaacc6245cfa77ee4a7086f16208060fcJakub Hrozek * Otherwise just add it to the queue and wait until the previous request
2b5518eeaacc6245cfa77ee4a7086f16208060fcJakub Hrozek * finishes and only at that point mark the current request as done, which
2b5518eeaacc6245cfa77ee4a7086f16208060fcJakub Hrozek * will trigger calling the recv function and allow the request to continue.
2b5518eeaacc6245cfa77ee4a7086f16208060fcJakub Hrozekstruct tevent_req *kcm_op_queue_send(TALLOC_CTX *mem_ctx,
2b5518eeaacc6245cfa77ee4a7086f16208060fcJakub Hrozek req = tevent_req_create(mem_ctx, &state, struct kcm_op_queue_state);
2b5518eeaacc6245cfa77ee4a7086f16208060fcJakub Hrozek "Adding request by %"SPRIuid" to the wait queue\n", uid);
fb51bb68e62de7bb8542f5d224994eb7143040a6Jakub Hrozek "Cannot get queue [%d]: %s\n", ret, sss_strerror(ret));
fb51bb68e62de7bb8542f5d224994eb7143040a6Jakub Hrozek "Queue was empty, running the request immediately\n");
2b5518eeaacc6245cfa77ee4a7086f16208060fcJakub Hrozek "Cannot enqueue request [%d]: %s\n", ret, sss_strerror(ret));
2b5518eeaacc6245cfa77ee4a7086f16208060fcJakub Hrozek DEBUG(SSSDBG_TRACE_LIBS, "Waiting our turn in the queue\n");
fb51bb68e62de7bb8542f5d224994eb7143040a6Jakub Hrozekstatic errno_t kcm_op_queue_add_req(struct kcm_ops_queue *kq,
fb51bb68e62de7bb8542f5d224994eb7143040a6Jakub Hrozek struct kcm_op_queue_state *state = tevent_req_data(req,
fb51bb68e62de7bb8542f5d224994eb7143040a6Jakub Hrozek state->entry = talloc_zero(kq->qctx->wait_queue_hash, struct kcm_ops_queue_entry);
fb51bb68e62de7bb8542f5d224994eb7143040a6Jakub Hrozek talloc_set_destructor(state->entry, kcm_op_queue_entry_destructor);
fb51bb68e62de7bb8542f5d224994eb7143040a6Jakub Hrozek /* First entry, will run callback at once */
fb51bb68e62de7bb8542f5d224994eb7143040a6Jakub Hrozek /* Will wait for the previous callbacks to finish */
fb51bb68e62de7bb8542f5d224994eb7143040a6Jakub Hrozek DLIST_ADD_END(kq->head, state->entry, struct kcm_ops_queue_entry *);
2b5518eeaacc6245cfa77ee4a7086f16208060fcJakub Hrozek * The queue recv function is called when this request is 'activated'. The queue
2b5518eeaacc6245cfa77ee4a7086f16208060fcJakub Hrozek * entry should be allocated on the same memory context as the enqueued request
2b5518eeaacc6245cfa77ee4a7086f16208060fcJakub Hrozek * to trigger freeing the kcm_ops_queue_entry structure destructor when the
2b5518eeaacc6245cfa77ee4a7086f16208060fcJakub Hrozek * parent request is done and its tevent_req freed. This would in turn unblock
2b5518eeaacc6245cfa77ee4a7086f16208060fcJakub Hrozek * the next request in the queue
2b5518eeaacc6245cfa77ee4a7086f16208060fcJakub Hrozekerrno_t kcm_op_queue_recv(struct tevent_req *req,