diff options
Diffstat (limited to 'drivers/firewire')
-rw-r--r-- | drivers/firewire/core-card.c | 2 | ||||
-rw-r--r-- | drivers/firewire/net.c | 454 |
2 files changed, 207 insertions, 249 deletions
diff --git a/drivers/firewire/core-card.c b/drivers/firewire/core-card.c index 8c45e43da7c..667603ac14b 100644 --- a/drivers/firewire/core-card.c +++ b/drivers/firewire/core-card.c @@ -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); diff --git a/drivers/firewire/net.c b/drivers/firewire/net.c index ba6f924b1b1..d83c54587a6 100644 --- a/drivers/firewire/net.c +++ b/drivers/firewire/net.c @@ -18,8 +18,10 @@ #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[] = { |