firewire: net: allow for unordered unit discovery
authorStefan Richter <stefanr@s5r6.in-berlin.de>
Sun, 14 Jun 2009 09:45:27 +0000 (11:45 +0200)
committerStefan Richter <stefanr@s5r6.in-berlin.de>
Sun, 14 Jun 2009 12:26:29 +0000 (14:26 +0200)
Decouple the creation and destruction of the net_device from the order
of discovery and removal of nodes with RFC 2734 unit directories since
there is no reliable order.  The net_device is now created when the
first RFC 2734 unit on a card is discovered, and destroyed when the last
RFC 2734 unit on a card went away.  This includes all remote units as
well as the local unit, which is therefore tracked as a peer now too.

Also, locking around the list of peers is slightly extended to guard
against peer removal.  As a side effect, fwnet_peer.pdg_lock has become
superfluous and is deleted.

Peer data (max_rec, speed, node ID, generation) are updated more
carefully.

Signed-off-by: Stefan Richter <stefanr@s5r6.in-berlin.de>
drivers/firewire/core-card.c
drivers/firewire/net.c
include/linux/firewire.h

index 8c45e43..667603a 100644 (file)
@@ -429,8 +429,6 @@ void fw_card_initialize(struct fw_card *card,
        card->local_node = NULL;
 
        INIT_DELAYED_WORK(&card->work, fw_card_bm_work);
-       card->netdev = NULL;
-       INIT_LIST_HEAD(&card->peer_list);
 }
 EXPORT_SYMBOL(fw_card_initialize);
 
index ba6f924..d83c545 100644 (file)
 #include <linux/mod_devicetable.h>
 #include <linux/module.h>
 #include <linux/moduleparam.h>
+#include <linux/mutex.h>
 #include <linux/netdevice.h>
 #include <linux/skbuff.h>
+#include <linux/spinlock.h>
 
 #include <asm/unaligned.h>
 #include <net/arp.h>
@@ -135,40 +137,11 @@ struct fwnet_partial_datagram {
        u16 datagram_size;
 };
 
-/*
- * We keep one of these for each IPv4 capable device attached to a fw_card.
- * The list of them is stored in the fw_card structure rather than in the
- * fwnet_device because the remote IPv4 nodes may be probed before the card is,
- * so we need a place to store them before the fwnet_device structure is
- * allocated.
- */
-struct fwnet_peer {
-       struct list_head peer_link;
-       /* guid of the remote peer */
-       u64 guid;
-       /* FIFO address to transmit datagrams to, or FWNET_NO_FIFO_ADDR */
-       u64 fifo;
-
-       spinlock_t pdg_lock;    /* partial datagram lock                */
-       /* List of partial datagrams received from this peer */
-       struct list_head pd_list;
-       /* Number of entries in pd_list at the moment */
-       unsigned pdg_size;
-
-       /* max payload to transmit to this remote peer */
-       /* This already includes the RFC2374_FRAG_HDR_SIZE overhead */
-       u16 max_payload;
-       /* outgoing datagram label */
-       u16 datagram_label;
-       /* Current node_id of the remote peer */
-       u16 node_id;
-       /* current generation of the remote peer */
-       u8 generation;
-       /* max speed that this peer can receive at */
-       u8 xmt_speed;
-};
+static DEFINE_MUTEX(fwnet_device_mutex);
+static LIST_HEAD(fwnet_device_list);
 
 struct fwnet_device {
+       struct list_head dev_link;
        spinlock_t lock;
        enum {
                FWNET_BROADCAST_ERROR,
@@ -206,7 +179,26 @@ struct fwnet_device {
        /* List of packets that have been sent but not yet acked */
        struct list_head sent_list;
 
+       struct list_head peer_list;
        struct fw_card *card;
+       struct net_device *netdev;
+};
+
+struct fwnet_peer {
+       struct list_head peer_link;
+       struct fwnet_device *dev;
+       u64 guid;
+       u64 fifo;
+
+       /* guarded by dev->lock */
+       struct list_head pd_list; /* received partial datagrams */
+       unsigned pdg_size;        /* pd_list size */
+
+       u16 datagram_label;       /* outgoing datagram label */
+       unsigned max_payload;     /* includes RFC2374_FRAG_HDR_SIZE overhead */
+       int node_id;
+       int generation;
+       unsigned speed;
 };
 
 /* This is our task struct. It's used for the packet complete callback.  */
@@ -479,102 +471,47 @@ static bool fwnet_pd_is_complete(struct fwnet_partial_datagram *pd)
        return fi->len == pd->datagram_size;
 }
 
-static int fwnet_peer_new(struct fw_card *card, struct fw_device *device)
-{
-       struct fwnet_peer *peer;
-
-       peer = kmalloc(sizeof(*peer), GFP_KERNEL);
-       if (!peer) {
-               fw_error("out of memory\n");
-
-               return -ENOMEM;
-       }
-       peer->guid = (u64)device->config_rom[3] << 32 | device->config_rom[4];
-       peer->fifo = FWNET_NO_FIFO_ADDR;
-       INIT_LIST_HEAD(&peer->pd_list);
-       spin_lock_init(&peer->pdg_lock);
-       peer->pdg_size = 0;
-       peer->generation = device->generation;
-       rmb();
-       peer->node_id = device->node_id;
-        /* FIXME what should it really be? */
-       peer->max_payload = IEEE1394_MAX_PAYLOAD_S100 - RFC2374_UNFRAG_HDR_SIZE;
-       peer->datagram_label = 0U;
-       peer->xmt_speed = device->max_speed;
-       list_add_tail(&peer->peer_link, &card->peer_list);
-
-       return 0;
-}
-
-/* FIXME caller must take the lock, or peer needs to be reference-counted */
+/* caller must hold dev->lock */
 static struct fwnet_peer *fwnet_peer_find_by_guid(struct fwnet_device *dev,
                                                  u64 guid)
 {
-       struct fwnet_peer *p, *peer = NULL;
-       unsigned long flags;
+       struct fwnet_peer *peer;
 
-       spin_lock_irqsave(&dev->lock, flags);
-       list_for_each_entry(p, &dev->card->peer_list, peer_link)
-               if (p->guid == guid) {
-                       peer = p;
-                       break;
-               }
-       spin_unlock_irqrestore(&dev->lock, flags);
+       list_for_each_entry(peer, &dev->peer_list, peer_link)
+               if (peer->guid == guid)
+                       return peer;
 
-       return peer;
+       return NULL;
 }
 
-/* FIXME caller must take the lock, or peer needs to be reference-counted */
-/* FIXME node_id doesn't mean anything without generation */
+/* caller must hold dev->lock */
 static struct fwnet_peer *fwnet_peer_find_by_node_id(struct fwnet_device *dev,
-                                                    u16 node_id)
+                                               int node_id, int generation)
 {
-       struct fwnet_peer *p, *peer = NULL;
-       unsigned long flags;
+       struct fwnet_peer *peer;
 
-       spin_lock_irqsave(&dev->lock, flags);
-       list_for_each_entry(p, &dev->card->peer_list, peer_link)
-               if (p->node_id == node_id) {
-                       peer = p;
-                       break;
-               }
-       spin_unlock_irqrestore(&dev->lock, flags);
+       list_for_each_entry(peer, &dev->peer_list, peer_link)
+               if (peer->node_id    == node_id &&
+                   peer->generation == generation)
+                       return peer;
 
-       return peer;
+       return NULL;
 }
 
-/* FIXME */
-static void fwnet_peer_delete(struct fw_card *card, struct fw_device *device)
+/* See IEEE 1394-2008 table 6-4, table 8-8, table 16-18. */
+static unsigned fwnet_max_payload(unsigned max_rec, unsigned speed)
 {
-       struct net_device *net;
-       struct fwnet_device *dev;
-       struct fwnet_peer *peer;
-       u64 guid;
-       unsigned long flags;
-       struct fwnet_partial_datagram *pd, *pd_next;
-
-       guid = (u64)device->config_rom[3] << 32 | device->config_rom[4];
-       net = card->netdev;
-       if (net)
-               dev = netdev_priv(net);
-       else
-               dev = NULL;
-       if (dev)
-               spin_lock_irqsave(&dev->lock, flags);
-
-       list_for_each_entry(peer, &card->peer_list, peer_link) {
-               if (peer->guid == guid) {
-                       list_del(&peer->peer_link);
-                       list_for_each_entry_safe(pd, pd_next, &peer->pd_list,
-                                                pd_link)
-                               fwnet_pd_delete(pd);
-                       break;
-               }
+       max_rec = min(max_rec, speed + 8);
+       max_rec = min(max_rec, 0xbU); /* <= 4096 */
+       if (max_rec < 8) {
+               fw_notify("max_rec %x out of range\n", max_rec);
+               max_rec = 8;
        }
-       if (dev)
-               spin_unlock_irqrestore(&dev->lock, flags);
+
+       return (1 << (max_rec + 1)) - RFC2374_FRAG_HDR_SIZE;
 }
 
+
 static int fwnet_finish_incoming_packet(struct net_device *net,
                                        struct sk_buff *skb, u16 source_node_id,
                                        bool is_broadcast, u16 ether_type)
@@ -606,71 +543,44 @@ static int fwnet_finish_incoming_packet(struct net_device *net,
                unsigned char *arp_ptr;
                u64 fifo_addr;
                u64 peer_guid;
-               u8 max_rec;
-               u8 sspd;
+               unsigned sspd;
                u16 max_payload;
                struct fwnet_peer *peer;
-               static const u16 fwnet_speed_to_max_payload[] = {
-                       /* S100, S200, S400, S800, S1600, S3200 */
-                           512, 1024, 2048, 4096,  4096,  4096
-               };
+               unsigned long flags;
+
+               arp1394   = (struct rfc2734_arp *)skb->data;
+               arp       = (struct arphdr *)skb->data;
+               arp_ptr   = (unsigned char *)(arp + 1);
+               peer_guid = get_unaligned_be64(&arp1394->s_uniq_id);
+               fifo_addr = (u64)get_unaligned_be16(&arp1394->fifo_hi) << 32
+                               | get_unaligned_be32(&arp1394->fifo_lo);
 
-               arp1394 = (struct rfc2734_arp *)skb->data;
-               arp = (struct arphdr *)skb->data;
-               arp_ptr = (unsigned char *)(arp + 1);
-               fifo_addr = (u64)ntohs(arp1394->fifo_hi) << 32
-                               | ntohl(arp1394->fifo_lo);
-               max_rec = dev->card->max_receive;
-               if (arp1394->max_rec < max_rec)
-                       max_rec = arp1394->max_rec;
                sspd = arp1394->sspd;
                /* Sanity check.  OS X 10.3 PPC reportedly sends 131. */
                if (sspd > SCODE_3200) {
                        fw_notify("sspd %x out of range\n", sspd);
-                       sspd = 0;
+                       sspd = SCODE_3200;
                }
+               max_payload = fwnet_max_payload(arp1394->max_rec, sspd);
 
-               max_payload = min(fwnet_speed_to_max_payload[sspd],
-                       (u16)(1 << (max_rec + 1))) - RFC2374_UNFRAG_HDR_SIZE;
-
-               peer_guid = get_unaligned_be64(&arp1394->s_uniq_id);
+               spin_lock_irqsave(&dev->lock, flags);
                peer = fwnet_peer_find_by_guid(dev, peer_guid);
+               if (peer) {
+                       peer->fifo = fifo_addr;
+
+                       if (peer->speed > sspd)
+                               peer->speed = sspd;
+                       if (peer->max_payload > max_payload)
+                               peer->max_payload = max_payload;
+               }
+               spin_unlock_irqrestore(&dev->lock, flags);
+
                if (!peer) {
                        fw_notify("No peer for ARP packet from %016llx\n",
                                  (unsigned long long)peer_guid);
                        goto failed_proto;
                }
 
-               /* FIXME don't use card->generation */
-               if (peer->node_id != source_node_id ||
-                   peer->generation != dev->card->generation) {
-                       fw_notify("Internal error: peer->node_id (%x) != "
-                                 "source_node_id (%x) or peer->generation (%x)"
-                                 " != dev->card->generation(%x)\n",
-                                 peer->node_id, source_node_id,
-                                 peer->generation, dev->card->generation);
-                       peer->node_id = source_node_id;
-                       peer->generation = dev->card->generation;
-               }
-
-               /* FIXME: for debugging */
-               if (sspd > SCODE_400)
-                       sspd = SCODE_400;
-               /* Update our speed/payload/fifo_offset table */
-               /*
-                * FIXME: this does not handle cases where two high-speed endpoints must use a slower speed because of
-                * a lower speed hub between them.  We need to look at the actual topology map here.
-                */
-               peer->fifo = fifo_addr;
-               peer->max_payload = max_payload;
-               /*
-                * Only allow speeds to go down from their initial value.
-                * Otherwise a local peer that can only do S400 or slower may
-                * be told to transmit at S800 to a faster remote peer.
-                */
-               if (peer->xmt_speed > sspd)
-                       peer->xmt_speed = sspd;
-
                /*
                 * Now that we're done with the 1394 specific stuff, we'll
                 * need to alter some of the data.  Believe it or not, all
@@ -764,10 +674,11 @@ static int fwnet_finish_incoming_packet(struct net_device *net,
 }
 
 static int fwnet_incoming_packet(struct fwnet_device *dev, __be32 *buf, int len,
-                                u16 source_node_id, bool is_broadcast)
+                                int source_node_id, int generation,
+                                bool is_broadcast)
 {
        struct sk_buff *skb;
-       struct net_device *net;
+       struct net_device *net = dev->netdev;
        struct rfc2734_header hdr;
        unsigned lf;
        unsigned long flags;
@@ -779,8 +690,6 @@ static int fwnet_incoming_packet(struct fwnet_device *dev, __be32 *buf, int len,
        int retval;
        u16 ether_type;
 
-       net = dev->card->netdev;
-
        hdr.w0 = be32_to_cpu(buf[0]);
        lf = fwnet_get_hdr_lf(&hdr);
        if (lf == RFC2374_HDR_UNFRAG) {
@@ -819,9 +728,12 @@ static int fwnet_incoming_packet(struct fwnet_device *dev, __be32 *buf, int len,
        }
        datagram_label = fwnet_get_hdr_dgl(&hdr);
        dg_size = fwnet_get_hdr_dg_size(&hdr); /* ??? + 1 */
-       peer = fwnet_peer_find_by_node_id(dev, source_node_id);
 
-       spin_lock_irqsave(&peer->pdg_lock, flags);
+       spin_lock_irqsave(&dev->lock, flags);
+
+       peer = fwnet_peer_find_by_node_id(dev, source_node_id, generation);
+       if (!peer)
+               goto bad_proto;
 
        pd = fwnet_pd_find(peer, datagram_label);
        if (pd == NULL) {
@@ -876,7 +788,7 @@ static int fwnet_incoming_packet(struct fwnet_device *dev, __be32 *buf, int len,
                skb = skb_get(pd->skb);
                fwnet_pd_delete(pd);
 
-               spin_unlock_irqrestore(&peer->pdg_lock, flags);
+               spin_unlock_irqrestore(&dev->lock, flags);
 
                return fwnet_finish_incoming_packet(net, skb, source_node_id,
                                                    false, ether_type);
@@ -885,12 +797,12 @@ static int fwnet_incoming_packet(struct fwnet_device *dev, __be32 *buf, int len,
         * Datagram is not complete, we're done for the
         * moment.
         */
-       spin_unlock_irqrestore(&peer->pdg_lock, flags);
+       spin_unlock_irqrestore(&dev->lock, flags);
 
        return 0;
 
  bad_proto:
-       spin_unlock_irqrestore(&peer->pdg_lock, flags);
+       spin_unlock_irqrestore(&dev->lock, flags);
 
        if (netif_queue_stopped(net))
                netif_wake_queue(net);
@@ -916,7 +828,8 @@ static void fwnet_receive_packet(struct fw_card *card, struct fw_request *r,
                return;
        }
 
-       status = fwnet_incoming_packet(dev, payload, length, source, false);
+       status = fwnet_incoming_packet(dev, payload, length,
+                                      source, generation, false);
        if (status != 0) {
                fw_error("Incoming packet failure\n");
                fw_send_response(card, r, RCODE_CONFLICT_ERROR);
@@ -966,7 +879,7 @@ static void fwnet_receive_broadcast(struct fw_iso_context *context,
                buf_ptr += 2;
                length -= IEEE1394_GASP_HDR_SIZE;
                fwnet_incoming_packet(dev, buf_ptr, length,
-                                     source_node_id, true);
+                                     source_node_id, -1, true);
        }
 
        packet.payload_length = dev->rcv_buffer_size;
@@ -1073,7 +986,6 @@ static int fwnet_send_packet(struct fwnet_packet_task *ptask)
        unsigned tx_len;
        struct rfc2734_header *bufhdr;
        unsigned long flags;
-       struct net_device *net;
 
        dev = ptask->dev;
        tx_len = ptask->max_payload;
@@ -1137,8 +1049,7 @@ static int fwnet_send_packet(struct fwnet_packet_task *ptask)
        list_add_tail(&ptask->pt_link, &dev->sent_list);
        spin_unlock_irqrestore(&dev->lock, flags);
 
-       net = dev->card->netdev;
-       net->trans_start = jiffies;
+       dev->netdev->trans_start = jiffies;
 
        return 0;
 }
@@ -1294,7 +1205,8 @@ static int fwnet_tx(struct sk_buff *skb, struct net_device *net)
        u16 dg_size;
        u16 *datagram_label_ptr;
        struct fwnet_packet_task *ptask;
-       struct fwnet_peer *peer = NULL;
+       struct fwnet_peer *peer;
+       unsigned long flags;
 
        ptask = kmem_cache_alloc(fwnet_packet_task_cache, GFP_ATOMIC);
        if (ptask == NULL)
@@ -1314,6 +1226,9 @@ static int fwnet_tx(struct sk_buff *skb, struct net_device *net)
        proto = hdr_buf.h_proto;
        dg_size = skb->len;
 
+       /* serialize access to peer, including peer->datagram_label */
+       spin_lock_irqsave(&dev->lock, flags);
+
        /*
         * Set the transmission type for the packet.  ARP packets and IP
         * broadcast packets are sent via GASP.
@@ -1322,35 +1237,30 @@ static int fwnet_tx(struct sk_buff *skb, struct net_device *net)
            || proto == htons(ETH_P_ARP)
            || (proto == htons(ETH_P_IP)
                && IN_MULTICAST(ntohl(ip_hdr(skb)->daddr)))) {
-               max_payload = dev->broadcast_xmt_max_payload;
+               max_payload        = dev->broadcast_xmt_max_payload;
                datagram_label_ptr = &dev->broadcast_xmt_datagramlabel;
 
-               ptask->fifo_addr = FWNET_NO_FIFO_ADDR;
-               ptask->generation = 0;
-               ptask->dest_node = IEEE1394_ALL_NODES;
-               ptask->speed = SCODE_100;
+               ptask->fifo_addr   = FWNET_NO_FIFO_ADDR;
+               ptask->generation  = 0;
+               ptask->dest_node   = IEEE1394_ALL_NODES;
+               ptask->speed       = SCODE_100;
        } else {
                __be64 guid = get_unaligned((__be64 *)hdr_buf.h_dest);
                u8 generation;
 
                peer = fwnet_peer_find_by_guid(dev, be64_to_cpu(guid));
-               if (!peer)
-                       goto fail;
-
-               if (peer->fifo == FWNET_NO_FIFO_ADDR)
-                       goto fail;
+               if (!peer || peer->fifo == FWNET_NO_FIFO_ADDR)
+                       goto fail_unlock;
 
-               generation = peer->generation;
-               smp_rmb();
-               dest_node = peer->node_id;
-
-               max_payload = peer->max_payload;
+               generation         = peer->generation;
+               dest_node          = peer->node_id;
+               max_payload        = peer->max_payload;
                datagram_label_ptr = &peer->datagram_label;
 
-               ptask->fifo_addr = peer->fifo;
-               ptask->generation = generation;
-               ptask->dest_node = dest_node;
-               ptask->speed = peer->xmt_speed;
+               ptask->fifo_addr   = peer->fifo;
+               ptask->generation  = generation;
+               ptask->dest_node   = dest_node;
+               ptask->speed       = peer->speed;
        }
 
        /* If this is an ARP packet, convert it */
@@ -1393,11 +1303,16 @@ static int fwnet_tx(struct sk_buff *skb, struct net_device *net)
                ptask->outstanding_pkts = DIV_ROUND_UP(dg_size, max_payload);
                max_payload += RFC2374_FRAG_HDR_SIZE;
        }
+
+       spin_unlock_irqrestore(&dev->lock, flags);
+
        ptask->max_payload = max_payload;
        fwnet_send_packet(ptask);
 
        return NETDEV_TX_OK;
 
+ fail_unlock:
+       spin_unlock_irqrestore(&dev->lock, flags);
  fail:
        if (ptask)
                kmem_cache_free(fwnet_packet_task_cache, ptask);
@@ -1467,7 +1382,48 @@ static void fwnet_init_dev(struct net_device *net)
        SET_ETHTOOL_OPS(net, &fwnet_ethtool_ops);
 }
 
-/* FIXME create netdev upon first fw_unit of a card, not upon local fw_unit */
+/* caller must hold fwnet_device_mutex */
+static struct fwnet_device *fwnet_dev_find(struct fw_card *card)
+{
+       struct fwnet_device *dev;
+
+       list_for_each_entry(dev, &fwnet_device_list, dev_link)
+               if (dev->card == card)
+                       return dev;
+
+       return NULL;
+}
+
+static int fwnet_add_peer(struct fwnet_device *dev,
+                         struct fw_unit *unit, struct fw_device *device)
+{
+       struct fwnet_peer *peer;
+
+       peer = kmalloc(sizeof(*peer), GFP_KERNEL);
+       if (!peer)
+               return -ENOMEM;
+
+       unit->device.driver_data = peer;
+       peer->dev = dev;
+       peer->guid = (u64)device->config_rom[3] << 32 | device->config_rom[4];
+       peer->fifo = FWNET_NO_FIFO_ADDR;
+       INIT_LIST_HEAD(&peer->pd_list);
+       peer->pdg_size = 0;
+       peer->datagram_label = 0;
+       peer->speed = device->max_speed;
+       peer->max_payload = fwnet_max_payload(device->max_rec, peer->speed);
+
+       peer->generation = device->generation;
+       smp_rmb();
+       peer->node_id = device->node_id;
+
+       spin_lock_irq(&dev->lock);
+       list_add_tail(&peer->peer_link, &dev->peer_list);
+       spin_unlock_irq(&dev->lock);
+
+       return 0;
+}
+
 static int fwnet_probe(struct device *_dev)
 {
        struct fw_unit *unit = fw_unit(_dev);
@@ -1476,16 +1432,22 @@ static int fwnet_probe(struct device *_dev)
        struct net_device *net;
        struct fwnet_device *dev;
        unsigned max_mtu;
+       bool new_netdev;
+       int ret;
 
-       if (!device->is_local) {
-               int added;
+       mutex_lock(&fwnet_device_mutex);
 
-               added = fwnet_peer_new(card, device);
-               return added;
+       dev = fwnet_dev_find(card);
+       if (dev) {
+               new_netdev = false;
+               net = dev->netdev;
+               goto have_dev;
        }
+
+       new_netdev = true;
        net = alloc_netdev(sizeof(*dev), "firewire%d", fwnet_init_dev);
        if (net == NULL) {
-               fw_error("out of memory\n");
+               ret = -ENOMEM;
                goto out;
        }
 
@@ -1500,12 +1462,13 @@ static int fwnet_probe(struct device *_dev)
 
        dev->local_fifo = FWNET_NO_FIFO_ADDR;
 
-       /* INIT_WORK(&dev->wake, fwnet_handle_queue);*/
        INIT_LIST_HEAD(&dev->packet_list);
        INIT_LIST_HEAD(&dev->broadcasted_list);
        INIT_LIST_HEAD(&dev->sent_list);
+       INIT_LIST_HEAD(&dev->peer_list);
 
        dev->card = card;
+       dev->netdev = net;
 
        /*
         * Use the RFC 2734 default 1500 octets or the maximum payload
@@ -1518,43 +1481,57 @@ static int fwnet_probe(struct device *_dev)
        /* Set our hardware address while we're at it */
        put_unaligned_be64(card->guid, net->dev_addr);
        put_unaligned_be64(~0ULL, net->broadcast);
-       if (register_netdev(net)) {
+       ret = register_netdev(net);
+       if (ret) {
                fw_error("Cannot register the driver\n");
                goto out;
        }
 
+       list_add_tail(&dev->dev_link, &fwnet_device_list);
        fw_notify("%s: IPv4 over FireWire on device %016llx\n",
                  net->name, (unsigned long long)card->guid);
-       card->netdev = net;
-
-       return 0;
+ have_dev:
+       ret = fwnet_add_peer(dev, unit, device);
+       if (ret && new_netdev) {
+               unregister_netdev(net);
+               list_del(&dev->dev_link);
+       }
  out:
-       if (net)
+       if (ret && new_netdev)
                free_netdev(net);
 
-       return -ENOENT;
+       mutex_unlock(&fwnet_device_mutex);
+
+       return ret;
+}
+
+static void fwnet_remove_peer(struct fwnet_peer *peer)
+{
+       struct fwnet_partial_datagram *pd, *pd_next;
+
+       spin_lock_irq(&peer->dev->lock);
+       list_del(&peer->peer_link);
+       spin_unlock_irq(&peer->dev->lock);
+
+       list_for_each_entry_safe(pd, pd_next, &peer->pd_list, pd_link)
+               fwnet_pd_delete(pd);
+
+       kfree(peer);
 }
 
 static int fwnet_remove(struct device *_dev)
 {
-       struct fw_unit *unit = fw_unit(_dev);
-       struct fw_device *device = fw_parent_device(unit);
-       struct fw_card *card = device->card;
+       struct fwnet_peer *peer = _dev->driver_data;
+       struct fwnet_device *dev = peer->dev;
        struct net_device *net;
-       struct fwnet_device *dev;
-       struct fwnet_peer *peer;
-       struct fwnet_partial_datagram *pd, *pd_next;
        struct fwnet_packet_task *ptask, *pt_next;
 
-       if (!device->is_local) {
-               fwnet_peer_delete(card, device);
+       mutex_lock(&fwnet_device_mutex);
 
-               return 0;
-       }
+       fwnet_remove_peer(peer);
 
-       net = card->netdev;
-       if (net) {
-               dev = netdev_priv(net);
+       if (list_empty(&dev->peer_list)) {
+               net = dev->netdev;
                unregister_netdev(net);
 
                if (dev->local_fifo != FWNET_NO_FIFO_ADDR)
@@ -1580,19 +1557,11 @@ static int fwnet_remove(struct device *_dev)
                        dev_kfree_skb_any(ptask->skb);
                        kmem_cache_free(fwnet_packet_task_cache, ptask);
                }
-               list_for_each_entry(peer, &card->peer_list, peer_link) {
-                       if (peer->pdg_size) {
-                               list_for_each_entry_safe(pd, pd_next,
-                                               &peer->pd_list, pd_link)
-                                       fwnet_pd_delete(pd);
-                               peer->pdg_size = 0;
-                       }
-                       peer->fifo = FWNET_NO_FIFO_ADDR;
-               }
                free_netdev(net);
-               card->netdev = NULL;
        }
 
+       mutex_unlock(&fwnet_device_mutex);
+
        return 0;
 }
 
@@ -1603,24 +1572,15 @@ static int fwnet_remove(struct device *_dev)
 static void fwnet_update(struct fw_unit *unit)
 {
        struct fw_device *device = fw_parent_device(unit);
-       struct net_device *net = device->card->netdev;
-       struct fwnet_device *dev;
-       struct fwnet_peer *peer;
-       u64 guid;
+       struct fwnet_peer *peer = unit->device.driver_data;
+       int generation;
 
-       if (net && !device->is_local) {
-               dev = netdev_priv(net);
-               guid = (u64)device->config_rom[3] << 32 | device->config_rom[4];
-               peer = fwnet_peer_find_by_guid(dev, guid);
-               if (!peer) {
-                       fw_error("fwnet_update: no peer for device %016llx\n",
-                                (unsigned long long)guid);
-                       return;
-               }
-               peer->generation = device->generation;
-               rmb();
-               peer->node_id = device->node_id;
-       }
+       generation = device->generation;
+
+       spin_lock_irq(&peer->dev->lock);
+       peer->node_id    = device->node_id;
+       peer->generation = generation;
+       spin_unlock_irq(&peer->dev->lock);
 }
 
 static const struct ieee1394_device_id fwnet_id_table[] = {
index 5cb0c15..9823946 100644 (file)
@@ -131,10 +131,6 @@ struct fw_card {
        bool broadcast_channel_allocated;
        u32 broadcast_channel;
        u32 topology_map[(CSR_TOPOLOGY_MAP_END - CSR_TOPOLOGY_MAP) / 4];
-
-       /* firewire-net driver data */
-       void *netdev;
-       struct list_head peer_list;
 };
 
 static inline struct fw_card *fw_card_get(struct fw_card *card)