]> Pileus Git - ~andy/linux/blobdiff - net/tipc/bearer.c
Merge tag 'fixes-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/arm...
[~andy/linux] / net / tipc / bearer.c
index 3f9707a16d0650d6367ad716ab2a110318a03e96..574b86193b15a8251dc85555cc03a8e5f9af1768 100644 (file)
@@ -1,8 +1,8 @@
 /*
  * net/tipc/bearer.c: TIPC bearer code
  *
- * Copyright (c) 1996-2006, Ericsson AB
- * Copyright (c) 2004-2006, 2010-2011, Wind River Systems
+ * Copyright (c) 1996-2006, 2013, Ericsson AB
+ * Copyright (c) 2004-2006, 2010-2013, Wind River Systems
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
 
 #define MAX_ADDR_STR 60
 
-static struct tipc_media *media_list[MAX_MEDIA];
-static u32 media_count;
+static struct tipc_media * const media_info_array[] = {
+       &eth_media_info,
+#ifdef CONFIG_TIPC_MEDIA_IB
+       &ib_media_info,
+#endif
+       NULL
+};
 
 struct tipc_bearer tipc_bearers[MAX_BEARERS];
 
@@ -55,11 +60,11 @@ struct tipc_media *tipc_media_find(const char *name)
 {
        u32 i;
 
-       for (i = 0; i < media_count; i++) {
-               if (!strcmp(media_list[i]->name, name))
-                       return media_list[i];
+       for (i = 0; media_info_array[i] != NULL; i++) {
+               if (!strcmp(media_info_array[i]->name, name))
+                       break;
        }
-       return NULL;
+       return media_info_array[i];
 }
 
 /**
@@ -69,44 +74,11 @@ static struct tipc_media *media_find_id(u8 type)
 {
        u32 i;
 
-       for (i = 0; i < media_count; i++) {
-               if (media_list[i]->type_id == type)
-                       return media_list[i];
+       for (i = 0; media_info_array[i] != NULL; i++) {
+               if (media_info_array[i]->type_id == type)
+                       break;
        }
-       return NULL;
-}
-
-/**
- * tipc_register_media - register a media type
- *
- * Bearers for this media type must be activated separately at a later stage.
- */
-int tipc_register_media(struct tipc_media *m_ptr)
-{
-       int res = -EINVAL;
-
-       write_lock_bh(&tipc_net_lock);
-
-       if ((strlen(m_ptr->name) + 1) > TIPC_MAX_MEDIA_NAME)
-               goto exit;
-       if (m_ptr->priority > TIPC_MAX_LINK_PRI)
-               goto exit;
-       if ((m_ptr->tolerance < TIPC_MIN_LINK_TOL) ||
-           (m_ptr->tolerance > TIPC_MAX_LINK_TOL))
-               goto exit;
-       if (media_count >= MAX_MEDIA)
-               goto exit;
-       if (tipc_media_find(m_ptr->name) || media_find_id(m_ptr->type_id))
-               goto exit;
-
-       media_list[media_count] = m_ptr;
-       media_count++;
-       res = 0;
-exit:
-       write_unlock_bh(&tipc_net_lock);
-       if (res)
-               pr_warn("Media <%s> registration error\n", m_ptr->name);
-       return res;
+       return media_info_array[i];
 }
 
 /**
@@ -144,13 +116,11 @@ struct sk_buff *tipc_media_get_names(void)
        if (!buf)
                return NULL;
 
-       read_lock_bh(&tipc_net_lock);
-       for (i = 0; i < media_count; i++) {
+       for (i = 0; media_info_array[i] != NULL; i++) {
                tipc_cfg_append_tlv(buf, TIPC_TLV_MEDIA_NAME,
-                                   media_list[i]->name,
-                                   strlen(media_list[i]->name) + 1);
+                                   media_info_array[i]->name,
+                                   strlen(media_info_array[i]->name) + 1);
        }
-       read_unlock_bh(&tipc_net_lock);
        return buf;
 }
 
@@ -214,32 +184,13 @@ struct tipc_bearer *tipc_bearer_find(const char *name)
        return NULL;
 }
 
-/**
- * tipc_bearer_find_interface - locates bearer object with matching interface name
- */
-struct tipc_bearer *tipc_bearer_find_interface(const char *if_name)
-{
-       struct tipc_bearer *b_ptr;
-       char *b_if_name;
-       u32 i;
-
-       for (i = 0, b_ptr = tipc_bearers; i < MAX_BEARERS; i++, b_ptr++) {
-               if (!b_ptr->active)
-                       continue;
-               b_if_name = strchr(b_ptr->name, ':') + 1;
-               if (!strcmp(b_if_name, if_name))
-                       return b_ptr;
-       }
-       return NULL;
-}
-
 /**
  * tipc_bearer_get_names - record names of bearers in buffer
  */
 struct sk_buff *tipc_bearer_get_names(void)
 {
        struct sk_buff *buf;
-       struct tipc_bearer *b_ptr;
+       struct tipc_bearer *b;
        int i, j;
 
        buf = tipc_cfg_reply_alloc(MAX_BEARERS * TLV_SPACE(TIPC_MAX_BEARER_NAME));
@@ -247,13 +198,13 @@ struct sk_buff *tipc_bearer_get_names(void)
                return NULL;
 
        read_lock_bh(&tipc_net_lock);
-       for (i = 0; i < media_count; i++) {
+       for (i = 0; media_info_array[i] != NULL; i++) {
                for (j = 0; j < MAX_BEARERS; j++) {
-                       b_ptr = &tipc_bearers[j];
-                       if (b_ptr->active && (b_ptr->media == media_list[i])) {
+                       b = &tipc_bearers[j];
+                       if (b->active && (b->media == media_info_array[i])) {
                                tipc_cfg_append_tlv(buf, TIPC_TLV_BEARER_NAME,
-                                                   b_ptr->name,
-                                                   strlen(b_ptr->name) + 1);
+                                                   b->name,
+                                                   strlen(b->name) + 1);
                        }
                }
        }
@@ -275,31 +226,6 @@ void tipc_bearer_remove_dest(struct tipc_bearer *b_ptr, u32 dest)
        tipc_disc_remove_dest(b_ptr->link_req);
 }
 
-/*
- * Interrupt enabling new requests after bearer blocking:
- * See bearer_send().
- */
-void tipc_continue(struct tipc_bearer *b)
-{
-       spin_lock_bh(&b->lock);
-       b->blocked = 0;
-       spin_unlock_bh(&b->lock);
-}
-
-/*
- * tipc_bearer_blocked - determines if bearer is currently blocked
- */
-int tipc_bearer_blocked(struct tipc_bearer *b)
-{
-       int res;
-
-       spin_lock_bh(&b->lock);
-       res = b->blocked;
-       spin_unlock_bh(&b->lock);
-
-       return res;
-}
-
 /**
  * tipc_enable_bearer - enable bearer with the given name
  */
@@ -387,6 +313,7 @@ restart:
 
        b_ptr = &tipc_bearers[bearer_id];
        strcpy(b_ptr->name, name);
+       b_ptr->media = m_ptr;
        res = m_ptr->enable_media(b_ptr);
        if (res) {
                pr_warn("Bearer <%s> rejected, enable failure (%d)\n",
@@ -395,7 +322,6 @@ restart:
        }
 
        b_ptr->identity = bearer_id;
-       b_ptr->media = m_ptr;
        b_ptr->tolerance = m_ptr->tolerance;
        b_ptr->window = m_ptr->window;
        b_ptr->net_plane = bearer_id + 'A';
@@ -420,17 +346,16 @@ exit:
 }
 
 /**
- * tipc_block_bearer - Block the bearer, and reset all its links
+ * tipc_reset_bearer - Reset all links established over this bearer
  */
-int tipc_block_bearer(struct tipc_bearer *b_ptr)
+static int tipc_reset_bearer(struct tipc_bearer *b_ptr)
 {
        struct tipc_link *l_ptr;
        struct tipc_link *temp_l_ptr;
 
        read_lock_bh(&tipc_net_lock);
-       pr_info("Blocking bearer <%s>\n", b_ptr->name);
+       pr_info("Resetting bearer <%s>\n", b_ptr->name);
        spin_lock_bh(&b_ptr->lock);
-       b_ptr->blocked = 1;
        list_for_each_entry_safe(l_ptr, temp_l_ptr, &b_ptr->links, link_list) {
                struct tipc_node *n_ptr = l_ptr->owner;
 
@@ -456,7 +381,6 @@ static void bearer_disable(struct tipc_bearer *b_ptr)
 
        pr_info("Disabling bearer <%s>\n", b_ptr->name);
        spin_lock_bh(&b_ptr->lock);
-       b_ptr->blocked = 1;
        b_ptr->media->disable_media(b_ptr);
        list_for_each_entry_safe(l_ptr, temp_l_ptr, &b_ptr->links, link_list) {
                tipc_link_delete(l_ptr);
@@ -490,6 +414,216 @@ int tipc_disable_bearer(const char *name)
 }
 
 
+/* tipc_l2_media_addr_set - initialize Ethernet media address structure
+ *
+ * Media-dependent "value" field stores MAC address in first 6 bytes
+ * and zeroes out the remaining bytes.
+ */
+void tipc_l2_media_addr_set(const struct tipc_bearer *b,
+                           struct tipc_media_addr *a, char *mac)
+{
+       int len = b->media->hwaddr_len;
+
+       if (unlikely(sizeof(a->value) < len)) {
+               WARN_ONCE(1, "Media length invalid\n");
+               return;
+       }
+
+       memcpy(a->value, mac, len);
+       memset(a->value + len, 0, sizeof(a->value) - len);
+       a->media_id = b->media->type_id;
+       a->broadcast = !memcmp(mac, b->bcast_addr.value, len);
+}
+
+int tipc_enable_l2_media(struct tipc_bearer *b)
+{
+       struct net_device *dev;
+       char *driver_name = strchr((const char *)b->name, ':') + 1;
+
+       /* Find device with specified name */
+       dev = dev_get_by_name(&init_net, driver_name);
+       if (!dev)
+               return -ENODEV;
+
+       /* Associate TIPC bearer with Ethernet bearer */
+       b->media_ptr = dev;
+       memset(b->bcast_addr.value, 0, sizeof(b->bcast_addr.value));
+       memcpy(b->bcast_addr.value, dev->broadcast, b->media->hwaddr_len);
+       b->bcast_addr.media_id = b->media->type_id;
+       b->bcast_addr.broadcast = 1;
+       b->mtu = dev->mtu;
+       tipc_l2_media_addr_set(b, &b->addr, (char *)dev->dev_addr);
+       rcu_assign_pointer(dev->tipc_ptr, b);
+       return 0;
+}
+
+/* tipc_disable_l2_media - detach TIPC bearer from an Ethernet interface
+ *
+ * Mark Ethernet bearer as inactive so that incoming buffers are thrown away,
+ * then get worker thread to complete bearer cleanup.  (Can't do cleanup
+ * here because cleanup code needs to sleep and caller holds spinlocks.)
+ */
+void tipc_disable_l2_media(struct tipc_bearer *b)
+{
+       struct net_device *dev = (struct net_device *)b->media_ptr;
+       RCU_INIT_POINTER(dev->tipc_ptr, NULL);
+       dev_put(dev);
+}
+
+/**
+ * tipc_l2_send_msg - send a TIPC packet out over an Ethernet interface
+ * @buf: the packet to be sent
+ * @b_ptr: the bearer through which the packet is to be sent
+ * @dest: peer destination address
+ */
+int tipc_l2_send_msg(struct sk_buff *buf, struct tipc_bearer *b,
+                    struct tipc_media_addr *dest)
+{
+       struct sk_buff *clone;
+       int delta;
+       struct net_device *dev = (struct net_device *)b->media_ptr;
+
+       clone = skb_clone(buf, GFP_ATOMIC);
+       if (!clone)
+               return 0;
+
+       delta = dev->hard_header_len - skb_headroom(buf);
+       if ((delta > 0) &&
+           pskb_expand_head(clone, SKB_DATA_ALIGN(delta), 0, GFP_ATOMIC)) {
+               kfree_skb(clone);
+               return 0;
+       }
+
+       skb_reset_network_header(clone);
+       clone->dev = dev;
+       clone->protocol = htons(ETH_P_TIPC);
+       dev_hard_header(clone, dev, ETH_P_TIPC, dest->value,
+                       dev->dev_addr, clone->len);
+       dev_queue_xmit(clone);
+       return 0;
+}
+
+/* tipc_bearer_send- sends buffer to destination over bearer
+ *
+ * IMPORTANT:
+ * The media send routine must not alter the buffer being passed in
+ * as it may be needed for later retransmission!
+ */
+void tipc_bearer_send(struct tipc_bearer *b, struct sk_buff *buf,
+                     struct tipc_media_addr *dest)
+{
+       b->media->send_msg(buf, b, dest);
+}
+
+/**
+ * tipc_l2_rcv_msg - handle incoming TIPC message from an interface
+ * @buf: the received packet
+ * @dev: the net device that the packet was received on
+ * @pt: the packet_type structure which was used to register this handler
+ * @orig_dev: the original receive net device in case the device is a bond
+ *
+ * Accept only packets explicitly sent to this node, or broadcast packets;
+ * ignores packets sent using interface multicast, and traffic sent to other
+ * nodes (which can happen if interface is running in promiscuous mode).
+ */
+static int tipc_l2_rcv_msg(struct sk_buff *buf, struct net_device *dev,
+                          struct packet_type *pt, struct net_device *orig_dev)
+{
+       struct tipc_bearer *b_ptr;
+
+       if (!net_eq(dev_net(dev), &init_net)) {
+               kfree_skb(buf);
+               return NET_RX_DROP;
+       }
+
+       rcu_read_lock();
+       b_ptr = rcu_dereference(dev->tipc_ptr);
+       if (likely(b_ptr)) {
+               if (likely(buf->pkt_type <= PACKET_BROADCAST)) {
+                       buf->next = NULL;
+                       tipc_rcv(buf, b_ptr);
+                       rcu_read_unlock();
+                       return NET_RX_SUCCESS;
+               }
+       }
+       rcu_read_unlock();
+
+       kfree_skb(buf);
+       return NET_RX_DROP;
+}
+
+/**
+ * tipc_l2_device_event - handle device events from network device
+ * @nb: the context of the notification
+ * @evt: the type of event
+ * @ptr: the net device that the event was on
+ *
+ * This function is called by the Ethernet driver in case of link
+ * change event.
+ */
+static int tipc_l2_device_event(struct notifier_block *nb, unsigned long evt,
+                               void *ptr)
+{
+       struct tipc_bearer *b_ptr;
+       struct net_device *dev = netdev_notifier_info_to_dev(ptr);
+
+       if (!net_eq(dev_net(dev), &init_net))
+               return NOTIFY_DONE;
+
+       rcu_read_lock();
+       b_ptr = rcu_dereference(dev->tipc_ptr);
+       if (!b_ptr) {
+               rcu_read_unlock();
+               return NOTIFY_DONE;
+       }
+
+       b_ptr->mtu = dev->mtu;
+
+       switch (evt) {
+       case NETDEV_CHANGE:
+               if (netif_carrier_ok(dev))
+                       break;
+       case NETDEV_DOWN:
+       case NETDEV_CHANGEMTU:
+       case NETDEV_CHANGEADDR:
+               tipc_reset_bearer(b_ptr);
+               break;
+       case NETDEV_UNREGISTER:
+       case NETDEV_CHANGENAME:
+               tipc_disable_bearer(b_ptr->name);
+               break;
+       }
+       rcu_read_unlock();
+
+       return NOTIFY_OK;
+}
+
+static struct packet_type tipc_packet_type __read_mostly = {
+       .type = __constant_htons(ETH_P_TIPC),
+       .func = tipc_l2_rcv_msg,
+};
+
+static struct notifier_block notifier = {
+       .notifier_call  = tipc_l2_device_event,
+       .priority       = 0,
+};
+
+int tipc_bearer_setup(void)
+{
+       int err;
+
+       err = register_netdevice_notifier(&notifier);
+       if (err)
+               return err;
+       dev_add_pack(&tipc_packet_type);
+       return 0;
+}
+
+void tipc_bearer_cleanup(void)
+{
+       unregister_netdevice_notifier(&notifier);
+       dev_remove_pack(&tipc_packet_type);
+}
 
 void tipc_bearer_stop(void)
 {
@@ -499,5 +633,4 @@ void tipc_bearer_stop(void)
                if (tipc_bearers[i].active)
                        bearer_disable(&tipc_bearers[i]);
        }
-       media_count = 0;
 }