]>
Commit | Line | Data |
---|---|---|
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 | ||
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->port == y->port && | |
a7a7372a | 49 | x->inst == y->inst && |
f545d387 MM |
50 | ipa_equal(x->addr, y->addr); |
51 | } | |
52 | ||
53 | static void | |
54 | olock_free(resource *r) | |
55 | { | |
56 | struct object_lock *q, *l = (struct object_lock *) r; | |
57 | node *n; | |
58 | ||
59 | DBG("olock: Freeing %p\n", l); | |
60 | switch (l->state) | |
61 | { | |
62 | case OLOCK_STATE_FREE: | |
63 | break; | |
64 | case OLOCK_STATE_LOCKED: | |
65 | case OLOCK_STATE_EVENT: | |
66 | rem_node(&l->n); | |
67 | n = HEAD(l->waiters); | |
68 | if (n->next) | |
69 | { | |
70 | DBG("olock: -> %p becomes locked\n", n); | |
71 | q = SKIP_BACK(struct object_lock, n, n); | |
72 | rem_node(n); | |
ab006391 | 73 | add_tail_list(&q->waiters, &l->waiters); |
f545d387 MM |
74 | q->state = OLOCK_STATE_EVENT; |
75 | add_head(&olock_list, n); | |
76 | ev_schedule(olock_event); | |
77 | } | |
78 | break; | |
79 | case OLOCK_STATE_WAITING: | |
80 | rem_node(&l->n); | |
81 | break; | |
82 | default: | |
83 | ASSERT(0); | |
84 | } | |
85 | } | |
86 | ||
87 | static void | |
88 | olock_dump(resource *r) | |
89 | { | |
90 | struct object_lock *l = (struct object_lock *) r; | |
91 | static char *olock_states[] = { "free", "locked", "waiting", "event" }; | |
92 | ||
a7a7372a | 93 | 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 |
94 | if (!EMPTY_LIST(l->waiters)) |
95 | debug(" [wanted]\n"); | |
96 | } | |
97 | ||
98 | static struct resclass olock_class = { | |
99 | "ObjLock", | |
100 | sizeof(struct object_lock), | |
101 | olock_free, | |
e81b440f | 102 | olock_dump, |
3e236955 JMM |
103 | NULL, |
104 | NULL, | |
f545d387 MM |
105 | }; |
106 | ||
1f495723 MM |
107 | /** |
108 | * olock_new - create an object lock | |
109 | * @p: resource pool to create the lock in. | |
110 | * | |
111 | * The olock_new() function creates a new resource of type &object_lock | |
112 | * and returns a pointer to it. After filling in the structure, the caller | |
113 | * should call olock_acquire() to do the real locking. | |
114 | */ | |
f545d387 MM |
115 | struct object_lock * |
116 | olock_new(pool *p) | |
117 | { | |
118 | struct object_lock *l = ralloc(p, &olock_class); | |
119 | ||
120 | l->state = OLOCK_STATE_FREE; | |
121 | init_list(&l->waiters); | |
122 | return l; | |
123 | } | |
124 | ||
1f495723 MM |
125 | /** |
126 | * olock_acquire - acquire a lock | |
127 | * @l: the lock to acquire | |
128 | * | |
129 | * This function attempts to acquire exclusive access to the non-shareable | |
130 | * resource described by the lock @l. It returns immediately, but as soon | |
131 | * as the resource becomes available, it calls the hook() function set up | |
132 | * by the caller. | |
133 | * | |
134 | * When you want to release the resource, just rfree() the lock. | |
135 | */ | |
f545d387 MM |
136 | void |
137 | olock_acquire(struct object_lock *l) | |
138 | { | |
139 | node *n; | |
140 | struct object_lock *q; | |
141 | ||
142 | WALK_LIST(n, olock_list) | |
143 | { | |
144 | q = SKIP_BACK(struct object_lock, n, n); | |
145 | if (olock_same(q, l)) | |
146 | { | |
147 | l->state = OLOCK_STATE_WAITING; | |
148 | add_tail(&q->waiters, &l->n); | |
149 | DBG("olock: %p waits\n", l); | |
150 | return; | |
151 | } | |
152 | } | |
153 | DBG("olock: %p acquired immediately\n", l); | |
154 | l->state = OLOCK_STATE_EVENT; | |
155 | add_head(&olock_list, &l->n); | |
156 | ev_schedule(olock_event); | |
157 | } | |
158 | ||
8f6accb5 | 159 | static void |
7c103b1e | 160 | olock_run_event(void *unused UNUSED) |
f545d387 MM |
161 | { |
162 | node *n; | |
163 | struct object_lock *q; | |
164 | ||
165 | DBG("olock: Processing events\n"); | |
166 | for(;;) | |
167 | { | |
168 | n = HEAD(olock_list); | |
169 | if (!n->next) | |
170 | break; | |
171 | q = SKIP_BACK(struct object_lock, n, n); | |
172 | if (q->state != OLOCK_STATE_EVENT) | |
173 | break; | |
174 | DBG("olock: %p locked\n", q); | |
175 | q->state = OLOCK_STATE_LOCKED; | |
176 | rem_node(&q->n); | |
177 | add_tail(&olock_list, &q->n); | |
178 | q->hook(q); | |
179 | } | |
f545d387 MM |
180 | } |
181 | ||
1f495723 MM |
182 | /** |
183 | * olock_init - initialize the object lock mechanism | |
184 | * | |
185 | * This function is called during BIRD startup. It initializes | |
186 | * all the internal data structures of the lock module. | |
187 | */ | |
f545d387 MM |
188 | void |
189 | olock_init(void) | |
190 | { | |
191 | DBG("olock: init\n"); | |
192 | init_list(&olock_list); | |
193 | olock_event = ev_new(&root_pool); | |
194 | olock_event->hook = olock_run_event; | |
195 | } |