]> git.ipfire.org Git - thirdparty/bird.git/blob - nest/locks.c
Renamed my past self in commit authorship and mentioned that in the contributing...
[thirdparty/bird.git] / nest / locks.c
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
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
18 * for a short time period only) an old protocol instance being shut down and a new one
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
22 * or some other non-shareable resource, it asks the core to lock it and it doesn't
23 * use the resource until it's notified that it has acquired the lock.
24 *
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
27 * (%OBJLOCK_UDP for a UDP port etc.), IP address (usually a broadcast or
28 * multicast address the port is bound to), port number, interface and optional
29 * instance ID.
30 */
31
32 #undef LOCAL_DEBUG
33
34 #include "nest/bird.h"
35 #include "lib/resource.h"
36 #include "nest/locks.h"
37 #include "nest/iface.h"
38
39 static list olock_list;
40 static event *olock_event;
41
42 static inline int
43 olock_same(struct object_lock *x, struct object_lock *y)
44 {
45 return
46 x->type == y->type &&
47 x->iface == y->iface &&
48 x->vrf == y->vrf &&
49 x->port == y->port &&
50 x->inst == y->inst &&
51 ipa_equal(x->addr, y->addr) &&
52 ipa_equal_wildcard(x->addr_local, y->addr_local);
53 }
54
55 static void
56 olock_free(resource *r)
57 {
58 struct object_lock *q, *l = (struct object_lock *) r;
59 node *n;
60
61 DBG("olock: Freeing %p\n", l);
62 switch (l->state)
63 {
64 case OLOCK_STATE_FREE:
65 break;
66 case OLOCK_STATE_LOCKED:
67 case OLOCK_STATE_EVENT:
68 rem_node(&l->n);
69 n = HEAD(l->waiters);
70 if (n->next)
71 {
72 DBG("olock: -> %p becomes locked\n", n);
73 q = SKIP_BACK(struct object_lock, n, n);
74 rem_node(n);
75 add_tail_list(&q->waiters, &l->waiters);
76 q->state = OLOCK_STATE_EVENT;
77 add_head(&olock_list, n);
78 ev_schedule(olock_event);
79 }
80 break;
81 case OLOCK_STATE_WAITING:
82 rem_node(&l->n);
83 break;
84 default:
85 ASSERT(0);
86 }
87 }
88
89 static void
90 olock_dump(resource *r)
91 {
92 struct object_lock *l = (struct object_lock *) r;
93 static char *olock_states[] = { "free", "locked", "waiting", "event" };
94
95 debug("(%d:%s:%I:%I:%d:%d) [%s]\n", l->type, (l->iface ? l->iface->name : "?"), l->addr, l->addr_local, l->port, l->inst, olock_states[l->state]);
96 if (!EMPTY_LIST(l->waiters))
97 debug(" [wanted]\n");
98 }
99
100 static struct resclass olock_class = {
101 "ObjLock",
102 sizeof(struct object_lock),
103 olock_free,
104 olock_dump,
105 NULL,
106 NULL,
107 };
108
109 /**
110 * olock_new - create an object lock
111 * @p: resource pool to create the lock in.
112 *
113 * The olock_new() function creates a new resource of type &object_lock
114 * and returns a pointer to it. After filling in the structure, the caller
115 * should call olock_acquire() to do the real locking.
116 */
117 struct object_lock *
118 olock_new(pool *p)
119 {
120 struct object_lock *l = ralloc(p, &olock_class);
121
122 l->state = OLOCK_STATE_FREE;
123 init_list(&l->waiters);
124 return l;
125 }
126
127 /**
128 * olock_acquire - acquire a lock
129 * @l: the lock to acquire
130 *
131 * This function attempts to acquire exclusive access to the non-shareable
132 * resource described by the lock @l. It returns immediately, but as soon
133 * as the resource becomes available, it calls the hook() function set up
134 * by the caller.
135 *
136 * When you want to release the resource, just rfree() the lock.
137 */
138 void
139 olock_acquire(struct object_lock *l)
140 {
141 node *n;
142 struct object_lock *q;
143
144 WALK_LIST(n, olock_list)
145 {
146 q = SKIP_BACK(struct object_lock, n, n);
147 if (olock_same(q, l))
148 {
149 l->state = OLOCK_STATE_WAITING;
150 add_tail(&q->waiters, &l->n);
151 DBG("olock: %p waits\n", l);
152 return;
153 }
154 }
155 DBG("olock: %p acquired immediately\n", l);
156 l->state = OLOCK_STATE_EVENT;
157 add_head(&olock_list, &l->n);
158 ev_schedule(olock_event);
159 }
160
161 static void
162 olock_run_event(void *unused UNUSED)
163 {
164 node *n;
165 struct object_lock *q;
166
167 DBG("olock: Processing events\n");
168 for(;;)
169 {
170 n = HEAD(olock_list);
171 if (!n->next)
172 break;
173 q = SKIP_BACK(struct object_lock, n, n);
174 if (q->state != OLOCK_STATE_EVENT)
175 break;
176 DBG("olock: %p locked\n", q);
177 q->state = OLOCK_STATE_LOCKED;
178 rem_node(&q->n);
179 add_tail(&olock_list, &q->n);
180 q->hook(q);
181 }
182 }
183
184 /**
185 * olock_init - initialize the object lock mechanism
186 *
187 * This function is called during BIRD startup. It initializes
188 * all the internal data structures of the lock module.
189 */
190 void
191 olock_init(void)
192 {
193 DBG("olock: init\n");
194 init_list(&olock_list);
195 olock_event = ev_new_init(&root_pool, olock_run_event, NULL);
196 }