mirror of
				https://gitlab.labs.nic.cz/labs/bird.git
				synced 2024-05-11 16:54:54 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			192 lines
		
	
	
		
			4.8 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			192 lines
		
	
	
		
			4.8 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /*
 | |
|  *	BIRD Object Locks
 | |
|  *
 | |
|  *	(c) 1999 Martin Mares <mj@ucw.cz>
 | |
|  *
 | |
|  *	Can be freely distributed and used under the terms of the GNU GPL.
 | |
|  */
 | |
| 
 | |
| /**
 | |
|  * DOC: Object locks
 | |
|  *
 | |
|  * The lock module provides a simple mechanism for avoiding conflicts between
 | |
|  * various protocols which would like to use a single physical resource (for
 | |
|  * example a network port). It would be easy to say that such collisions can
 | |
|  * occur only when the user specifies an invalid configuration and therefore
 | |
|  * he deserves to get what he has asked for, but unfortunately they can also
 | |
|  * arise legitimately when the daemon is reconfigured and there exists (although
 | |
|  * for a short time period only) an old protocol instance being shut down and a new one
 | |
|  * willing to start up on the same interface.
 | |
|  *
 | |
|  * The solution is very simple: when any protocol wishes to use a network port
 | |
|  * or some other non-shareable resource, it asks the core to lock it and it doesn't
 | |
|  * use the resource until it's notified that it has acquired the lock.
 | |
|  *
 | |
|  * Object locks are represented by &object_lock structures which are in turn a kind of
 | |
|  * resource. Lockable resources are uniquely determined by resource type
 | |
|  * (%OBJLOCK_UDP for a UDP port etc.), IP address (usually a broadcast or
 | |
|  * multicast address the port is bound to), port number and interface.
 | |
|  */
 | |
| 
 | |
| #undef 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]\n", 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
 | |
| };
 | |
| 
 | |
| /**
 | |
|  * olock_new - create an object lock
 | |
|  * @p: resource pool to create the lock in.
 | |
|  *
 | |
|  * The olock_new() function creates a new resource of type &object_lock
 | |
|  * and returns a pointer to it. After filling in the structure, the caller
 | |
|  * should call olock_acquire() to do the real locking.
 | |
|  */
 | |
| 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;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * olock_acquire - acquire a lock
 | |
|  * @l: the lock to acquire
 | |
|  *
 | |
|  * This function attempts to acquire exclusive access to the non-shareable
 | |
|  * resource described by the lock @l. It returns immediately, but as soon
 | |
|  * as the resource becomes available, it calls the hook() function set up
 | |
|  * by the caller.
 | |
|  *
 | |
|  * When you want to release the resource, just rfree() the lock.
 | |
|  */
 | |
| 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);
 | |
| }
 | |
| 
 | |
| static void
 | |
| olock_run_event(void *unused 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);
 | |
|     }
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * olock_init - initialize the object lock mechanism
 | |
|  *
 | |
|  * This function is called during BIRD startup. It initializes
 | |
|  * all the internal data structures of the lock module.
 | |
|  */
 | |
| void
 | |
| olock_init(void)
 | |
| {
 | |
|   DBG("olock: init\n");
 | |
|   init_list(&olock_list);
 | |
|   olock_event = ev_new(&root_pool);
 | |
|   olock_event->hook = olock_run_event;
 | |
| }
 |