// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright 2023 Red Hat
*/
#include "completion.h"
#include <linux/kernel.h>
#include "logger.h"
#include "permassert.h"
#include "status-codes.h"
#include "types.h"
#include "vio.h"
#include "vdo.h"
/**
* DOC : vdo completions .
*
* Most of vdo ' s data structures are lock free , each either belonging to a single " zone , " or
* divided into a number of zones whose accesses to the structure do not overlap . During normal
* operation , at most one thread will be operating in any given zone . Each zone has a
* vdo_work_queue which holds vdo_completions that are to be run in that zone . A completion may
* only be enqueued on one queue or operating in a single zone at a time .
*
* At each step of a multi - threaded operation , the completion performing the operation is given a
* callback , error handler , and thread id for the next step . A completion is " run " when it is
* operating on the correct thread ( as specified by its callback_thread_id ) . If the value of its
* " result " field is an error ( i . e . not VDO_SUCCESS ) , the function in its " error_handler " will be
* invoked . If the error_handler is NULL , or there is no error , the function set as its " callback "
* will be invoked . Generally , a completion will not be run directly , but rather will be
* " launched . " In this case , it will check whether it is operating on the correct thread . If it is ,
* it will run immediately . Otherwise , it will be enqueue on the vdo_work_queue associated with the
* completion ' s " callback_thread_id " . When it is dequeued , it will be on the correct thread , and
* will get run . In some cases , the completion should get queued instead of running immediately ,
* even if it is being launched from the correct thread . This is usually in cases where there is a
* long chain of callbacks , all on the same thread , which could overflow the stack . In such cases ,
* the completion ' s " requeue " field should be set to true . Doing so will skip the current thread
* check and simply enqueue the completion .
*
* A completion may be " finished , " in which case its " complete " field will be set to true before it
* is next run . It is a bug to attempt to set the result or re - finish a finished completion .
* Because a completion ' s fields are not safe to examine from any thread other than the one on
* which the completion is currently operating , this field is used only to aid in detecting
* programming errors . It can not be used for cross - thread checking on the status of an operation .
* A completion must be " reset " before it can be reused after it has been finished . Resetting will
* also clear any error from the result field .
**/
void vdo_initialize_completion(struct vdo_completion *completion,
struct vdo *vdo,
enum vdo_completion_type type)
{
memset(completion, 0 , sizeof (*completion));
completion->vdo = vdo;
completion->type = type;
vdo_reset_completion(completion);
}
static inline void assert_incomplete(struct vdo_completion *completion)
{
VDO_ASSERT_LOG_ONLY(!completion->complete, "completion is not complete" );
}
/**
* vdo_set_completion_result ( ) - Set the result of a completion .
*
* Older errors will not be masked .
*/
void vdo_set_completion_result(struct vdo_completion *completion, int result)
{
assert_incomplete(completion);
if (completion->result == VDO_SUCCESS)
completion->result = result;
}
/**
* vdo_launch_completion_with_priority ( ) - Run or enqueue a completion .
* @ priority : The priority at which to enqueue the completion .
*
* If called on the correct thread ( i . e . the one specified in the completion ' s callback_thread_id
* field ) and not marked for requeue , the completion will be run immediately . Otherwise , the
* completion will be enqueued on the specified thread .
*/
void vdo_launch_completion_with_priority(struct vdo_completion *completion,
enum vdo_completion_priority priority)
{
thread_id_t callback_thread = completion->callback_thread_id;
if (completion->requeue || (callback_thread != vdo_get_callback_thread_id())) {
vdo_enqueue_completion(completion, priority);
return ;
}
vdo_run_completion(completion);
}
/** vdo_finish_completion() - Mark a completion as complete and then launch it. */
void vdo_finish_completion(struct vdo_completion *completion)
{
assert_incomplete(completion);
completion->complete = true ;
if (completion->callback != NULL)
vdo_launch_completion(completion);
}
void vdo_enqueue_completion(struct vdo_completion *completion,
enum vdo_completion_priority priority)
{
struct vdo *vdo = completion->vdo;
thread_id_t thread_id = completion->callback_thread_id;
if (VDO_ASSERT(thread_id < vdo->thread_config.thread_count,
"thread_id %u (completion type %d) is less than thread count %u" ,
thread_id, completion->type,
vdo->thread_config.thread_count) != VDO_SUCCESS)
BUG();
completion->requeue = false ;
completion->priority = priority;
completion->my_queue = NULL;
vdo_enqueue_work_queue(vdo->threads[thread_id].queue, completion);
}
/**
* vdo_requeue_completion_if_needed ( ) - Requeue a completion if not called on the specified thread .
*
* Return : True if the completion was requeued ; callers may not access the completion in this case .
*/
bool vdo_requeue_completion_if_needed(struct vdo_completion *completion,
thread_id_t callback_thread_id)
{
if (vdo_get_callback_thread_id() == callback_thread_id)
return false ;
completion->callback_thread_id = callback_thread_id;
vdo_enqueue_completion(completion, VDO_WORK_Q_DEFAULT_PRIORITY);
return true ;
}
Messung V0.5 in Prozent C=96 H=100 G=97
¤ Dauer der Verarbeitung: 0.9 Sekunden
(vorverarbeitet am 2026-09-29)
¤
*© Formatika GbR, Deutschland