[PATCH v3] eal: add seqlock
Mattias Rönnblom
hofors at lysator.liu.se
Fri Apr 8 15:45:32 CEST 2022
On 2022-04-03 19:37, Honnappa Nagarahalli wrote:
> <snip>
>
>>
>>>> + * Example usage:
>>>> + * @code{.c}
>>>> + * #define MAX_Y_LEN (16)
>>>> + * // Application-defined example data structure, protected by a seqlock.
>>>> + * struct config {
>>>> + * rte_seqlock_t lock;
>>>> + * int param_x;
>>>> + * char param_y[MAX_Y_LEN];
>>>> + * };
>>>> + *
>>>> + * // Accessor function for reading config fields.
>>>> + * void
>>>> + * config_read(const struct config *config, int *param_x, char
>>>> +*param_y)
>>>> + * {
>>>> + * // Temporary variables, just to improve readability.
>>> I think the above comment is not necessary. It is beneficial to copy the
>> protected data to keep the read side critical section small.
>>>
>>
>> The data here would be copied into the buffers supplied by config_read()
>> anyways, so it's a copy regardless.
> I see what you mean here. I would think the local variables add confusion, the copy can happen to the passed parameters directly. I will leave it to you to decide.
>
I'll remove the temp variables.
>>
>>>> + * int tentative_x;
>>>> + * char tentative_y[MAX_Y_LEN];
>>>> + * uint32_t sn;
>>>> + *
>>>> + * sn = rte_seqlock_read_lock(&config->lock);
>>>> + * do {
>>>> + * // Loads may be atomic or non-atomic, as in this example.
>>>> + * tentative_x = config->param_x;
>>>> + * strcpy(tentative_y, config->param_y);
>>>> + * } while (!rte_seqlock_read_tryunlock(&config->lock, &sn));
>>>> + * // An application could skip retrying, and try again later, if
>>>> + * // progress is possible without the data.
>>>> + *
>>>> + * *param_x = tentative_x;
>>>> + * strcpy(param_y, tentative_y);
>>>> + * }
>>>> + *
>>>> + * // Accessor function for writing config fields.
>>>> + * void
>>>> + * config_update(struct config *config, int param_x, const char
>>>> +*param_y)
>>>> + * {
>>>> + * rte_seqlock_write_lock(&config->lock);
>>>> + * // Stores may be atomic or non-atomic, as in this example.
>>>> + * config->param_x = param_x;
>>>> + * strcpy(config->param_y, param_y);
>>>> + * rte_seqlock_write_unlock(&config->lock);
>>>> + * }
>>>> + * @endcode
>>>> + *
>>>> + * @see
>>>> + * https://en.wikipedia.org/wiki/Seqlock.
>>>> + */
>>>> +
>>>> +#include <stdbool.h>
>>>> +#include <stdint.h>
>>>> +
>>>> +#include <rte_atomic.h>
>>>> +#include <rte_branch_prediction.h>
>>>> +#include <rte_spinlock.h>
>>>> +
>>>> +/**
>>>> + * The RTE seqlock type.
>>>> + */
>>>> +typedef struct {
>>>> + uint32_t sn; /**< A sequence number for the protected data. */
>>>> + rte_spinlock_t lock; /**< Spinlock used to serialize writers. */ }
>>> Suggest using ticket lock for the writer side. It should have low overhead
>> when there is a single writer, but provides better functionality when there are
>> multiple writers.
>>>
>>
>> Is a seqlock the synchronization primitive of choice for high-contention cases?
>> I would say no, but I'm not sure what you would use instead.
> I think Stephen has come across some use cases of high contention writers with readers, maybe Stephen can provide some input.
>
> IMO, there is no harm/perf issues in using ticket lock.
>
OK. I will leave at as spinlock for now (PATCH v4).
More information about the dev
mailing list