]>
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 OZ |
102 | olock_dump, |
103 | NULL | |
f545d387 MM |
104 | }; |
105 | ||
1f495723 MM |
106 | /** |
107 | * olock_new - create an object lock | |
108 | * @p: resource pool to create the lock in. | |
109 | * | |
110 | * The olock_new() function creates a new resource of type &object_lock | |
111 | * and returns a pointer to it. After filling in the structure, the caller | |
112 | * should call olock_acquire() to do the real locking. | |
113 | */ | |
f545d387 MM |
114 | struct object_lock * |
115 | olock_new(pool *p) | |
116 | { | |
117 | struct object_lock *l = ralloc(p, &olock_class); | |
118 | ||
119 | l->state = OLOCK_STATE_FREE; | |
120 | init_list(&l->waiters); | |
121 | return l; | |
122 | } | |
123 | ||
1f495723 MM |
124 | /** |
125 | * olock_acquire - acquire a lock | |
126 | * @l: the lock to acquire | |
127 | * | |
128 | * This function attempts to acquire exclusive access to the non-shareable | |
129 | * resource described by the lock @l. It returns immediately, but as soon | |
130 | * as the resource becomes available, it calls the hook() function set up | |
131 | * by the caller. | |
132 | * | |
133 | * When you want to release the resource, just rfree() the lock. | |
134 | */ | |
f545d387 MM |
135 | void |
136 | olock_acquire(struct object_lock *l) | |
137 | { | |
138 | node *n; | |
139 | struct object_lock *q; | |
140 | ||
141 | WALK_LIST(n, olock_list) | |
142 | { | |
143 | q = SKIP_BACK(struct object_lock, n, n); | |
144 | if (olock_same(q, l)) | |
145 | { | |
146 | l->state = OLOCK_STATE_WAITING; | |
147 | add_tail(&q->waiters, &l->n); | |
148 | DBG("olock: %p waits\n", l); | |
149 | return; | |
150 | } | |
151 | } | |
152 | DBG("olock: %p acquired immediately\n", l); | |
153 | l->state = OLOCK_STATE_EVENT; | |
154 | add_head(&olock_list, &l->n); | |
155 | ev_schedule(olock_event); | |
156 | } | |
157 | ||
8f6accb5 | 158 | static void |
7c103b1e | 159 | olock_run_event(void *unused UNUSED) |
f545d387 MM |
160 | { |
161 | node *n; | |
162 | struct object_lock *q; | |
163 | ||
164 | DBG("olock: Processing events\n"); | |
165 | for(;;) | |
166 | { | |
167 | n = HEAD(olock_list); | |
168 | if (!n->next) | |
169 | break; | |
170 | q = SKIP_BACK(struct object_lock, n, n); | |
171 | if (q->state != OLOCK_STATE_EVENT) | |
172 | break; | |
173 | DBG("olock: %p locked\n", q); | |
174 | q->state = OLOCK_STATE_LOCKED; | |
175 | rem_node(&q->n); | |
176 | add_tail(&olock_list, &q->n); | |
177 | q->hook(q); | |
178 | } | |
f545d387 MM |
179 | } |
180 | ||
1f495723 MM |
181 | /** |
182 | * olock_init - initialize the object lock mechanism | |
183 | * | |
184 | * This function is called during BIRD startup. It initializes | |
185 | * all the internal data structures of the lock module. | |
186 | */ | |
f545d387 MM |
187 | void |
188 | olock_init(void) | |
189 | { | |
190 | DBG("olock: init\n"); | |
191 | init_list(&olock_list); | |
192 | olock_event = ev_new(&root_pool); | |
193 | olock_event->hook = olock_run_event; | |
194 | } |