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