mirror of
https://gitlab.nic.cz/labs/bird.git
synced 2024-11-08 12:18:42 +00:00
Added universal locking mechanism which will solve problems
with protocols wanting to use the same port on the same interface during reconfiguration time. How to use locks: In the if_notify hook, just order locks for the interfaces you want to work with and do the real socket opening after the lock hook function gets called. When you stop using the socket, close it and rfree() the lock. Please update your protocols to use the new locking mechanism.
This commit is contained in:
parent
30bc402ebb
commit
f545d38707
@ -1,4 +1,4 @@
|
||||
source=rt-table.c rt-fib.c rt-attr.c proto.c iface.c rt-dev.c password.c cli.c
|
||||
source=rt-table.c rt-fib.c rt-attr.c proto.c iface.c rt-dev.c password.c cli.c locks.c
|
||||
root-rel=../
|
||||
dir-name=nest
|
||||
|
||||
|
147
nest/locks.c
Normal file
147
nest/locks.c
Normal file
@ -0,0 +1,147 @@
|
||||
/*
|
||||
* BIRD Object Locks
|
||||
*
|
||||
* (c) 1999 Martin Mares <mj@ucw.cz>
|
||||
*
|
||||
* Can be freely distributed and used under the terms of the GNU GPL.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#define LOCAL_DEBUG
|
||||
|
||||
#include "nest/bird.h"
|
||||
#include "lib/resource.h"
|
||||
#include "nest/locks.h"
|
||||
#include "nest/iface.h"
|
||||
|
||||
static list olock_list;
|
||||
static event *olock_event;
|
||||
|
||||
static inline int
|
||||
olock_same(struct object_lock *x, struct object_lock *y)
|
||||
{
|
||||
return
|
||||
x->type == y->type &&
|
||||
x->iface == y->iface &&
|
||||
x->port == y->port &&
|
||||
ipa_equal(x->addr, y->addr);
|
||||
}
|
||||
|
||||
static void
|
||||
olock_free(resource *r)
|
||||
{
|
||||
struct object_lock *q, *l = (struct object_lock *) r;
|
||||
node *n;
|
||||
|
||||
DBG("olock: Freeing %p\n", l);
|
||||
switch (l->state)
|
||||
{
|
||||
case OLOCK_STATE_FREE:
|
||||
break;
|
||||
case OLOCK_STATE_LOCKED:
|
||||
case OLOCK_STATE_EVENT:
|
||||
rem_node(&l->n);
|
||||
n = HEAD(l->waiters);
|
||||
if (n->next)
|
||||
{
|
||||
DBG("olock: -> %p becomes locked\n", n);
|
||||
q = SKIP_BACK(struct object_lock, n, n);
|
||||
rem_node(n);
|
||||
add_tail_list(&l->waiters, &q->waiters);
|
||||
q->state = OLOCK_STATE_EVENT;
|
||||
add_head(&olock_list, n);
|
||||
ev_schedule(olock_event);
|
||||
}
|
||||
break;
|
||||
case OLOCK_STATE_WAITING:
|
||||
rem_node(&l->n);
|
||||
break;
|
||||
default:
|
||||
ASSERT(0);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
olock_dump(resource *r)
|
||||
{
|
||||
struct object_lock *l = (struct object_lock *) r;
|
||||
static char *olock_states[] = { "free", "locked", "waiting", "event" };
|
||||
|
||||
debug("(%d:%s:%I:%d) [%s]", l->type, (l->iface ? l->iface->name : "?"), l->addr, l->port, olock_states[l->state]);
|
||||
if (!EMPTY_LIST(l->waiters))
|
||||
debug(" [wanted]\n");
|
||||
}
|
||||
|
||||
static struct resclass olock_class = {
|
||||
"ObjLock",
|
||||
sizeof(struct object_lock),
|
||||
olock_free,
|
||||
olock_dump
|
||||
};
|
||||
|
||||
struct object_lock *
|
||||
olock_new(pool *p)
|
||||
{
|
||||
struct object_lock *l = ralloc(p, &olock_class);
|
||||
|
||||
l->state = OLOCK_STATE_FREE;
|
||||
init_list(&l->waiters);
|
||||
return l;
|
||||
}
|
||||
|
||||
void
|
||||
olock_acquire(struct object_lock *l)
|
||||
{
|
||||
node *n;
|
||||
struct object_lock *q;
|
||||
|
||||
WALK_LIST(n, olock_list)
|
||||
{
|
||||
q = SKIP_BACK(struct object_lock, n, n);
|
||||
if (olock_same(q, l))
|
||||
{
|
||||
l->state = OLOCK_STATE_WAITING;
|
||||
add_tail(&q->waiters, &l->n);
|
||||
DBG("olock: %p waits\n", l);
|
||||
return;
|
||||
}
|
||||
}
|
||||
DBG("olock: %p acquired immediately\n", l);
|
||||
l->state = OLOCK_STATE_EVENT;
|
||||
add_head(&olock_list, &l->n);
|
||||
ev_schedule(olock_event);
|
||||
}
|
||||
|
||||
int
|
||||
olock_run_event(void *unused)
|
||||
{
|
||||
node *n;
|
||||
struct object_lock *q;
|
||||
|
||||
DBG("olock: Processing events\n");
|
||||
for(;;)
|
||||
{
|
||||
n = HEAD(olock_list);
|
||||
if (!n->next)
|
||||
break;
|
||||
q = SKIP_BACK(struct object_lock, n, n);
|
||||
if (q->state != OLOCK_STATE_EVENT)
|
||||
break;
|
||||
DBG("olock: %p locked\n", q);
|
||||
q->state = OLOCK_STATE_LOCKED;
|
||||
rem_node(&q->n);
|
||||
add_tail(&olock_list, &q->n);
|
||||
q->hook(q);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
olock_init(void)
|
||||
{
|
||||
DBG("olock: init\n");
|
||||
init_list(&olock_list);
|
||||
olock_event = ev_new(&root_pool);
|
||||
olock_event->hook = olock_run_event;
|
||||
}
|
52
nest/locks.h
Normal file
52
nest/locks.h
Normal file
@ -0,0 +1,52 @@
|
||||
/*
|
||||
* BIRD Object Locks
|
||||
*
|
||||
* (c) 1999 Martin Mares <mj@ucw.cz>
|
||||
*
|
||||
* Can be freely distributed and used under the terms of the GNU GPL.
|
||||
*/
|
||||
|
||||
#ifndef _BIRD_LOCKS_H_
|
||||
#define _BIRD_LOCKS_H_
|
||||
|
||||
#include "lib/resource.h"
|
||||
#include "lib/event.h"
|
||||
|
||||
/*
|
||||
* The object locks are used for controlling exclusive access
|
||||
* to various physical resources like UDP ports on specific devices.
|
||||
* When you want to access such resource, you ask for a object lock
|
||||
* structure, fill in specification of the object and your function
|
||||
* you want to have called when the object is available and invoke
|
||||
* olock_acquire() afterwards. When the object becomes free, the lock
|
||||
* manager calls your function. To free the object lock, just call rfree
|
||||
* on its resource.
|
||||
*/
|
||||
|
||||
struct object_lock {
|
||||
resource r;
|
||||
ip_addr addr; /* Identification of a object: IP address */
|
||||
unsigned int type; /* ... object type (OBJLOCK_xxx) */
|
||||
struct iface *iface; /* ... interface */
|
||||
unsigned int port; /* ... port number */
|
||||
void (*hook)(struct object_lock *); /* Called when the lock succeeds */
|
||||
void *data; /* User data */
|
||||
/* ... internal to lock manager, don't touch ... */
|
||||
node n; /* Node in list of olocks */
|
||||
int state; /* OLOCK_STATE_xxx */
|
||||
list waiters; /* Locks waiting for the same resource */
|
||||
};
|
||||
|
||||
struct object_lock *olock_new(pool *);
|
||||
void olock_acquire(struct object_lock *);
|
||||
void olock_init(void);
|
||||
|
||||
#define OBJLOCK_UDP 1 /* UDP port */
|
||||
#define OBJLOCK_TCP 2 /* TCP port */
|
||||
|
||||
#define OLOCK_STATE_FREE 0
|
||||
#define OLOCK_STATE_LOCKED 1
|
||||
#define OLOCK_STATE_WAITING 2
|
||||
#define OLOCK_STATE_EVENT 3 /* waiting for unlock processing */
|
||||
|
||||
#endif
|
@ -22,6 +22,7 @@
|
||||
#include "nest/protocol.h"
|
||||
#include "nest/iface.h"
|
||||
#include "nest/cli.h"
|
||||
#include "nest/locks.h"
|
||||
#include "conf/conf.h"
|
||||
#include "filter/filter.h"
|
||||
|
||||
@ -334,6 +335,7 @@ main(int argc, char **argv)
|
||||
|
||||
debug("Initializing.\n");
|
||||
resource_init();
|
||||
olock_init();
|
||||
io_init();
|
||||
rt_init();
|
||||
if_init();
|
||||
|
Loading…
Reference in New Issue
Block a user