diff options
Diffstat (limited to 'drivers/staging/batman-adv/vis.c')
| -rw-r--r-- | drivers/staging/batman-adv/vis.c | 597 |
1 files changed, 0 insertions, 597 deletions
diff --git a/drivers/staging/batman-adv/vis.c b/drivers/staging/batman-adv/vis.c deleted file mode 100644 index fedec1bb309..00000000000 --- a/drivers/staging/batman-adv/vis.c +++ /dev/null @@ -1,597 +0,0 @@ -/* - * Copyright (C) 2008-2009 B.A.T.M.A.N. contributors: - * - * Simon Wunderlich - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of version 2 of the GNU General Public - * License as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA - * 02110-1301, USA - * - */ - -#include "main.h" -#include "send.h" -#include "translation-table.h" -#include "vis.h" -#include "soft-interface.h" -#include "hard-interface.h" -#include "hash.h" - -struct hashtable_t *vis_hash; -DEFINE_SPINLOCK(vis_hash_lock); -static struct vis_info *my_vis_info; -static struct list_head send_list; /* always locked with vis_hash_lock */ - -static void start_vis_timer(void); - -/* free the info */ -static void free_info(void *data) -{ - struct vis_info *info = data; - struct recvlist_node *entry, *tmp; - - list_del_init(&info->send_list); - list_for_each_entry_safe(entry, tmp, &info->recv_list, list) { - list_del(&entry->list); - kfree(entry); - } - kfree(info); -} - -/* Compare two vis packets, used by the hashing algorithm */ -static int vis_info_cmp(void *data1, void *data2) -{ - struct vis_info *d1, *d2; - d1 = data1; - d2 = data2; - return compare_orig(d1->packet.vis_orig, d2->packet.vis_orig); -} - -/* hash function to choose an entry in a hash table of given size */ -/* hash algorithm from http://en.wikipedia.org/wiki/Hash_table */ -static int vis_info_choose(void *data, int size) -{ - struct vis_info *vis_info = data; - unsigned char *key; - uint32_t hash = 0; - size_t i; - - key = vis_info->packet.vis_orig; - for (i = 0; i < ETH_ALEN; i++) { - hash += key[i]; - hash += (hash << 10); - hash ^= (hash >> 6); - } - - hash += (hash << 3); - hash ^= (hash >> 11); - hash += (hash << 15); - - return hash % size; -} - -/* insert interface to the list of interfaces of one originator, if it - * does not already exist in the list */ -static void proc_vis_insert_interface(const uint8_t *interface, - struct hlist_head *if_list, - bool primary) -{ - struct if_list_entry *entry; - struct hlist_node *pos; - - hlist_for_each_entry(entry, pos, if_list, list) { - if (compare_orig(entry->addr, (void *)interface)) - return; - } - - /* its a new address, add it to the list */ - entry = kmalloc(sizeof(*entry), GFP_KERNEL); - if (!entry) - return; - memcpy(entry->addr, interface, ETH_ALEN); - entry->primary = primary; - hlist_add_head(&entry->list, if_list); -} - -void proc_vis_read_prim_sec(struct seq_file *seq, - struct hlist_head *if_list) -{ - struct if_list_entry *entry; - struct hlist_node *pos, *n; - char tmp_addr_str[ETH_STR_LEN]; - - hlist_for_each_entry_safe(entry, pos, n, if_list, list) { - if (entry->primary) { - seq_printf(seq, "PRIMARY, "); - } else { - addr_to_string(tmp_addr_str, entry->addr); - seq_printf(seq, "SEC %s, ", tmp_addr_str); - } - - hlist_del(&entry->list); - kfree(entry); - } -} - -/* read an entry */ -void proc_vis_read_entry(struct seq_file *seq, - struct vis_info_entry *entry, - struct hlist_head *if_list, - uint8_t *vis_orig) -{ - char to[40]; - - addr_to_string(to, entry->dest); - if (entry->quality == 0) { - proc_vis_insert_interface(vis_orig, if_list, true); - seq_printf(seq, "HNA %s, ", to); - } else { - proc_vis_insert_interface(entry->src, if_list, - compare_orig(entry->src, vis_orig)); - seq_printf(seq, "TQ %s %d, ", to, entry->quality); - } -} - -/* tries to add one entry to the receive list. */ -static void recv_list_add(struct list_head *recv_list, char *mac) -{ - struct recvlist_node *entry; - entry = kmalloc(sizeof(struct recvlist_node), GFP_ATOMIC); - if (!entry) - return; - - memcpy(entry->mac, mac, ETH_ALEN); - list_add_tail(&entry->list, recv_list); -} - -/* returns 1 if this mac is in the recv_list */ -static int recv_list_is_in(struct list_head *recv_list, char *mac) -{ - struct recvlist_node *entry; - - list_for_each_entry(entry, recv_list, list) { - if (memcmp(entry->mac, mac, ETH_ALEN) == 0) - return 1; - } - - return 0; -} - -/* try to add the packet to the vis_hash. return NULL if invalid (e.g. too old, - * broken.. ). vis hash must be locked outside. is_new is set when the packet - * is newer than old entries in the hash. */ -static struct vis_info *add_packet(struct vis_packet *vis_packet, - int vis_info_len, int *is_new) -{ - struct vis_info *info, *old_info; - struct vis_info search_elem; - - *is_new = 0; - /* sanity check */ - if (vis_hash == NULL) - return NULL; - - /* see if the packet is already in vis_hash */ - memcpy(search_elem.packet.vis_orig, vis_packet->vis_orig, ETH_ALEN); - old_info = hash_find(vis_hash, &search_elem); - - if (old_info != NULL) { - if (vis_packet->seqno - old_info->packet.seqno <= 0) { - if (old_info->packet.seqno == vis_packet->seqno) { - recv_list_add(&old_info->recv_list, - vis_packet->sender_orig); - return old_info; - } else { - /* newer packet is already in hash. */ - return NULL; - } - } - /* remove old entry */ - hash_remove(vis_hash, old_info); - free_info(old_info); - } - - info = kmalloc(sizeof(struct vis_info) + vis_info_len, GFP_ATOMIC); - if (info == NULL) - return NULL; - - INIT_LIST_HEAD(&info->send_list); - INIT_LIST_HEAD(&info->recv_list); - info->first_seen = jiffies; - memcpy(&info->packet, vis_packet, - sizeof(struct vis_packet) + vis_info_len); - - /* initialize and add new packet. */ - *is_new = 1; - - /* repair if entries is longer than packet. */ - if (info->packet.entries * sizeof(struct vis_info_entry) > vis_info_len) - info->packet.entries = vis_info_len / sizeof(struct vis_info_entry); - - recv_list_add(&info->recv_list, info->packet.sender_orig); - - /* try to add it */ - if (hash_add(vis_hash, info) < 0) { - /* did not work (for some reason) */ - free_info(info); - info = NULL; - } - - return info; -} - -/* handle the server sync packet, forward if needed. */ -void receive_server_sync_packet(struct vis_packet *vis_packet, int vis_info_len) -{ - struct vis_info *info; - int is_new; - unsigned long flags; - int vis_server = atomic_read(&vis_mode); - - spin_lock_irqsave(&vis_hash_lock, flags); - info = add_packet(vis_packet, vis_info_len, &is_new); - if (info == NULL) - goto end; - - /* only if we are server ourselves and packet is newer than the one in - * hash.*/ - if (vis_server == VIS_TYPE_SERVER_SYNC && is_new) { - memcpy(info->packet.target_orig, broadcastAddr, ETH_ALEN); - if (list_empty(&info->send_list)) - list_add_tail(&info->send_list, &send_list); - } -end: - spin_unlock_irqrestore(&vis_hash_lock, flags); -} - -/* handle an incoming client update packet and schedule forward if needed. */ -void receive_client_update_packet(struct vis_packet *vis_packet, - int vis_info_len) -{ - struct vis_info *info; - int is_new; - unsigned long flags; - int vis_server = atomic_read(&vis_mode); - - /* clients shall not broadcast. */ - if (is_bcast(vis_packet->target_orig)) - return; - - spin_lock_irqsave(&vis_hash_lock, flags); - info = add_packet(vis_packet, vis_info_len, &is_new); - if (info == NULL) - goto end; - /* note that outdated packets will be dropped at this point. */ - - - /* send only if we're the target server or ... */ - if (vis_server == VIS_TYPE_SERVER_SYNC && - is_my_mac(info->packet.target_orig) && - is_new) { - info->packet.vis_type = VIS_TYPE_SERVER_SYNC; /* upgrade! */ - memcpy(info->packet.target_orig, broadcastAddr, ETH_ALEN); - if (list_empty(&info->send_list)) - list_add_tail(&info->send_list, &send_list); - - /* ... we're not the recipient (and thus need to forward). */ - } else if (!is_my_mac(info->packet.target_orig)) { - if (list_empty(&info->send_list)) - list_add_tail(&info->send_list, &send_list); - } -end: - spin_unlock_irqrestore(&vis_hash_lock, flags); -} - -/* Walk the originators and find the VIS server with the best tq. Set the packet - * address to its address and return the best_tq. - * - * Must be called with the originator hash locked */ -static int find_best_vis_server(struct vis_info *info) -{ - HASHIT(hashit); - struct orig_node *orig_node; - int best_tq = -1; - - while (hash_iterate(orig_hash, &hashit)) { - orig_node = hashit.bucket->data; - if ((orig_node != NULL) && - (orig_node->router != NULL) && - (orig_node->flags & VIS_SERVER) && - (orig_node->router->tq_avg > best_tq)) { - best_tq = orig_node->router->tq_avg; - memcpy(info->packet.target_orig, orig_node->orig, - ETH_ALEN); - } - } - return best_tq; -} - -/* Return true if the vis packet is full. */ -static bool vis_packet_full(struct vis_info *info) -{ - if (info->packet.entries + 1 > - (1000 - sizeof(struct vis_info)) / sizeof(struct vis_info_entry)) - return true; - return false; -} - -/* generates a packet of own vis data, - * returns 0 on success, -1 if no packet could be generated */ -static int generate_vis_packet(void) -{ - HASHIT(hashit_local); - HASHIT(hashit_global); - struct orig_node *orig_node; - struct vis_info *info = (struct vis_info *)my_vis_info; - struct vis_info_entry *entry, *entry_array; - struct hna_local_entry *hna_local_entry; - int best_tq = -1; - unsigned long flags; - - info->first_seen = jiffies; - info->packet.vis_type = atomic_read(&vis_mode); - - spin_lock_irqsave(&orig_hash_lock, flags); - memcpy(info->packet.target_orig, broadcastAddr, ETH_ALEN); - info->packet.ttl = TTL; - info->packet.seqno++; - info->packet.entries = 0; - - if (info->packet.vis_type == VIS_TYPE_CLIENT_UPDATE) { - best_tq = find_best_vis_server(info); - if (best_tq < 0) { - spin_unlock_irqrestore(&orig_hash_lock, flags); - return -1; - } - } - - entry_array = (struct vis_info_entry *) - ((char *)info + sizeof(struct vis_info)); - - while (hash_iterate(orig_hash, &hashit_global)) { - orig_node = hashit_global.bucket->data; - if (orig_node->router != NULL - && compare_orig(orig_node->router->addr, orig_node->orig) - && orig_node->batman_if - && (orig_node->batman_if->if_active == IF_ACTIVE) - && orig_node->router->tq_avg > 0) { - - /* fill one entry into buffer. */ - entry = &entry_array[info->packet.entries]; - memcpy(entry->src, orig_node->batman_if->net_dev->dev_addr, ETH_ALEN); - memcpy(entry->dest, orig_node->orig, ETH_ALEN); - entry->quality = orig_node->router->tq_avg; - info->packet.entries++; - - if (vis_packet_full(info)) { - spin_unlock_irqrestore(&orig_hash_lock, flags); - return 0; - } - } - } - - spin_unlock_irqrestore(&orig_hash_lock, flags); - - spin_lock_irqsave(&hna_local_hash_lock, flags); - while (hash_iterate(hna_local_hash, &hashit_local)) { - hna_local_entry = hashit_local.bucket->data; - entry = &entry_array[info->packet.entries]; - memset(entry->src, 0, ETH_ALEN); - memcpy(entry->dest, hna_local_entry->addr, ETH_ALEN); - entry->quality = 0; /* 0 means HNA */ - info->packet.entries++; - - if (vis_packet_full(info)) { - spin_unlock_irqrestore(&hna_local_hash_lock, flags); - return 0; - } - } - spin_unlock_irqrestore(&hna_local_hash_lock, flags); - return 0; -} - -static void purge_vis_packets(void) -{ - HASHIT(hashit); - struct vis_info *info; - - while (hash_iterate(vis_hash, &hashit)) { - info = hashit.bucket->data; - if (info == my_vis_info) /* never purge own data. */ - continue; - if (time_after(jiffies, - info->first_seen + (VIS_TIMEOUT*HZ)/1000)) { - hash_remove_bucket(vis_hash, &hashit); - free_info(info); - } - } -} - -static void broadcast_vis_packet(struct vis_info *info, int packet_length) -{ - HASHIT(hashit); - struct orig_node *orig_node; - unsigned long flags; - - spin_lock_irqsave(&orig_hash_lock, flags); - - /* send to all routers in range. */ - while (hash_iterate(orig_hash, &hashit)) { - orig_node = hashit.bucket->data; - - /* if it's a vis server and reachable, send it. */ - if (orig_node && - (orig_node->flags & VIS_SERVER) && - orig_node->batman_if && - orig_node->router) { - - /* don't send it if we already received the packet from - * this node. */ - if (recv_list_is_in(&info->recv_list, orig_node->orig)) - continue; - - memcpy(info->packet.target_orig, - orig_node->orig, ETH_ALEN); - - send_raw_packet((unsigned char *) &info->packet, - packet_length, - orig_node->batman_if, - orig_node->router->addr); - } - } - memcpy(info->packet.target_orig, broadcastAddr, ETH_ALEN); - spin_unlock_irqrestore(&orig_hash_lock, flags); -} - -static void unicast_vis_packet(struct vis_info *info, int packet_length) -{ - struct orig_node *orig_node; - unsigned long flags; - - spin_lock_irqsave(&orig_hash_lock, flags); - orig_node = ((struct orig_node *) - hash_find(orig_hash, info->packet.target_orig)); - - if ((orig_node != NULL) && - (orig_node->batman_if != NULL) && - (orig_node->router != NULL)) { - send_raw_packet((unsigned char *) &info->packet, packet_length, - orig_node->batman_if, - orig_node->router->addr); - } - spin_unlock_irqrestore(&orig_hash_lock, flags); -} - -/* only send one vis packet. called from send_vis_packets() */ -static void send_vis_packet(struct vis_info *info) -{ - int packet_length; - - if (info->packet.ttl < 2) { - printk(KERN_WARNING "batman-adv: Error - can't send vis packet: ttl exceeded\n"); - return; - } - - memcpy(info->packet.sender_orig, mainIfAddr, ETH_ALEN); - info->packet.ttl--; - - packet_length = sizeof(struct vis_packet) + - info->packet.entries * sizeof(struct vis_info_entry); - - if (is_bcast(info->packet.target_orig)) - broadcast_vis_packet(info, packet_length); - else - unicast_vis_packet(info, packet_length); - info->packet.ttl++; /* restore TTL */ -} - -/* called from timer; send (and maybe generate) vis packet. */ -static void send_vis_packets(struct work_struct *work) -{ - struct vis_info *info, *temp; - unsigned long flags; - - spin_lock_irqsave(&vis_hash_lock, flags); - purge_vis_packets(); - - if (generate_vis_packet() == 0) - /* schedule if generation was successful */ - list_add_tail(&my_vis_info->send_list, &send_list); - - list_for_each_entry_safe(info, temp, &send_list, send_list) { - list_del_init(&info->send_list); - send_vis_packet(info); - } - spin_unlock_irqrestore(&vis_hash_lock, flags); - start_vis_timer(); -} -static DECLARE_DELAYED_WORK(vis_timer_wq, send_vis_packets); - -/* init the vis server. this may only be called when if_list is already - * initialized (e.g. bat0 is initialized, interfaces have been added) */ -int vis_init(void) -{ - unsigned long flags; - if (vis_hash) - return 1; - - spin_lock_irqsave(&vis_hash_lock, flags); - - vis_hash = hash_new(256, vis_info_cmp, vis_info_choose); - if (!vis_hash) { - printk(KERN_ERR "batman-adv:Can't initialize vis_hash\n"); - goto err; - } - - my_vis_info = kmalloc(1000, GFP_ATOMIC); - if (!my_vis_info) { - printk(KERN_ERR "batman-adv:Can't initialize vis packet\n"); - goto err; - } - - /* prefill the vis info */ - my_vis_info->first_seen = jiffies - atomic_read(&vis_interval); - INIT_LIST_HEAD(&my_vis_info->recv_list); - INIT_LIST_HEAD(&my_vis_info->send_list); - my_vis_info->packet.version = COMPAT_VERSION; - my_vis_info->packet.packet_type = BAT_VIS; - my_vis_info->packet.ttl = TTL; - my_vis_info->packet.seqno = 0; - my_vis_info->packet.entries = 0; - - INIT_LIST_HEAD(&send_list); - - memcpy(my_vis_info->packet.vis_orig, mainIfAddr, ETH_ALEN); - memcpy(my_vis_info->packet.sender_orig, mainIfAddr, ETH_ALEN); - - if (hash_add(vis_hash, my_vis_info) < 0) { - printk(KERN_ERR - "batman-adv:Can't add own vis packet into hash\n"); - free_info(my_vis_info); /* not in hash, need to remove it - * manually. */ - goto err; - } - - spin_unlock_irqrestore(&vis_hash_lock, flags); - start_vis_timer(); - return 1; - -err: - spin_unlock_irqrestore(&vis_hash_lock, flags); - vis_quit(); - return 0; -} - -/* shutdown vis-server */ -void vis_quit(void) -{ - unsigned long flags; - if (!vis_hash) - return; - - cancel_delayed_work_sync(&vis_timer_wq); - - spin_lock_irqsave(&vis_hash_lock, flags); - /* properly remove, kill timers ... */ - hash_delete(vis_hash, free_info); - vis_hash = NULL; - my_vis_info = NULL; - spin_unlock_irqrestore(&vis_hash_lock, flags); -} - -/* schedule packets for (re)transmission */ -static void start_vis_timer(void) -{ - queue_delayed_work(bat_event_workqueue, &vis_timer_wq, - (atomic_read(&vis_interval) * HZ) / 1000); -} |
