]>
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 && | |
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 | ||
54 | static void | |
55 | olock_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 | ||
88 | static void | |
89 | olock_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 | ||
99 | static 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 |
116 | struct object_lock * |
117 | olock_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 |
137 | void |
138 | olock_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 | 160 | static void |
7c103b1e | 161 | olock_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 |
189 | void |
190 | olock_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 | } |