www.pudn.com > dsr-uu-0.2.rar > dsr-rtc-simple.c


/* Copyright (C) Uppsala University
 *
 * This file is distributed under the terms of the GNU general Public
 * License (GPL), see the file LICENSE
 *
 * Author: Erik Nordström, 
 */
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#undef DEBUG
#include "tbl.h"
#include "dsr-rtc.h"
#include "dsr-srt.h"
#include "debug.h"

#define RTC_MAX_LEN 1024

static unsigned int rtc_len;
static rwlock_t rtc_lock = RW_LOCK_UNLOCKED;
static LIST_HEAD(rtc_head);
static TBL(rtc_tbl, RTC_MAX_LEN);

#define list_is_first(e) (&e->l == rtc_head.next)

MODULE_AUTHOR("Erik Nordstroem ");
MODULE_DESCRIPTION("Dynamic Source Routing (DSR) simple route cache");
MODULE_LICENSE("GPL");

/* Timers and timeouts could potentially be handled in the kernel. However,
 * currently they are not, because it complicates things quite a bit. The code
 * for adding timers is still here though... - Erik */

struct rtc_entry {
	list_t l;
	unsigned long expires;
	unsigned short flags;
	struct dsr_srt srt;
};

#define RTC_TIMER

#ifdef RTC_TIMER
static DSRUUTimer rtc_timer;

static void dsr_rtc_timeout(unsigned long data);

static inline void __dsr_rtc_set_next_timeout(void)
{
	struct rtc_entry *ne;

	if (list_empty(&rtc_head))
		return;

	/* Get first entry */
	ne = (struct rtc_entry *)rtc_head.next;

	if (timer_pending(&rtc_timer)) {
		mod_timer(&rtc_timer, ne->expires);
	} else {
		rtc_timer.function = dsr_rtc_timeout;
		rtc_timer.expires = ne->expires;
		rtc_timer.data = 0;
		add_timer(&rtc_timer);
	}
}

static void dsr_rtc_timeout(unsigned long data)
{
	list_t *pos, *tmp;
	int time = TimeNow;

	DSR_WRITE_LOCK(&rtc_lock);

	DEBUG("srt timeout\n");

	list_for_each_safe(pos, tmp, &rtc_head) {
		struct rtc_entry *e = (struct rtc_entry *)pos;

		if (e->expires > time)
			break;

		list_del(&e->l);
		FREE(e);
		rtc_len--;
	}
	__dsr_rtc_set_next_timeout();
	DSR_WRITE_UNLOCK(&rtc_lock);
}
#endif				/* RTC_TIMER */

static inline void __dsr_rtc_flush(void)
{
	list_t *pos, *tmp;

	list_for_each_safe(pos, tmp, &rtc_head) {
		struct rtc_entry *e = (struct rtc_entry *)pos;
		list_del(&e->l);
		rtc_len--;
		FREE(e);
	}
}

static inline int __dsr_rtc_add(struct rtc_entry *e)
{

	if (rtc_len >= RTC_MAX_LEN) {
		printk(KERN_WARNING "dsr_rtc: Max list len reached\n");
		return -ENOSPC;
	}

	if (list_empty(&rtc_head)) {
		list_add(&e->l, &rtc_head);
	} else {
		list_t *pos;

		list_for_each(pos, &rtc_head) {
			struct rtc_entry *curr = (struct rtc_entry *)pos;

			if (curr->expires > e->expires)
				break;
		}
		list_add(&e->l, pos->prev);
	}
	return 1;
}

static inline struct rtc_entry *__dsr_rtc_find(__u32 daddr)
{
	list_t *pos;

	list_for_each(pos, &rtc_head) {
		struct rtc_entry *e = (struct rtc_entry *)pos;

		if (e->srt.dst.s_addr == daddr)
			return e;
	}
	return NULL;
}

static inline int __dsr_rtc_del(struct rtc_entry *e)
{
	if (e == NULL)
		return 0;

	if (list_is_first(e)) {

		list_del(&e->l);
#ifdef RTC_TIMER
		if (!list_empty(&rtc_head)) {
			/* Get the first entry */
			struct rtc_entry *f = (struct rtc_entry *)rtc_head.next;

			/* Update the timer */
			mod_timer(&rtc_timer, f->expires);
		}
#endif
	} else
		list_del(&e->l);

	return 1;
}

int dsr_rtc_del(struct in_addr src, struct in_addr dst)
{
	int res;
	struct rtc_entry *e;

	DSR_WRITE_LOCK(&rtc_lock);

	e = __dsr_rtc_find(dst.s_addr);

	if (e == NULL) {
		res = 0;
		goto unlock;
	}

	res = __dsr_rtc_del(e);

	if (res)
		FREE(e);
      unlock:
	DSR_WRITE_UNLOCK(&rtc_lock);

	return res;
}

struct dsr_srt *dsr_rtc_find(struct in_addr src, struct in_addr dst)
{
	struct rtc_entry *e;
	struct dsr_srt *srt;

/*     printk("Checking activeness\n"); */
	DSR_READ_LOCK(&rtc_lock);
	e = __dsr_rtc_find(dst.s_addr);

	if (e) {
		/* We must make a copy of the source route so that we do not
		 * return a pointer into the shared data structure */
		srt =
		    MALLOC(e->srt.laddrs + sizeof(struct dsr_srt), GFP_ATOMIC);
		memcpy(srt, &e->srt, e->srt.laddrs + sizeof(struct dsr_srt));
		DSR_READ_UNLOCK(&rtc_lock);
		return srt;
	}
	DSR_READ_UNLOCK(&rtc_lock);
	return NULL;
}

int dsr_rtc_add(struct dsr_srt *srt, unsigned long time, unsigned short flags)
{
	struct rtc_entry *e;
	int status = 0;

	if (!srt || dsr_rtc_find(srt->src, srt->dst))
		return 0;

	DEBUG("Adding source route to route cache\n");

	e = MALLOC(sizeof(struct rtc_entry) + srt->laddrs, GFP_ATOMIC);

	if (e == NULL) {
		printk(KERN_ERR "rtc: OOM in rtc_add\n");
		return -ENOMEM;
	}

	e->flags = flags;
	e->expires = TimeNow + (time * HZ) / 1000;
	memcpy(&e->srt, srt, sizeof(struct dsr_srt));
	memcpy(e->srt.addrs, srt->addrs, srt->laddrs);

	DSR_WRITE_LOCK(&rtc_lock);

	status = __dsr_rtc_add(e);

	if (status)
		rtc_len++;

#ifdef RTC_TIMER
	/* If the added element was added first in the list we update the timer */
	if (status && list_is_first(e)) {

		if (timer_pending(&rtc_timer))
			mod_timer(&rtc_timer, e->expires);
		else {
			rtc_timer.function = dsr_rtc_timeout;
			rtc_timer.expires = e->expires;
			rtc_timer.data = 0;
			add_timer(&rtc_timer);
		}
	}
#endif
	DSR_WRITE_UNLOCK(&rtc_lock);

	if (status < 0) {
		DEBUG("add failed\n");
		FREE(e);
	}
	return status;
}

void
dsr_rtc_update(struct dsr_srt *srt, unsigned long time, unsigned short flags)
{
	struct rtc_entry *e;

	if (!srt)
		return;

	DSR_WRITE_LOCK(&rtc_lock);

	e = __dsr_rtc_find(srt->dst.s_addr);

	if (e == NULL) {
		/* printk("rtc_update: No entry to update!\n"); */
		goto unlock;
	}
	e->flags = flags;
	/* Update expire time */
	e->expires = TimeNow + (time * HZ) / 1000;
	memcpy(&e->srt, srt, sizeof(struct dsr_srt));
	memcpy(e->srt.addrs, srt->addrs, srt->laddrs);

	/* Remove from list */
	list_del(&e->l);
	__dsr_rtc_add(e);
#ifdef RTC_TIMER
	__dsr_rtc_set_next_timeout();
#endif

      unlock:
	DSR_WRITE_UNLOCK(&rtc_lock);
}

static int dsr_rtc_print(char *buf)
{
	list_t *pos;
	int len = 0;

	DSR_READ_LOCK(&rtc_lock);

	len += sprintf(buf, "# %-5s %-8s Source Route\n", "Flags", "Expires");

	list_for_each(pos, &rtc_head) {
		char flags[4];
		int num_flags = 0;
		struct rtc_entry *e = (struct rtc_entry *)pos;

		flags[num_flags] = '\0';

		len += sprintf(buf + len, "  %-5s %-8lu %s\n", flags,
			       (e->expires - TimeNow) * 1000 / HZ,
			       print_srt(&e->srt));
	}

	DSR_READ_UNLOCK(&rtc_lock);
	return len;
}
static int
dsr_rtc_proc_info(char *buffer, char **start, off_t offset, int length)
{
	int len;

	len = dsr_rtc_print(buffer);

	*start = buffer + offset;
	len -= offset;
	if (len > length)
		len = length;
	else if (len < 0)
		len = 0;
	return len;
}

void dsr_rtc_flush(void)
{
#ifdef RTC_TIMER
	if (timer_pending(&rtc_timer))
		del_timer(&rtc_timer);
#endif

	DSR_WRITE_LOCK(&rtc_lock);

	__dsr_rtc_flush();

	DSR_WRITE_UNLOCK(&rtc_lock);
}

int __init dsr_rtc_init(void)
{
	proc_net_create(DSR_RTC_PROC_NAME, 0, dsr_rtc_proc_info);

#ifdef RTC_TIMER
	init_timer(&rtc_timer);
#endif
	return 0;
}
void __exit dsr_rtc_cleanup(void)
{
	dsr_rtc_flush();
	proc_net_remove(DSR_RTC_PROC_NAME);
}

EXPORT_SYMBOL(dsr_rtc_add);
EXPORT_SYMBOL(dsr_rtc_del);
EXPORT_SYMBOL(dsr_rtc_find);
EXPORT_SYMBOL(dsr_rtc_flush);

module_init(dsr_rtc_init);
module_exit(dsr_rtc_cleanup);