Skip to content

Commit 1dfa72c

Browse files
mctpd: Deadline-based recovery
This PR replaces individual recovery timers with u64 deadlines instead. Add a global time entrypoint that triggers the deadlines. Signed-off-by: Khang D Nguyen <[email protected]>
1 parent 9c68dab commit 1dfa72c

File tree

8 files changed

+281
-49
lines changed

8 files changed

+281
-49
lines changed

src/mctp-ops.c

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77

88
#define _GNU_SOURCE
99

10+
#include <sys/time.h>
11+
#include <sys/timerfd.h>
1012
#include <unistd.h>
1113
#include <linux/netlink.h>
1214
#include <err.h>
@@ -57,6 +59,60 @@ static void mctp_bug_warn(const char *fmt, va_list args)
5759
vwarnx(fmt, args);
5860
}
5961

62+
static inline struct itimerspec itimespec_from_usec(uint64_t u)
63+
{
64+
return (struct itimerspec){
65+
.it_interval = { 0 },
66+
.it_value.tv_sec = u / 1000000,
67+
.it_value.tv_nsec = (u % 1000000) * 1000,
68+
};
69+
}
70+
static inline uint64_t usec_from_itimespec(struct itimerspec spec)
71+
{
72+
return (spec.it_value.tv_sec * 1000000) +
73+
(spec.it_value.tv_nsec / 1000);
74+
}
75+
76+
static int mctp_op_timerfd_create()
77+
{
78+
return timerfd_create(CLOCK_MONOTONIC, 0);
79+
}
80+
81+
static int mctp_op_timerfd_settime(int fd, uint64_t new_value,
82+
uint64_t *old_value)
83+
{
84+
struct itimerspec new = itimespec_from_usec(new_value);
85+
struct itimerspec old;
86+
int rc;
87+
88+
rc = timerfd_settime(fd, TFD_TIMER_ABSTIME, &new, &old);
89+
if (rc < 0)
90+
return rc;
91+
92+
*old_value = usec_from_itimespec(old);
93+
94+
return 0;
95+
}
96+
97+
static int mctp_op_timerfd_gettime(int fd, uint64_t *curr_value)
98+
{
99+
struct itimerspec spec;
100+
int rc;
101+
102+
rc = timerfd_gettime(fd, &spec);
103+
if (rc < 0)
104+
return rc;
105+
106+
*curr_value = usec_from_itimespec(spec);
107+
108+
return 0;
109+
}
110+
111+
static int mctp_op_timerfd_read(int fd, uint64_t *n)
112+
{
113+
return read(fd, n, sizeof(*n));
114+
}
115+
60116
const struct mctp_ops mctp_ops = {
61117
.mctp = {
62118
.socket = mctp_op_mctp_socket,
@@ -74,6 +130,12 @@ const struct mctp_ops mctp_ops = {
74130
.recvfrom = mctp_op_recvfrom,
75131
.close = mctp_op_close,
76132
},
133+
.timerfd = {
134+
.create = mctp_op_timerfd_create,
135+
.gettime = mctp_op_timerfd_gettime,
136+
.settime = mctp_op_timerfd_settime,
137+
.read = mctp_op_timerfd_read,
138+
},
77139
.bug_warn = mctp_bug_warn,
78140
};
79141

src/mctp-ops.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,10 @@
77
*/
88
#pragma once
99

10+
#include <stdint.h>
1011
#include <sys/socket.h>
1112
#include <stdarg.h>
13+
#include <sys/timerfd.h>
1214

1315
#define _GNU_SOURCE
1416

@@ -24,9 +26,17 @@ struct socket_ops {
2426
int (*close)(int sd);
2527
};
2628

29+
struct timerfd_ops {
30+
int (*create)();
31+
int (*settime)(int fd, uint64_t new_value, uint64_t *old_value);
32+
int (*gettime)(int fd, uint64_t *curr_value);
33+
int (*read)(int fd, uint64_t *n);
34+
};
35+
2736
struct mctp_ops {
2837
struct socket_ops mctp;
2938
struct socket_ops nl;
39+
struct timerfd_ops timerfd;
3040
void (*bug_warn)(const char *fmt, va_list args);
3141
};
3242

src/mctpd.c

Lines changed: 78 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -189,7 +189,7 @@ struct peer {
189189
bool degraded;
190190
struct {
191191
uint64_t delay;
192-
sd_event_source *source;
192+
uint64_t deadline;
193193
int npolls;
194194
mctp_eid_t eid;
195195
uint8_t endpoint_type;
@@ -248,6 +248,10 @@ struct ctx {
248248

249249
// maximum pool size for assumed MCTP Bridge
250250
uint8_t max_pool_size;
251+
252+
uint64_t now;
253+
uint64_t next_deadline;
254+
int timerfd;
251255
};
252256

253257
static int emit_endpoint_added(const struct peer *peer);
@@ -1791,6 +1795,7 @@ static int add_peer(struct ctx *ctx, const dest_phys *dest, mctp_eid_t eid,
17911795
memcpy(&peer->phys, dest, sizeof(*dest));
17921796
peer->state = REMOTE;
17931797
peer->ctx = ctx;
1798+
peer->recovery.deadline = UINT64_MAX;
17941799

17951800
// Update network eid map
17961801
n->peers[eid] = peer;
@@ -1851,16 +1856,7 @@ static int remove_peer(struct peer *peer)
18511856

18521857
// Clear it
18531858
if (peer->degraded) {
1854-
int rc;
1855-
1856-
rc = sd_event_source_set_enabled(peer->recovery.source,
1857-
SD_EVENT_OFF);
1858-
if (rc < 0) {
1859-
/* XXX: Fix caller assumptions? */
1860-
warnx("Failed to stop recovery timer while removing peer: %d",
1861-
rc);
1862-
}
1863-
sd_event_source_unref(peer->recovery.source);
1859+
peer->recovery.deadline = UINT64_MAX;
18641860
}
18651861

18661862
n->peers[peer->eid] = NULL;
@@ -3079,10 +3075,19 @@ static int method_endpoint_remove(sd_bus_message *call, void *data,
30793075
(MCTP_I2C_TSYM_MT1_MAX_US + 2 * MCTP_I2C_TSYM_MT3_MAX_US)
30803076
#define MCTP_I2C_TSYM_MT2_MAX_MS MCTP_I2C_TSYM_MT4_MIN_US
30813077

3082-
static int peer_endpoint_recover(sd_event_source *s, uint64_t usec,
3083-
void *userdata)
3078+
static int update_deadline(struct ctx *ctx, uint64_t new_deadline)
3079+
{
3080+
uint64_t prev_deadline;
3081+
3082+
if (ctx->next_deadline < new_deadline)
3083+
return 0;
3084+
3085+
return mctp_ops.timerfd.settime(ctx->timerfd, new_deadline,
3086+
&prev_deadline);
3087+
}
3088+
3089+
static int peer_endpoint_recover(struct peer *peer)
30843090
{
3085-
struct peer *peer = userdata;
30863091
struct ctx *ctx = peer->ctx;
30873092
const char *peer_path;
30883093
int rc;
@@ -3145,8 +3150,7 @@ static int peer_endpoint_recover(sd_event_source *s, uint64_t usec,
31453150
/* It's not known to be the same device, allocate a new EID */
31463151
dest_phys phys = peer->phys;
31473152

3148-
assert(sd_event_source_get_enabled(
3149-
peer->recovery.source, NULL) == 0);
3153+
peer->recovery.deadline = UINT64_MAX;
31503154
remove_peer(peer);
31513155
/*
31523156
* The representation of the old peer is now gone. Set up the new peer,
@@ -3184,28 +3188,21 @@ static int peer_endpoint_recover(sd_event_source *s, uint64_t usec,
31843188
goto reschedule;
31853189
}
31863190

3187-
assert(sd_event_source_get_enabled(peer->recovery.source, NULL) == 0);
3188-
sd_event_source_unref(peer->recovery.source);
31893191
peer->recovery.delay = 0;
3190-
peer->recovery.source = NULL;
3192+
peer->recovery.deadline = UINT64_MAX;
31913193
peer->recovery.npolls = 0;
31923194

31933195
return rc;
31943196

31953197
reschedule:
31963198
if (peer->recovery.npolls > 0) {
3197-
rc = sd_event_source_set_time_relative(peer->recovery.source,
3198-
peer->recovery.delay);
3199-
if (rc >= 0) {
3200-
rc = sd_event_source_set_enabled(peer->recovery.source,
3201-
SD_EVENT_ONESHOT);
3202-
}
3199+
peer->recovery.deadline = ctx->now + peer->recovery.delay;
3200+
rc = update_deadline(ctx, peer->recovery.deadline);
32033201
}
32043202
if (rc < 0) {
32053203
reclaim:
32063204
/* Recovery unsuccessful, clean up the peer */
3207-
assert(sd_event_source_get_enabled(peer->recovery.source,
3208-
NULL) == 0);
3205+
peer->recovery.deadline = UINT64_MAX;
32093206
remove_peer(peer);
32103207
}
32113208
return rc < 0 ? rc : 0;
@@ -3225,18 +3222,13 @@ static int method_endpoint_recover(sd_bus_message *call, void *data,
32253222

32263223
if (!previously) {
32273224
assert(!peer->recovery.delay);
3228-
assert(!peer->recovery.source);
3225+
assert(peer->recovery.deadline == UINT64_MAX);
32293226
assert(!peer->recovery.npolls);
32303227
peer->recovery.npolls = MCTP_I2C_TSYM_MN1_MIN + 1;
32313228
peer->recovery.delay =
32323229
(MCTP_I2C_TSYM_TRECLAIM_MIN_US / 2) - ctx->mctp_timeout;
3233-
rc = sd_event_add_time_relative(
3234-
ctx->event, &peer->recovery.source, CLOCK_MONOTONIC, 0,
3235-
ctx->mctp_timeout, peer_endpoint_recover, peer);
3236-
if (rc < 0) {
3237-
goto out;
3238-
}
3239-
3230+
peer->recovery.deadline = ctx->now + ctx->mctp_timeout;
3231+
update_deadline(ctx, peer->recovery.deadline);
32403232
peer->degraded = true;
32413233

32423234
rc = sd_bus_emit_properties_changed(
@@ -3255,13 +3247,10 @@ static int method_endpoint_recover(sd_bus_message *call, void *data,
32553247
if (rc < 0 && !previously) {
32563248
if (peer->degraded) {
32573249
/* Cleanup the timer if it was setup successfully. */
3258-
sd_event_source_set_enabled(peer->recovery.source,
3259-
SD_EVENT_OFF);
3260-
sd_event_source_unref(peer->recovery.source);
3250+
peer->recovery.deadline = UINT64_MAX;
32613251
}
32623252
peer->degraded = previously;
32633253
peer->recovery.delay = 0;
3264-
peer->recovery.source = NULL;
32653254
peer->recovery.npolls = 0;
32663255
}
32673256
set_berr(ctx, rc, berr);
@@ -4949,6 +4938,52 @@ static int endpoint_allocate_eids(struct peer *peer)
49494938
return 0;
49504939
}
49514940

4941+
int global_time_callback(sd_event_source *source, int timerfd, uint revents,
4942+
void *userdata)
4943+
{
4944+
struct ctx *ctx = userdata;
4945+
uint64_t n_expireds;
4946+
size_t i;
4947+
4948+
mctp_ops.timerfd.read(timerfd, &n_expireds);
4949+
mctp_ops.timerfd.gettime(timerfd, &ctx->now);
4950+
4951+
for (i = 0; i < ctx->num_peers; i++) {
4952+
struct peer *p = ctx->peers[i];
4953+
if (p->recovery.deadline <= ctx->now) {
4954+
peer_endpoint_recover(p);
4955+
}
4956+
}
4957+
4958+
return 0;
4959+
}
4960+
4961+
int setup_timers(struct ctx *ctx)
4962+
{
4963+
int rc;
4964+
4965+
ctx->timerfd = mctp_ops.timerfd.create();
4966+
if (ctx->timerfd < 0) {
4967+
warn("timerfd_create");
4968+
return -errno;
4969+
}
4970+
4971+
rc = mctp_ops.timerfd.gettime(ctx->timerfd, &ctx->now);
4972+
if (rc < 0) {
4973+
warn("timerfd_gettime");
4974+
return -errno;
4975+
}
4976+
4977+
ctx->next_deadline = UINT64_MAX;
4978+
4979+
rc = sd_event_add_io(ctx->event, NULL, ctx->timerfd, EPOLLIN,
4980+
global_time_callback, ctx);
4981+
if (rc < 0)
4982+
return rc;
4983+
4984+
return 0;
4985+
}
4986+
49524987
int main(int argc, char **argv)
49534988
{
49544989
struct ctx ctxi = { 0 }, *ctx = &ctxi;
@@ -4997,6 +5032,10 @@ int main(int argc, char **argv)
49975032
if (rc < 0)
49985033
return 1;
49995034

5035+
rc = setup_timers(ctx);
5036+
if (rc < 0)
5037+
return 1;
5038+
50005039
// TODO add net argument?
50015040
rc = listen_control_msg(ctx, MCTP_NET_ANY);
50025041
if (rc < 0) {

tests/conftest.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11

22
import sys
3+
import math
34

45
import pytest
56
import asyncdbus
@@ -25,9 +26,12 @@ def config():
2526
return None
2627

2728
@pytest.fixture
28-
async def mctpd(nursery, dbus, sysnet, config):
29+
async def mctpd(nursery, dbus, sysnet, config, autojump_clock):
30+
autojump_clock.rate = 1
31+
autojump_clock.autojump_threshold = 1
2932
m = mctpenv.MctpdWrapper(dbus, sysnet, config = config)
3033
await m.start_mctpd(nursery)
34+
autojump_clock.autojump_threshold = math.inf
3135
yield m
3236
res = await m.stop_mctpd()
3337
assert res == 0

0 commit comments

Comments
 (0)