]> git.ipfire.org Git - thirdparty/bird.git/blame - nest/locks.c
Merge remote-tracking branch 'origin/master' into mq-filter-stack
[thirdparty/bird.git] / nest / locks.c
CommitLineData
f545d387
MM
1/*
2 * BIRD Object Locks
3 *
4 * (c) 1999 Martin Mares <mj@ucw.cz>
5 *
6 * Can be freely distributed and used under the terms of the GNU GPL.
7 */
8
1f495723
MM
9/**
10 * DOC: Object locks
11 *
12 * The lock module provides a simple mechanism for avoiding conflicts between
13 * various protocols which would like to use a single physical resource (for
14 * example a network port). It would be easy to say that such collisions can
15 * occur only when the user specifies an invalid configuration and therefore
16 * he deserves to get what he has asked for, but unfortunately they can also
17 * arise legitimately when the daemon is reconfigured and there exists (although
58f7d004 18 * for a short time period only) an old protocol instance being shut down and a new one
1f495723
MM
19 * willing to start up on the same interface.
20 *
21 * The solution is very simple: when any protocol wishes to use a network port
58f7d004 22 * or some other non-shareable resource, it asks the core to lock it and it doesn't
1f495723
MM
23 * use the resource until it's notified that it has acquired the lock.
24 *
a7a7372a
OZ
25 * Object locks are represented by &object_lock structures which are in turn a
26 * kind of resource. Lockable resources are uniquely determined by resource type
1f495723 27 * (%OBJLOCK_UDP for a UDP port etc.), IP address (usually a broadcast or
a7a7372a
OZ
28 * multicast address the port is bound to), port number, interface and optional
29 * instance ID.
1f495723
MM
30 */
31
1d9622e1 32#undef LOCAL_DEBUG
f545d387
MM
33
34#include "nest/bird.h"
35#include "lib/resource.h"
36#include "nest/locks.h"
37#include "nest/iface.h"
38
39static list olock_list;
40static event *olock_event;
41
42static inline int
43olock_same(struct object_lock *x, struct object_lock *y)
44{
45 return
46 x->type == y->type &&
47 x->iface == y->iface &&
9f4908fe 48 x->vrf == y->vrf &&
f545d387 49 x->port == y->port &&
a7a7372a 50 x->inst == y->inst &&
f545d387
MM
51 ipa_equal(x->addr, y->addr);
52}
53
54static void
55olock_free(resource *r)
56{
57 struct object_lock *q, *l = (struct object_lock *) r;
58 node *n;
59
60 DBG("olock: Freeing %p\n", l);
61 switch (l->state)
62 {
63 case OLOCK_STATE_FREE:
64 break;
65 case OLOCK_STATE_LOCKED:
66 case OLOCK_STATE_EVENT:
67 rem_node(&l->n);
68 n = HEAD(l->waiters);
69 if (n->next)
70 {
71 DBG("olock: -> %p becomes locked\n", n);
72 q = SKIP_BACK(struct object_lock, n, n);
73 rem_node(n);
ab006391 74 add_tail_list(&q->waiters, &l->waiters);
f545d387
MM
75 q->state = OLOCK_STATE_EVENT;
76 add_head(&olock_list, n);
77 ev_schedule(olock_event);
78 }
79 break;
80 case OLOCK_STATE_WAITING:
81 rem_node(&l->n);
82 break;
83 default:
84 ASSERT(0);
85 }
86}
87
88static void
89olock_dump(resource *r)
90{
91 struct object_lock *l = (struct object_lock *) r;
92 static char *olock_states[] = { "free", "locked", "waiting", "event" };
93
a7a7372a 94 debug("(%d:%s:%I:%d:%d) [%s]\n", l->type, (l->iface ? l->iface->name : "?"), l->addr, l->port, l->inst, olock_states[l->state]);
f545d387
MM
95 if (!EMPTY_LIST(l->waiters))
96 debug(" [wanted]\n");
97}
98
99static struct resclass olock_class = {
100 "ObjLock",
101 sizeof(struct object_lock),
102 olock_free,
e81b440f 103 olock_dump,
3e236955
JMM
104 NULL,
105 NULL,
f545d387
MM
106};
107
1f495723
MM
108/**
109 * olock_new - create an object lock
110 * @p: resource pool to create the lock in.
111 *
112 * The olock_new() function creates a new resource of type &object_lock
113 * and returns a pointer to it. After filling in the structure, the caller
114 * should call olock_acquire() to do the real locking.
115 */
f545d387
MM
116struct object_lock *
117olock_new(pool *p)
118{
119 struct object_lock *l = ralloc(p, &olock_class);
120
121 l->state = OLOCK_STATE_FREE;
122 init_list(&l->waiters);
123 return l;
124}
125
1f495723
MM
126/**
127 * olock_acquire - acquire a lock
128 * @l: the lock to acquire
129 *
130 * This function attempts to acquire exclusive access to the non-shareable
131 * resource described by the lock @l. It returns immediately, but as soon
132 * as the resource becomes available, it calls the hook() function set up
133 * by the caller.
134 *
135 * When you want to release the resource, just rfree() the lock.
136 */
f545d387
MM
137void
138olock_acquire(struct object_lock *l)
139{
140 node *n;
141 struct object_lock *q;
142
143 WALK_LIST(n, olock_list)
144 {
145 q = SKIP_BACK(struct object_lock, n, n);
146 if (olock_same(q, l))
147 {
148 l->state = OLOCK_STATE_WAITING;
149 add_tail(&q->waiters, &l->n);
150 DBG("olock: %p waits\n", l);
151 return;
152 }
153 }
154 DBG("olock: %p acquired immediately\n", l);
155 l->state = OLOCK_STATE_EVENT;
156 add_head(&olock_list, &l->n);
157 ev_schedule(olock_event);
158}
159
8f6accb5 160static void
7c103b1e 161olock_run_event(void *unused UNUSED)
f545d387
MM
162{
163 node *n;
164 struct object_lock *q;
165
166 DBG("olock: Processing events\n");
167 for(;;)
168 {
169 n = HEAD(olock_list);
170 if (!n->next)
171 break;
172 q = SKIP_BACK(struct object_lock, n, n);
173 if (q->state != OLOCK_STATE_EVENT)
174 break;
175 DBG("olock: %p locked\n", q);
176 q->state = OLOCK_STATE_LOCKED;
177 rem_node(&q->n);
178 add_tail(&olock_list, &q->n);
179 q->hook(q);
180 }
f545d387
MM
181}
182
1f495723
MM
183/**
184 * olock_init - initialize the object lock mechanism
185 *
186 * This function is called during BIRD startup. It initializes
187 * all the internal data structures of the lock module.
188 */
f545d387
MM
189void
190olock_init(void)
191{
192 DBG("olock: init\n");
193 init_list(&olock_list);
961671c0 194 olock_event = ev_new_init(&root_pool, olock_run_event, NULL);
f545d387 195}