2019-05-19 20:08:20 +08:00
|
|
|
// SPDX-License-Identifier: GPL-2.0-only
|
2009-05-19 01:08:06 +08:00
|
|
|
/*
|
|
|
|
* IPv4 over IEEE 1394, per RFC 2734
|
2013-03-25 16:26:24 +08:00
|
|
|
* IPv6 over IEEE 1394, per RFC 3146
|
2009-05-19 01:08:06 +08:00
|
|
|
*
|
|
|
|
* Copyright (C) 2009 Jay Fenlason <fenlason@redhat.com>
|
|
|
|
*
|
|
|
|
* based on eth1394 by Ben Collins et al
|
|
|
|
*/
|
|
|
|
|
2009-06-08 04:57:53 +08:00
|
|
|
#include <linux/bug.h>
|
2011-05-30 01:07:19 +08:00
|
|
|
#include <linux/compiler.h>
|
2010-11-07 06:18:23 +08:00
|
|
|
#include <linux/delay.h>
|
2009-05-19 01:08:06 +08:00
|
|
|
#include <linux/device.h>
|
2010-12-18 05:22:33 +08:00
|
|
|
#include <linux/ethtool.h>
|
2009-05-19 01:08:06 +08:00
|
|
|
#include <linux/firewire.h>
|
|
|
|
#include <linux/firewire-constants.h>
|
|
|
|
#include <linux/highmem.h>
|
|
|
|
#include <linux/in.h>
|
|
|
|
#include <linux/ip.h>
|
2009-06-08 04:57:53 +08:00
|
|
|
#include <linux/jiffies.h>
|
2009-05-19 01:08:06 +08:00
|
|
|
#include <linux/mod_devicetable.h>
|
|
|
|
#include <linux/module.h>
|
|
|
|
#include <linux/moduleparam.h>
|
2009-06-14 17:45:27 +08:00
|
|
|
#include <linux/mutex.h>
|
2009-05-19 01:08:06 +08:00
|
|
|
#include <linux/netdevice.h>
|
|
|
|
#include <linux/skbuff.h>
|
include cleanup: Update gfp.h and slab.h includes to prepare for breaking implicit slab.h inclusion from percpu.h
percpu.h is included by sched.h and module.h and thus ends up being
included when building most .c files. percpu.h includes slab.h which
in turn includes gfp.h making everything defined by the two files
universally available and complicating inclusion dependencies.
percpu.h -> slab.h dependency is about to be removed. Prepare for
this change by updating users of gfp and slab facilities include those
headers directly instead of assuming availability. As this conversion
needs to touch large number of source files, the following script is
used as the basis of conversion.
http://userweb.kernel.org/~tj/misc/slabh-sweep.py
The script does the followings.
* Scan files for gfp and slab usages and update includes such that
only the necessary includes are there. ie. if only gfp is used,
gfp.h, if slab is used, slab.h.
* When the script inserts a new include, it looks at the include
blocks and try to put the new include such that its order conforms
to its surrounding. It's put in the include block which contains
core kernel includes, in the same order that the rest are ordered -
alphabetical, Christmas tree, rev-Xmas-tree or at the end if there
doesn't seem to be any matching order.
* If the script can't find a place to put a new include (mostly
because the file doesn't have fitting include block), it prints out
an error message indicating which .h file needs to be added to the
file.
The conversion was done in the following steps.
1. The initial automatic conversion of all .c files updated slightly
over 4000 files, deleting around 700 includes and adding ~480 gfp.h
and ~3000 slab.h inclusions. The script emitted errors for ~400
files.
2. Each error was manually checked. Some didn't need the inclusion,
some needed manual addition while adding it to implementation .h or
embedding .c file was more appropriate for others. This step added
inclusions to around 150 files.
3. The script was run again and the output was compared to the edits
from #2 to make sure no file was left behind.
4. Several build tests were done and a couple of problems were fixed.
e.g. lib/decompress_*.c used malloc/free() wrappers around slab
APIs requiring slab.h to be added manually.
5. The script was run on all .h files but without automatically
editing them as sprinkling gfp.h and slab.h inclusions around .h
files could easily lead to inclusion dependency hell. Most gfp.h
inclusion directives were ignored as stuff from gfp.h was usually
wildly available and often used in preprocessor macros. Each
slab.h inclusion directive was examined and added manually as
necessary.
6. percpu.h was updated not to include slab.h.
7. Build test were done on the following configurations and failures
were fixed. CONFIG_GCOV_KERNEL was turned off for all tests (as my
distributed build env didn't work with gcov compiles) and a few
more options had to be turned off depending on archs to make things
build (like ipr on powerpc/64 which failed due to missing writeq).
* x86 and x86_64 UP and SMP allmodconfig and a custom test config.
* powerpc and powerpc64 SMP allmodconfig
* sparc and sparc64 SMP allmodconfig
* ia64 SMP allmodconfig
* s390 SMP allmodconfig
* alpha SMP allmodconfig
* um on x86_64 SMP allmodconfig
8. percpu.h modifications were reverted so that it could be applied as
a separate patch and serve as bisection point.
Given the fact that I had only a couple of failures from tests on step
6, I'm fairly confident about the coverage of this conversion patch.
If there is a breakage, it's likely to be something in one of the arch
headers which should be easily discoverable easily on most builds of
the specific arch.
Signed-off-by: Tejun Heo <tj@kernel.org>
Guess-its-ok-by: Christoph Lameter <cl@linux-foundation.org>
Cc: Ingo Molnar <mingo@redhat.com>
Cc: Lee Schermerhorn <Lee.Schermerhorn@hp.com>
2010-03-24 16:04:11 +08:00
|
|
|
#include <linux/slab.h>
|
2009-06-14 17:45:27 +08:00
|
|
|
#include <linux/spinlock.h>
|
2009-05-19 01:08:06 +08:00
|
|
|
|
|
|
|
#include <asm/unaligned.h>
|
|
|
|
#include <net/arp.h>
|
firewire net, ipv4 arp: Extend hardware address and remove driver-level packet inspection.
Inspection of upper layer protocol is considered harmful, especially
if it is about ARP or other stateful upper layer protocol; driver
cannot (and should not) have full state of them.
IPv4 over Firewire module used to inspect ARP (both in sending path
and in receiving path), and record peer's GUID, max packet size, max
speed and fifo address. This patch removes such inspection by extending
our "hardware address" definition to include other information as well:
max packet size, max speed and fifo. By doing this, The neighbour
module in networking subsystem can cache them.
Note: As we have started ignoring sspd and max_rec in ARP/NDP, those
information will not be used in the driver when sending.
When a packet is being sent, the IP layer fills our pseudo header with
the extended "hardware address", including GUID and fifo. The driver
can look-up node-id (the real but rather volatile low-level address)
by GUID, and then the module can send the packet to the wire using
parameters provided in the extendedn hardware address.
This approach is realistic because IP over IEEE1394 (RFC2734) and IPv6
over IEEE1394 (RFC3146) share same "hardware address" format
in their address resolution protocols.
Here, extended "hardware address" is defined as follows:
union fwnet_hwaddr {
u8 u[16];
struct {
__be64 uniq_id; /* EUI-64 */
u8 max_rec; /* max packet size */
u8 sspd; /* max speed */
__be16 fifo_hi; /* hi 16bits of FIFO addr */
__be32 fifo_lo; /* lo 32bits of FIFO addr */
} __packed uc;
};
Note that Hardware address is declared as union, so that we can map full
IP address into this, when implementing MCAP (Multicast Cannel Allocation
Protocol) for IPv6, but IP and ARP subsystem do not need to know this
format in detail.
One difference between original ARP (RFC826) and 1394 ARP (RFC2734)
is that 1394 ARP Request/Reply do not contain the target hardware address
field (aka ar$tha). This difference is handled in the ARP subsystem.
CC: Stephan Gatzka <stephan.gatzka@gmail.com>
Signed-off-by: YOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org>
Signed-off-by: David S. Miller <davem@davemloft.net>
2013-03-25 16:26:16 +08:00
|
|
|
#include <net/firewire.h>
|
2009-05-19 01:08:06 +08:00
|
|
|
|
2010-11-14 21:35:40 +08:00
|
|
|
/* rx limits */
|
|
|
|
#define FWNET_MAX_FRAGMENTS 30 /* arbitrary, > TX queue depth */
|
|
|
|
#define FWNET_ISO_PAGE_COUNT (PAGE_SIZE < 16*1024 ? 4 : 2)
|
|
|
|
|
|
|
|
/* tx limits */
|
|
|
|
#define FWNET_MAX_QUEUED_DATAGRAMS 20 /* < 64 = number of tlabels */
|
|
|
|
#define FWNET_MIN_QUEUED_DATAGRAMS 10 /* should keep AT DMA busy enough */
|
|
|
|
#define FWNET_TX_QUEUE_LEN FWNET_MAX_QUEUED_DATAGRAMS /* ? */
|
2009-05-19 01:08:06 +08:00
|
|
|
|
2009-06-08 04:57:53 +08:00
|
|
|
#define IEEE1394_BROADCAST_CHANNEL 31
|
|
|
|
#define IEEE1394_ALL_NODES (0xffc0 | 0x003f)
|
|
|
|
#define IEEE1394_MAX_PAYLOAD_S100 512
|
|
|
|
#define FWNET_NO_FIFO_ADDR (~0ULL)
|
2009-05-19 01:08:06 +08:00
|
|
|
|
2009-06-08 04:57:53 +08:00
|
|
|
#define IANA_SPECIFIER_ID 0x00005eU
|
|
|
|
#define RFC2734_SW_VERSION 0x000001U
|
2013-03-25 16:26:24 +08:00
|
|
|
#define RFC3146_SW_VERSION 0x000002U
|
2009-05-19 01:08:06 +08:00
|
|
|
|
2009-06-08 04:57:53 +08:00
|
|
|
#define IEEE1394_GASP_HDR_SIZE 8
|
2009-05-19 01:08:06 +08:00
|
|
|
|
2009-06-08 04:57:53 +08:00
|
|
|
#define RFC2374_UNFRAG_HDR_SIZE 4
|
|
|
|
#define RFC2374_FRAG_HDR_SIZE 8
|
|
|
|
#define RFC2374_FRAG_OVERHEAD 4
|
2009-05-19 01:08:06 +08:00
|
|
|
|
2009-06-08 04:57:53 +08:00
|
|
|
#define RFC2374_HDR_UNFRAG 0 /* unfragmented */
|
|
|
|
#define RFC2374_HDR_FIRSTFRAG 1 /* first fragment */
|
|
|
|
#define RFC2374_HDR_LASTFRAG 2 /* last fragment */
|
|
|
|
#define RFC2374_HDR_INTFRAG 3 /* interior fragment */
|
2009-05-19 01:08:06 +08:00
|
|
|
|
2013-03-25 16:25:48 +08:00
|
|
|
static bool fwnet_hwaddr_is_multicast(u8 *ha)
|
|
|
|
{
|
|
|
|
return !!(*ha & 1);
|
|
|
|
}
|
|
|
|
|
2009-06-08 04:57:53 +08:00
|
|
|
/* IPv4 and IPv6 encapsulation header */
|
|
|
|
struct rfc2734_header {
|
2009-05-19 01:08:06 +08:00
|
|
|
u32 w0;
|
|
|
|
u32 w1;
|
|
|
|
};
|
|
|
|
|
2009-06-08 04:57:53 +08:00
|
|
|
#define fwnet_get_hdr_lf(h) (((h)->w0 & 0xc0000000) >> 30)
|
|
|
|
#define fwnet_get_hdr_ether_type(h) (((h)->w0 & 0x0000ffff))
|
firewire: net: fix fragmented datagram_size off-by-one
RFC 2734 defines the datagram_size field in fragment encapsulation
headers thus:
datagram_size: The encoded size of the entire IP datagram. The
value of datagram_size [...] SHALL be one less than the value of
Total Length in the datagram's IP header (see STD 5, RFC 791).
Accordingly, the eth1394 driver of Linux 2.6.36 and older set and got
this field with a -/+1 offset:
ether1394_tx() /* transmit */
ether1394_encapsulate_prep()
hdr->ff.dg_size = dg_size - 1;
ether1394_data_handler() /* receive */
if (hdr->common.lf == ETH1394_HDR_LF_FF)
dg_size = hdr->ff.dg_size + 1;
else
dg_size = hdr->sf.dg_size + 1;
Likewise, I observe OS X 10.4 and Windows XP Pro SP3 to transmit 1500
byte sized datagrams in fragments with datagram_size=1499 if link
fragmentation is required.
Only firewire-net sets and gets datagram_size without this offset. The
result is lacking interoperability of firewire-net with OS X, Windows
XP, and presumably Linux' eth1394. (I did not test with the latter.)
For example, FTP data transfers to a Linux firewire-net box with max_rec
smaller than the 1500 bytes MTU
- from OS X fail entirely,
- from Win XP start out with a bunch of fragmented datagrams which
time out, then continue with unfragmented datagrams because Win XP
temporarily reduces the MTU to 576 bytes.
So let's fix firewire-net's datagram_size accessors.
Note that firewire-net thereby loses interoperability with unpatched
firewire-net, but only if link fragmentation is employed. (This happens
with large broadcast datagrams, and with large datagrams on several
FireWire CardBus cards with smaller max_rec than equivalent PCI cards,
and it can be worked around by setting a small enough MTU.)
Cc: stable@vger.kernel.org
Signed-off-by: Stefan Richter <stefanr@s5r6.in-berlin.de>
2016-10-31 00:32:01 +08:00
|
|
|
#define fwnet_get_hdr_dg_size(h) ((((h)->w0 & 0x0fff0000) >> 16) + 1)
|
2009-06-08 04:57:53 +08:00
|
|
|
#define fwnet_get_hdr_fg_off(h) (((h)->w0 & 0x00000fff))
|
|
|
|
#define fwnet_get_hdr_dgl(h) (((h)->w1 & 0xffff0000) >> 16)
|
2009-05-19 01:08:06 +08:00
|
|
|
|
firewire: net: fix fragmented datagram_size off-by-one
RFC 2734 defines the datagram_size field in fragment encapsulation
headers thus:
datagram_size: The encoded size of the entire IP datagram. The
value of datagram_size [...] SHALL be one less than the value of
Total Length in the datagram's IP header (see STD 5, RFC 791).
Accordingly, the eth1394 driver of Linux 2.6.36 and older set and got
this field with a -/+1 offset:
ether1394_tx() /* transmit */
ether1394_encapsulate_prep()
hdr->ff.dg_size = dg_size - 1;
ether1394_data_handler() /* receive */
if (hdr->common.lf == ETH1394_HDR_LF_FF)
dg_size = hdr->ff.dg_size + 1;
else
dg_size = hdr->sf.dg_size + 1;
Likewise, I observe OS X 10.4 and Windows XP Pro SP3 to transmit 1500
byte sized datagrams in fragments with datagram_size=1499 if link
fragmentation is required.
Only firewire-net sets and gets datagram_size without this offset. The
result is lacking interoperability of firewire-net with OS X, Windows
XP, and presumably Linux' eth1394. (I did not test with the latter.)
For example, FTP data transfers to a Linux firewire-net box with max_rec
smaller than the 1500 bytes MTU
- from OS X fail entirely,
- from Win XP start out with a bunch of fragmented datagrams which
time out, then continue with unfragmented datagrams because Win XP
temporarily reduces the MTU to 576 bytes.
So let's fix firewire-net's datagram_size accessors.
Note that firewire-net thereby loses interoperability with unpatched
firewire-net, but only if link fragmentation is employed. (This happens
with large broadcast datagrams, and with large datagrams on several
FireWire CardBus cards with smaller max_rec than equivalent PCI cards,
and it can be worked around by setting a small enough MTU.)
Cc: stable@vger.kernel.org
Signed-off-by: Stefan Richter <stefanr@s5r6.in-berlin.de>
2016-10-31 00:32:01 +08:00
|
|
|
#define fwnet_set_hdr_lf(lf) ((lf) << 30)
|
2009-06-08 04:57:53 +08:00
|
|
|
#define fwnet_set_hdr_ether_type(et) (et)
|
firewire: net: fix fragmented datagram_size off-by-one
RFC 2734 defines the datagram_size field in fragment encapsulation
headers thus:
datagram_size: The encoded size of the entire IP datagram. The
value of datagram_size [...] SHALL be one less than the value of
Total Length in the datagram's IP header (see STD 5, RFC 791).
Accordingly, the eth1394 driver of Linux 2.6.36 and older set and got
this field with a -/+1 offset:
ether1394_tx() /* transmit */
ether1394_encapsulate_prep()
hdr->ff.dg_size = dg_size - 1;
ether1394_data_handler() /* receive */
if (hdr->common.lf == ETH1394_HDR_LF_FF)
dg_size = hdr->ff.dg_size + 1;
else
dg_size = hdr->sf.dg_size + 1;
Likewise, I observe OS X 10.4 and Windows XP Pro SP3 to transmit 1500
byte sized datagrams in fragments with datagram_size=1499 if link
fragmentation is required.
Only firewire-net sets and gets datagram_size without this offset. The
result is lacking interoperability of firewire-net with OS X, Windows
XP, and presumably Linux' eth1394. (I did not test with the latter.)
For example, FTP data transfers to a Linux firewire-net box with max_rec
smaller than the 1500 bytes MTU
- from OS X fail entirely,
- from Win XP start out with a bunch of fragmented datagrams which
time out, then continue with unfragmented datagrams because Win XP
temporarily reduces the MTU to 576 bytes.
So let's fix firewire-net's datagram_size accessors.
Note that firewire-net thereby loses interoperability with unpatched
firewire-net, but only if link fragmentation is employed. (This happens
with large broadcast datagrams, and with large datagrams on several
FireWire CardBus cards with smaller max_rec than equivalent PCI cards,
and it can be worked around by setting a small enough MTU.)
Cc: stable@vger.kernel.org
Signed-off-by: Stefan Richter <stefanr@s5r6.in-berlin.de>
2016-10-31 00:32:01 +08:00
|
|
|
#define fwnet_set_hdr_dg_size(dgs) (((dgs) - 1) << 16)
|
2009-06-08 04:57:53 +08:00
|
|
|
#define fwnet_set_hdr_fg_off(fgo) (fgo)
|
2009-05-19 01:08:06 +08:00
|
|
|
|
2009-06-08 04:57:53 +08:00
|
|
|
#define fwnet_set_hdr_dgl(dgl) ((dgl) << 16)
|
2009-05-19 01:08:06 +08:00
|
|
|
|
2009-06-08 04:57:53 +08:00
|
|
|
static inline void fwnet_make_uf_hdr(struct rfc2734_header *hdr,
|
|
|
|
unsigned ether_type)
|
|
|
|
{
|
|
|
|
hdr->w0 = fwnet_set_hdr_lf(RFC2374_HDR_UNFRAG)
|
|
|
|
| fwnet_set_hdr_ether_type(ether_type);
|
|
|
|
}
|
2009-05-19 01:08:06 +08:00
|
|
|
|
2009-06-08 04:57:53 +08:00
|
|
|
static inline void fwnet_make_ff_hdr(struct rfc2734_header *hdr,
|
|
|
|
unsigned ether_type, unsigned dg_size, unsigned dgl)
|
|
|
|
{
|
|
|
|
hdr->w0 = fwnet_set_hdr_lf(RFC2374_HDR_FIRSTFRAG)
|
|
|
|
| fwnet_set_hdr_dg_size(dg_size)
|
|
|
|
| fwnet_set_hdr_ether_type(ether_type);
|
|
|
|
hdr->w1 = fwnet_set_hdr_dgl(dgl);
|
|
|
|
}
|
2009-05-19 01:08:06 +08:00
|
|
|
|
2009-06-08 04:57:53 +08:00
|
|
|
static inline void fwnet_make_sf_hdr(struct rfc2734_header *hdr,
|
|
|
|
unsigned lf, unsigned dg_size, unsigned fg_off, unsigned dgl)
|
|
|
|
{
|
|
|
|
hdr->w0 = fwnet_set_hdr_lf(lf)
|
|
|
|
| fwnet_set_hdr_dg_size(dg_size)
|
|
|
|
| fwnet_set_hdr_fg_off(fg_off);
|
|
|
|
hdr->w1 = fwnet_set_hdr_dgl(dgl);
|
|
|
|
}
|
2009-05-19 01:08:06 +08:00
|
|
|
|
|
|
|
/* This list keeps track of what parts of the datagram have been filled in */
|
2009-06-08 04:57:53 +08:00
|
|
|
struct fwnet_fragment_info {
|
|
|
|
struct list_head fi_link;
|
2009-05-19 01:08:06 +08:00
|
|
|
u16 offset;
|
|
|
|
u16 len;
|
|
|
|
};
|
|
|
|
|
2009-06-08 04:57:53 +08:00
|
|
|
struct fwnet_partial_datagram {
|
|
|
|
struct list_head pd_link;
|
|
|
|
struct list_head fi_list;
|
2009-05-19 01:08:06 +08:00
|
|
|
struct sk_buff *skb;
|
|
|
|
/* FIXME Why not use skb->data? */
|
|
|
|
char *pbuf;
|
|
|
|
u16 datagram_label;
|
|
|
|
u16 ether_type;
|
|
|
|
u16 datagram_size;
|
|
|
|
};
|
|
|
|
|
2009-06-14 17:45:27 +08:00
|
|
|
static DEFINE_MUTEX(fwnet_device_mutex);
|
|
|
|
static LIST_HEAD(fwnet_device_list);
|
2009-05-19 01:08:06 +08:00
|
|
|
|
2009-06-08 04:57:53 +08:00
|
|
|
struct fwnet_device {
|
2009-06-14 17:45:27 +08:00
|
|
|
struct list_head dev_link;
|
2009-05-19 01:08:06 +08:00
|
|
|
spinlock_t lock;
|
2009-06-08 04:57:53 +08:00
|
|
|
enum {
|
|
|
|
FWNET_BROADCAST_ERROR,
|
|
|
|
FWNET_BROADCAST_RUNNING,
|
|
|
|
FWNET_BROADCAST_STOPPED,
|
|
|
|
} broadcast_state;
|
2009-05-19 01:08:06 +08:00
|
|
|
struct fw_iso_context *broadcast_rcv_context;
|
|
|
|
struct fw_iso_buffer broadcast_rcv_buffer;
|
|
|
|
void **broadcast_rcv_buffer_ptrs;
|
|
|
|
unsigned broadcast_rcv_next_ptr;
|
|
|
|
unsigned num_broadcast_rcv_ptrs;
|
|
|
|
unsigned rcv_buffer_size;
|
|
|
|
/*
|
|
|
|
* This value is the maximum unfragmented datagram size that can be
|
|
|
|
* sent by the hardware. It already has the GASP overhead and the
|
|
|
|
* unfragmented datagram header overhead calculated into it.
|
|
|
|
*/
|
|
|
|
unsigned broadcast_xmt_max_payload;
|
|
|
|
u16 broadcast_xmt_datagramlabel;
|
|
|
|
|
|
|
|
/*
|
2009-06-08 04:57:53 +08:00
|
|
|
* The CSR address that remote nodes must send datagrams to for us to
|
2009-05-19 01:08:06 +08:00
|
|
|
* receive them.
|
|
|
|
*/
|
|
|
|
struct fw_address_handler handler;
|
|
|
|
u64 local_fifo;
|
|
|
|
|
2010-11-07 06:18:23 +08:00
|
|
|
/* Number of tx datagrams that have been queued but not yet acked */
|
|
|
|
int queued_datagrams;
|
2009-05-19 01:08:06 +08:00
|
|
|
|
2010-12-18 05:22:33 +08:00
|
|
|
int peer_count;
|
2009-06-14 17:45:27 +08:00
|
|
|
struct list_head peer_list;
|
2009-05-19 01:08:06 +08:00
|
|
|
struct fw_card *card;
|
2009-06-14 17:45:27 +08:00
|
|
|
struct net_device *netdev;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct fwnet_peer {
|
|
|
|
struct list_head peer_link;
|
|
|
|
struct fwnet_device *dev;
|
|
|
|
u64 guid;
|
|
|
|
|
|
|
|
/* guarded by dev->lock */
|
|
|
|
struct list_head pd_list; /* received partial datagrams */
|
|
|
|
unsigned pdg_size; /* pd_list size */
|
|
|
|
|
|
|
|
u16 datagram_label; /* outgoing datagram label */
|
2010-11-07 06:18:23 +08:00
|
|
|
u16 max_payload; /* includes RFC2374_FRAG_HDR_SIZE overhead */
|
2009-06-14 17:45:27 +08:00
|
|
|
int node_id;
|
|
|
|
int generation;
|
|
|
|
unsigned speed;
|
2009-05-19 01:08:06 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
/* This is our task struct. It's used for the packet complete callback. */
|
2009-06-08 04:57:53 +08:00
|
|
|
struct fwnet_packet_task {
|
2009-05-19 01:08:06 +08:00
|
|
|
struct fw_transaction transaction;
|
2009-06-08 04:57:53 +08:00
|
|
|
struct rfc2734_header hdr;
|
2009-05-19 01:08:06 +08:00
|
|
|
struct sk_buff *skb;
|
2009-06-08 04:57:53 +08:00
|
|
|
struct fwnet_device *dev;
|
|
|
|
|
2009-05-19 01:08:06 +08:00
|
|
|
int outstanding_pkts;
|
|
|
|
u64 fifo_addr;
|
|
|
|
u16 dest_node;
|
2010-11-07 06:18:23 +08:00
|
|
|
u16 max_payload;
|
2009-05-19 01:08:06 +08:00
|
|
|
u8 generation;
|
|
|
|
u8 speed;
|
2010-11-07 06:18:23 +08:00
|
|
|
u8 enqueued;
|
2009-05-19 01:08:06 +08:00
|
|
|
};
|
|
|
|
|
2009-06-08 04:57:53 +08:00
|
|
|
/*
|
|
|
|
* saddr == NULL means use device source address.
|
|
|
|
* daddr == NULL means leave destination address (eg unresolved arp).
|
|
|
|
*/
|
|
|
|
static int fwnet_header_create(struct sk_buff *skb, struct net_device *net,
|
|
|
|
unsigned short type, const void *daddr,
|
|
|
|
const void *saddr, unsigned len)
|
|
|
|
{
|
|
|
|
struct fwnet_header *h;
|
2009-05-19 01:08:06 +08:00
|
|
|
|
networking: make skb_push & __skb_push return void pointers
It seems like a historic accident that these return unsigned char *,
and in many places that means casts are required, more often than not.
Make these functions return void * and remove all the casts across
the tree, adding a (u8 *) cast only where the unsigned char pointer
was used directly, all done with the following spatch:
@@
expression SKB, LEN;
typedef u8;
identifier fn = { skb_push, __skb_push, skb_push_rcsum };
@@
- *(fn(SKB, LEN))
+ *(u8 *)fn(SKB, LEN)
@@
expression E, SKB, LEN;
identifier fn = { skb_push, __skb_push, skb_push_rcsum };
type T;
@@
- E = ((T *)(fn(SKB, LEN)))
+ E = fn(SKB, LEN)
@@
expression SKB, LEN;
identifier fn = { skb_push, __skb_push, skb_push_rcsum };
@@
- fn(SKB, LEN)[0]
+ *(u8 *)fn(SKB, LEN)
Note that the last part there converts from push(...)[0] to the
more idiomatic *(u8 *)push(...).
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2017-06-16 20:29:23 +08:00
|
|
|
h = skb_push(skb, sizeof(*h));
|
2009-06-08 04:57:53 +08:00
|
|
|
put_unaligned_be16(type, &h->h_proto);
|
2009-05-19 01:08:06 +08:00
|
|
|
|
2009-06-08 04:57:53 +08:00
|
|
|
if (net->flags & (IFF_LOOPBACK | IFF_NOARP)) {
|
|
|
|
memset(h->h_dest, 0, net->addr_len);
|
2009-05-19 01:08:06 +08:00
|
|
|
|
2009-06-08 04:57:53 +08:00
|
|
|
return net->hard_header_len;
|
2009-05-19 01:08:06 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
if (daddr) {
|
2009-06-08 04:57:53 +08:00
|
|
|
memcpy(h->h_dest, daddr, net->addr_len);
|
|
|
|
|
|
|
|
return net->hard_header_len;
|
2009-05-19 01:08:06 +08:00
|
|
|
}
|
|
|
|
|
2009-06-08 04:57:53 +08:00
|
|
|
return -net->hard_header_len;
|
2009-05-19 01:08:06 +08:00
|
|
|
}
|
|
|
|
|
2009-06-08 04:57:53 +08:00
|
|
|
static int fwnet_header_cache(const struct neighbour *neigh,
|
2011-07-13 14:28:12 +08:00
|
|
|
struct hh_cache *hh, __be16 type)
|
2009-06-08 04:57:53 +08:00
|
|
|
{
|
|
|
|
struct net_device *net;
|
|
|
|
struct fwnet_header *h;
|
2009-05-19 01:08:06 +08:00
|
|
|
|
2011-07-13 14:28:12 +08:00
|
|
|
if (type == cpu_to_be16(ETH_P_802_3))
|
2009-05-19 01:08:06 +08:00
|
|
|
return -1;
|
2009-06-08 04:57:53 +08:00
|
|
|
net = neigh->dev;
|
2013-01-20 06:03:07 +08:00
|
|
|
h = (struct fwnet_header *)((u8 *)hh->hh_data + HH_DATA_OFF(sizeof(*h)));
|
2011-07-13 14:28:12 +08:00
|
|
|
h->h_proto = type;
|
2009-06-08 04:57:53 +08:00
|
|
|
memcpy(h->h_dest, neigh->ha, net->addr_len);
|
2019-11-08 10:29:11 +08:00
|
|
|
|
|
|
|
/* Pairs with the READ_ONCE() in neigh_resolve_output(),
|
|
|
|
* neigh_hh_output() and neigh_update_hhs().
|
|
|
|
*/
|
|
|
|
smp_store_release(&hh->hh_len, FWNET_HLEN);
|
2009-05-19 01:08:06 +08:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Called by Address Resolution module to notify changes in address. */
|
2009-06-08 04:57:53 +08:00
|
|
|
static void fwnet_header_cache_update(struct hh_cache *hh,
|
|
|
|
const struct net_device *net, const unsigned char *haddr)
|
|
|
|
{
|
2013-01-20 06:03:07 +08:00
|
|
|
memcpy((u8 *)hh->hh_data + HH_DATA_OFF(FWNET_HLEN), haddr, net->addr_len);
|
2009-05-19 01:08:06 +08:00
|
|
|
}
|
|
|
|
|
2009-06-08 04:57:53 +08:00
|
|
|
static int fwnet_header_parse(const struct sk_buff *skb, unsigned char *haddr)
|
|
|
|
{
|
|
|
|
memcpy(haddr, skb->dev->dev_addr, FWNET_ALEN);
|
|
|
|
|
|
|
|
return FWNET_ALEN;
|
2009-05-19 01:08:06 +08:00
|
|
|
}
|
|
|
|
|
2009-06-08 04:57:53 +08:00
|
|
|
static const struct header_ops fwnet_header_ops = {
|
|
|
|
.create = fwnet_header_create,
|
|
|
|
.cache = fwnet_header_cache,
|
|
|
|
.cache_update = fwnet_header_cache_update,
|
|
|
|
.parse = fwnet_header_parse,
|
2009-05-19 01:08:06 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
/* FIXME: is this correct for all cases? */
|
2009-06-08 04:57:53 +08:00
|
|
|
static bool fwnet_frag_overlap(struct fwnet_partial_datagram *pd,
|
|
|
|
unsigned offset, unsigned len)
|
2009-05-19 01:08:06 +08:00
|
|
|
{
|
2009-06-08 04:57:53 +08:00
|
|
|
struct fwnet_fragment_info *fi;
|
2009-05-19 01:08:06 +08:00
|
|
|
unsigned end = offset + len;
|
|
|
|
|
2009-06-08 04:57:53 +08:00
|
|
|
list_for_each_entry(fi, &pd->fi_list, fi_link)
|
|
|
|
if (offset < fi->offset + fi->len && end > fi->offset)
|
2009-05-19 01:08:06 +08:00
|
|
|
return true;
|
2009-06-08 04:57:53 +08:00
|
|
|
|
2009-05-19 01:08:06 +08:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Assumes that new fragment does not overlap any existing fragments */
|
2009-06-08 04:57:53 +08:00
|
|
|
static struct fwnet_fragment_info *fwnet_frag_new(
|
|
|
|
struct fwnet_partial_datagram *pd, unsigned offset, unsigned len)
|
|
|
|
{
|
|
|
|
struct fwnet_fragment_info *fi, *fi2, *new;
|
2009-05-19 01:08:06 +08:00
|
|
|
struct list_head *list;
|
|
|
|
|
2009-06-08 04:57:53 +08:00
|
|
|
list = &pd->fi_list;
|
|
|
|
list_for_each_entry(fi, &pd->fi_list, fi_link) {
|
2009-05-19 01:08:06 +08:00
|
|
|
if (fi->offset + fi->len == offset) {
|
|
|
|
/* The new fragment can be tacked on to the end */
|
|
|
|
/* Did the new fragment plug a hole? */
|
2009-06-08 04:57:53 +08:00
|
|
|
fi2 = list_entry(fi->fi_link.next,
|
|
|
|
struct fwnet_fragment_info, fi_link);
|
2009-05-19 01:08:06 +08:00
|
|
|
if (fi->offset + fi->len == fi2->offset) {
|
|
|
|
/* glue fragments together */
|
|
|
|
fi->len += len + fi2->len;
|
2009-06-08 04:57:53 +08:00
|
|
|
list_del(&fi2->fi_link);
|
2009-05-19 01:08:06 +08:00
|
|
|
kfree(fi2);
|
|
|
|
} else {
|
|
|
|
fi->len += len;
|
|
|
|
}
|
2009-06-08 04:57:53 +08:00
|
|
|
|
2009-05-19 01:08:06 +08:00
|
|
|
return fi;
|
|
|
|
}
|
|
|
|
if (offset + len == fi->offset) {
|
|
|
|
/* The new fragment can be tacked on to the beginning */
|
|
|
|
/* Did the new fragment plug a hole? */
|
2009-06-08 04:57:53 +08:00
|
|
|
fi2 = list_entry(fi->fi_link.prev,
|
|
|
|
struct fwnet_fragment_info, fi_link);
|
2009-05-19 01:08:06 +08:00
|
|
|
if (fi2->offset + fi2->len == fi->offset) {
|
|
|
|
/* glue fragments together */
|
|
|
|
fi2->len += fi->len + len;
|
2009-06-08 04:57:53 +08:00
|
|
|
list_del(&fi->fi_link);
|
2009-05-19 01:08:06 +08:00
|
|
|
kfree(fi);
|
2009-06-08 04:57:53 +08:00
|
|
|
|
2009-05-19 01:08:06 +08:00
|
|
|
return fi2;
|
|
|
|
}
|
|
|
|
fi->offset = offset;
|
|
|
|
fi->len += len;
|
2009-06-08 04:57:53 +08:00
|
|
|
|
2009-05-19 01:08:06 +08:00
|
|
|
return fi;
|
|
|
|
}
|
|
|
|
if (offset > fi->offset + fi->len) {
|
2009-06-08 04:57:53 +08:00
|
|
|
list = &fi->fi_link;
|
2009-05-19 01:08:06 +08:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (offset + len < fi->offset) {
|
2009-06-08 04:57:53 +08:00
|
|
|
list = fi->fi_link.prev;
|
2009-05-19 01:08:06 +08:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
new = kmalloc(sizeof(*new), GFP_ATOMIC);
|
2013-03-25 00:32:00 +08:00
|
|
|
if (!new)
|
2009-05-19 01:08:06 +08:00
|
|
|
return NULL;
|
|
|
|
|
|
|
|
new->offset = offset;
|
|
|
|
new->len = len;
|
2009-06-08 04:57:53 +08:00
|
|
|
list_add(&new->fi_link, list);
|
|
|
|
|
2009-05-19 01:08:06 +08:00
|
|
|
return new;
|
|
|
|
}
|
|
|
|
|
2009-06-08 04:57:53 +08:00
|
|
|
static struct fwnet_partial_datagram *fwnet_pd_new(struct net_device *net,
|
|
|
|
struct fwnet_peer *peer, u16 datagram_label, unsigned dg_size,
|
|
|
|
void *frag_buf, unsigned frag_off, unsigned frag_len)
|
|
|
|
{
|
|
|
|
struct fwnet_partial_datagram *new;
|
|
|
|
struct fwnet_fragment_info *fi;
|
2009-05-19 01:08:06 +08:00
|
|
|
|
|
|
|
new = kmalloc(sizeof(*new), GFP_ATOMIC);
|
|
|
|
if (!new)
|
|
|
|
goto fail;
|
2009-06-08 04:57:53 +08:00
|
|
|
|
|
|
|
INIT_LIST_HEAD(&new->fi_list);
|
|
|
|
fi = fwnet_frag_new(new, frag_off, frag_len);
|
|
|
|
if (fi == NULL)
|
2009-05-19 01:08:06 +08:00
|
|
|
goto fail_w_new;
|
2009-06-08 04:57:53 +08:00
|
|
|
|
2009-05-19 01:08:06 +08:00
|
|
|
new->datagram_label = datagram_label;
|
|
|
|
new->datagram_size = dg_size;
|
2013-01-20 06:03:07 +08:00
|
|
|
new->skb = dev_alloc_skb(dg_size + LL_RESERVED_SPACE(net));
|
2009-06-08 04:57:53 +08:00
|
|
|
if (new->skb == NULL)
|
2009-05-19 01:08:06 +08:00
|
|
|
goto fail_w_fi;
|
2009-06-08 04:57:53 +08:00
|
|
|
|
2013-01-20 06:03:07 +08:00
|
|
|
skb_reserve(new->skb, LL_RESERVED_SPACE(net));
|
2009-05-19 01:08:06 +08:00
|
|
|
new->pbuf = skb_put(new->skb, dg_size);
|
|
|
|
memcpy(new->pbuf + frag_off, frag_buf, frag_len);
|
2009-06-08 04:57:53 +08:00
|
|
|
list_add_tail(&new->pd_link, &peer->pd_list);
|
|
|
|
|
2009-05-19 01:08:06 +08:00
|
|
|
return new;
|
|
|
|
|
|
|
|
fail_w_fi:
|
|
|
|
kfree(fi);
|
|
|
|
fail_w_new:
|
|
|
|
kfree(new);
|
|
|
|
fail:
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2009-06-08 04:57:53 +08:00
|
|
|
static struct fwnet_partial_datagram *fwnet_pd_find(struct fwnet_peer *peer,
|
|
|
|
u16 datagram_label)
|
|
|
|
{
|
|
|
|
struct fwnet_partial_datagram *pd;
|
2009-05-19 01:08:06 +08:00
|
|
|
|
2009-06-08 04:57:53 +08:00
|
|
|
list_for_each_entry(pd, &peer->pd_list, pd_link)
|
|
|
|
if (pd->datagram_label == datagram_label)
|
2009-05-19 01:08:06 +08:00
|
|
|
return pd;
|
2009-06-08 04:57:53 +08:00
|
|
|
|
2009-05-19 01:08:06 +08:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-06-08 04:57:53 +08:00
|
|
|
static void fwnet_pd_delete(struct fwnet_partial_datagram *old)
|
|
|
|
{
|
|
|
|
struct fwnet_fragment_info *fi, *n;
|
2009-05-19 01:08:06 +08:00
|
|
|
|
2009-06-08 04:57:53 +08:00
|
|
|
list_for_each_entry_safe(fi, n, &old->fi_list, fi_link)
|
2009-05-19 01:08:06 +08:00
|
|
|
kfree(fi);
|
2009-06-08 04:57:53 +08:00
|
|
|
|
|
|
|
list_del(&old->pd_link);
|
2009-05-19 01:08:06 +08:00
|
|
|
dev_kfree_skb_any(old->skb);
|
|
|
|
kfree(old);
|
|
|
|
}
|
|
|
|
|
2009-06-08 04:57:53 +08:00
|
|
|
static bool fwnet_pd_update(struct fwnet_peer *peer,
|
|
|
|
struct fwnet_partial_datagram *pd, void *frag_buf,
|
|
|
|
unsigned frag_off, unsigned frag_len)
|
|
|
|
{
|
|
|
|
if (fwnet_frag_new(pd, frag_off, frag_len) == NULL)
|
2009-05-19 01:08:06 +08:00
|
|
|
return false;
|
2009-06-08 04:57:53 +08:00
|
|
|
|
2009-05-19 01:08:06 +08:00
|
|
|
memcpy(pd->pbuf + frag_off, frag_buf, frag_len);
|
|
|
|
|
|
|
|
/*
|
2011-03-31 09:57:33 +08:00
|
|
|
* Move list entry to beginning of list so that oldest partial
|
2009-05-19 01:08:06 +08:00
|
|
|
* datagrams percolate to the end of the list
|
|
|
|
*/
|
2009-06-08 04:57:53 +08:00
|
|
|
list_move_tail(&pd->pd_link, &peer->pd_list);
|
|
|
|
|
2009-05-19 01:08:06 +08:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2009-06-08 04:57:53 +08:00
|
|
|
static bool fwnet_pd_is_complete(struct fwnet_partial_datagram *pd)
|
|
|
|
{
|
|
|
|
struct fwnet_fragment_info *fi;
|
2009-05-19 01:08:06 +08:00
|
|
|
|
2009-06-08 04:57:53 +08:00
|
|
|
fi = list_entry(pd->fi_list.next, struct fwnet_fragment_info, fi_link);
|
2009-05-19 01:08:06 +08:00
|
|
|
|
2009-06-08 04:57:53 +08:00
|
|
|
return fi->len == pd->datagram_size;
|
2009-05-19 01:08:06 +08:00
|
|
|
}
|
|
|
|
|
2009-06-14 17:45:27 +08:00
|
|
|
/* caller must hold dev->lock */
|
2009-06-08 04:57:53 +08:00
|
|
|
static struct fwnet_peer *fwnet_peer_find_by_guid(struct fwnet_device *dev,
|
|
|
|
u64 guid)
|
|
|
|
{
|
2009-06-14 17:45:27 +08:00
|
|
|
struct fwnet_peer *peer;
|
2009-05-19 01:08:06 +08:00
|
|
|
|
2009-06-14 17:45:27 +08:00
|
|
|
list_for_each_entry(peer, &dev->peer_list, peer_link)
|
|
|
|
if (peer->guid == guid)
|
|
|
|
return peer;
|
2009-05-19 01:08:06 +08:00
|
|
|
|
2009-06-14 17:45:27 +08:00
|
|
|
return NULL;
|
2009-05-19 01:08:06 +08:00
|
|
|
}
|
|
|
|
|
2009-06-14 17:45:27 +08:00
|
|
|
/* caller must hold dev->lock */
|
2009-06-08 04:57:53 +08:00
|
|
|
static struct fwnet_peer *fwnet_peer_find_by_node_id(struct fwnet_device *dev,
|
2009-06-14 17:45:27 +08:00
|
|
|
int node_id, int generation)
|
2009-06-08 04:57:53 +08:00
|
|
|
{
|
2009-06-14 17:45:27 +08:00
|
|
|
struct fwnet_peer *peer;
|
2009-05-19 01:08:06 +08:00
|
|
|
|
2009-06-14 17:45:27 +08:00
|
|
|
list_for_each_entry(peer, &dev->peer_list, peer_link)
|
|
|
|
if (peer->node_id == node_id &&
|
|
|
|
peer->generation == generation)
|
|
|
|
return peer;
|
2009-06-08 04:57:53 +08:00
|
|
|
|
2009-06-14 17:45:27 +08:00
|
|
|
return NULL;
|
2009-05-19 01:08:06 +08:00
|
|
|
}
|
|
|
|
|
2009-06-14 17:45:27 +08:00
|
|
|
/* See IEEE 1394-2008 table 6-4, table 8-8, table 16-18. */
|
|
|
|
static unsigned fwnet_max_payload(unsigned max_rec, unsigned speed)
|
2009-06-08 04:57:53 +08:00
|
|
|
{
|
2009-06-14 17:45:27 +08:00
|
|
|
max_rec = min(max_rec, speed + 8);
|
2011-09-19 06:20:48 +08:00
|
|
|
max_rec = clamp(max_rec, 8U, 11U); /* 512...4096 */
|
2009-06-14 17:45:27 +08:00
|
|
|
|
|
|
|
return (1 << (max_rec + 1)) - RFC2374_FRAG_HDR_SIZE;
|
2009-05-19 01:08:06 +08:00
|
|
|
}
|
|
|
|
|
2009-06-14 17:45:27 +08:00
|
|
|
|
2009-06-08 04:57:53 +08:00
|
|
|
static int fwnet_finish_incoming_packet(struct net_device *net,
|
|
|
|
struct sk_buff *skb, u16 source_node_id,
|
|
|
|
bool is_broadcast, u16 ether_type)
|
|
|
|
{
|
2009-05-19 01:08:06 +08:00
|
|
|
int status;
|
|
|
|
|
2013-03-25 16:25:38 +08:00
|
|
|
switch (ether_type) {
|
|
|
|
case ETH_P_ARP:
|
|
|
|
case ETH_P_IP:
|
2013-03-25 16:26:24 +08:00
|
|
|
#if IS_ENABLED(CONFIG_IPV6)
|
|
|
|
case ETH_P_IPV6:
|
|
|
|
#endif
|
2013-03-25 16:25:38 +08:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
2009-05-19 01:08:06 +08:00
|
|
|
/* Write metadata, and then pass to the receive level */
|
2009-06-08 04:57:53 +08:00
|
|
|
skb->dev = net;
|
2013-01-20 05:43:40 +08:00
|
|
|
skb->ip_summed = CHECKSUM_NONE;
|
2009-05-19 01:08:06 +08:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Parse the encapsulation header. This actually does the job of
|
firewire net, ipv4 arp: Extend hardware address and remove driver-level packet inspection.
Inspection of upper layer protocol is considered harmful, especially
if it is about ARP or other stateful upper layer protocol; driver
cannot (and should not) have full state of them.
IPv4 over Firewire module used to inspect ARP (both in sending path
and in receiving path), and record peer's GUID, max packet size, max
speed and fifo address. This patch removes such inspection by extending
our "hardware address" definition to include other information as well:
max packet size, max speed and fifo. By doing this, The neighbour
module in networking subsystem can cache them.
Note: As we have started ignoring sspd and max_rec in ARP/NDP, those
information will not be used in the driver when sending.
When a packet is being sent, the IP layer fills our pseudo header with
the extended "hardware address", including GUID and fifo. The driver
can look-up node-id (the real but rather volatile low-level address)
by GUID, and then the module can send the packet to the wire using
parameters provided in the extendedn hardware address.
This approach is realistic because IP over IEEE1394 (RFC2734) and IPv6
over IEEE1394 (RFC3146) share same "hardware address" format
in their address resolution protocols.
Here, extended "hardware address" is defined as follows:
union fwnet_hwaddr {
u8 u[16];
struct {
__be64 uniq_id; /* EUI-64 */
u8 max_rec; /* max packet size */
u8 sspd; /* max speed */
__be16 fifo_hi; /* hi 16bits of FIFO addr */
__be32 fifo_lo; /* lo 32bits of FIFO addr */
} __packed uc;
};
Note that Hardware address is declared as union, so that we can map full
IP address into this, when implementing MCAP (Multicast Cannel Allocation
Protocol) for IPv6, but IP and ARP subsystem do not need to know this
format in detail.
One difference between original ARP (RFC826) and 1394 ARP (RFC2734)
is that 1394 ARP Request/Reply do not contain the target hardware address
field (aka ar$tha). This difference is handled in the ARP subsystem.
CC: Stephan Gatzka <stephan.gatzka@gmail.com>
Signed-off-by: YOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org>
Signed-off-by: David S. Miller <davem@davemloft.net>
2013-03-25 16:26:16 +08:00
|
|
|
* converting to an ethernet-like pseudo frame header.
|
2009-05-19 01:08:06 +08:00
|
|
|
*/
|
2009-06-08 04:57:53 +08:00
|
|
|
if (dev_hard_header(skb, net, ether_type,
|
firewire net, ipv4 arp: Extend hardware address and remove driver-level packet inspection.
Inspection of upper layer protocol is considered harmful, especially
if it is about ARP or other stateful upper layer protocol; driver
cannot (and should not) have full state of them.
IPv4 over Firewire module used to inspect ARP (both in sending path
and in receiving path), and record peer's GUID, max packet size, max
speed and fifo address. This patch removes such inspection by extending
our "hardware address" definition to include other information as well:
max packet size, max speed and fifo. By doing this, The neighbour
module in networking subsystem can cache them.
Note: As we have started ignoring sspd and max_rec in ARP/NDP, those
information will not be used in the driver when sending.
When a packet is being sent, the IP layer fills our pseudo header with
the extended "hardware address", including GUID and fifo. The driver
can look-up node-id (the real but rather volatile low-level address)
by GUID, and then the module can send the packet to the wire using
parameters provided in the extendedn hardware address.
This approach is realistic because IP over IEEE1394 (RFC2734) and IPv6
over IEEE1394 (RFC3146) share same "hardware address" format
in their address resolution protocols.
Here, extended "hardware address" is defined as follows:
union fwnet_hwaddr {
u8 u[16];
struct {
__be64 uniq_id; /* EUI-64 */
u8 max_rec; /* max packet size */
u8 sspd; /* max speed */
__be16 fifo_hi; /* hi 16bits of FIFO addr */
__be32 fifo_lo; /* lo 32bits of FIFO addr */
} __packed uc;
};
Note that Hardware address is declared as union, so that we can map full
IP address into this, when implementing MCAP (Multicast Cannel Allocation
Protocol) for IPv6, but IP and ARP subsystem do not need to know this
format in detail.
One difference between original ARP (RFC826) and 1394 ARP (RFC2734)
is that 1394 ARP Request/Reply do not contain the target hardware address
field (aka ar$tha). This difference is handled in the ARP subsystem.
CC: Stephan Gatzka <stephan.gatzka@gmail.com>
Signed-off-by: YOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org>
Signed-off-by: David S. Miller <davem@davemloft.net>
2013-03-25 16:26:16 +08:00
|
|
|
is_broadcast ? net->broadcast : net->dev_addr,
|
2009-06-08 04:57:53 +08:00
|
|
|
NULL, skb->len) >= 0) {
|
|
|
|
struct fwnet_header *eth;
|
2009-05-19 01:08:06 +08:00
|
|
|
u16 *rawp;
|
|
|
|
__be16 protocol;
|
|
|
|
|
|
|
|
skb_reset_mac_header(skb);
|
|
|
|
skb_pull(skb, sizeof(*eth));
|
2009-06-08 04:57:53 +08:00
|
|
|
eth = (struct fwnet_header *)skb_mac_header(skb);
|
2013-03-25 16:25:48 +08:00
|
|
|
if (fwnet_hwaddr_is_multicast(eth->h_dest)) {
|
2009-06-08 04:57:53 +08:00
|
|
|
if (memcmp(eth->h_dest, net->broadcast,
|
|
|
|
net->addr_len) == 0)
|
2009-05-19 01:08:06 +08:00
|
|
|
skb->pkt_type = PACKET_BROADCAST;
|
|
|
|
#if 0
|
|
|
|
else
|
|
|
|
skb->pkt_type = PACKET_MULTICAST;
|
|
|
|
#endif
|
|
|
|
} else {
|
2009-06-14 17:46:57 +08:00
|
|
|
if (memcmp(eth->h_dest, net->dev_addr, net->addr_len))
|
2009-05-19 01:08:06 +08:00
|
|
|
skb->pkt_type = PACKET_OTHERHOST;
|
|
|
|
}
|
2013-03-28 12:38:25 +08:00
|
|
|
if (ntohs(eth->h_proto) >= ETH_P_802_3_MIN) {
|
2009-05-19 01:08:06 +08:00
|
|
|
protocol = eth->h_proto;
|
|
|
|
} else {
|
|
|
|
rawp = (u16 *)skb->data;
|
2009-06-08 04:57:53 +08:00
|
|
|
if (*rawp == 0xffff)
|
2009-05-19 01:08:06 +08:00
|
|
|
protocol = htons(ETH_P_802_3);
|
2009-06-08 04:57:53 +08:00
|
|
|
else
|
2009-05-19 01:08:06 +08:00
|
|
|
protocol = htons(ETH_P_802_2);
|
|
|
|
}
|
|
|
|
skb->protocol = protocol;
|
|
|
|
}
|
|
|
|
status = netif_rx(skb);
|
2009-06-08 04:57:53 +08:00
|
|
|
if (status == NET_RX_DROP) {
|
|
|
|
net->stats.rx_errors++;
|
|
|
|
net->stats.rx_dropped++;
|
2009-05-19 01:08:06 +08:00
|
|
|
} else {
|
2009-06-08 04:57:53 +08:00
|
|
|
net->stats.rx_packets++;
|
|
|
|
net->stats.rx_bytes += skb->len;
|
2009-05-19 01:08:06 +08:00
|
|
|
}
|
2009-06-08 04:57:53 +08:00
|
|
|
|
2009-05-19 01:08:06 +08:00
|
|
|
return 0;
|
|
|
|
|
2013-03-25 16:25:38 +08:00
|
|
|
err:
|
2009-06-08 04:57:53 +08:00
|
|
|
net->stats.rx_errors++;
|
|
|
|
net->stats.rx_dropped++;
|
|
|
|
|
2009-05-19 01:08:06 +08:00
|
|
|
dev_kfree_skb_any(skb);
|
2009-06-08 04:57:53 +08:00
|
|
|
|
2010-08-17 05:45:54 +08:00
|
|
|
return -ENOENT;
|
2009-05-19 01:08:06 +08:00
|
|
|
}
|
|
|
|
|
2009-06-08 04:57:53 +08:00
|
|
|
static int fwnet_incoming_packet(struct fwnet_device *dev, __be32 *buf, int len,
|
2009-06-14 17:45:27 +08:00
|
|
|
int source_node_id, int generation,
|
|
|
|
bool is_broadcast)
|
2009-06-08 04:57:53 +08:00
|
|
|
{
|
2009-05-19 01:08:06 +08:00
|
|
|
struct sk_buff *skb;
|
2009-06-14 17:45:27 +08:00
|
|
|
struct net_device *net = dev->netdev;
|
2009-06-08 04:57:53 +08:00
|
|
|
struct rfc2734_header hdr;
|
2009-05-19 01:08:06 +08:00
|
|
|
unsigned lf;
|
|
|
|
unsigned long flags;
|
2009-06-08 04:57:53 +08:00
|
|
|
struct fwnet_peer *peer;
|
|
|
|
struct fwnet_partial_datagram *pd;
|
2009-05-19 01:08:06 +08:00
|
|
|
int fg_off;
|
|
|
|
int dg_size;
|
|
|
|
u16 datagram_label;
|
|
|
|
int retval;
|
|
|
|
u16 ether_type;
|
|
|
|
|
2016-10-30 03:28:18 +08:00
|
|
|
if (len <= RFC2374_UNFRAG_HDR_SIZE)
|
|
|
|
return 0;
|
|
|
|
|
2009-06-08 04:57:53 +08:00
|
|
|
hdr.w0 = be32_to_cpu(buf[0]);
|
|
|
|
lf = fwnet_get_hdr_lf(&hdr);
|
|
|
|
if (lf == RFC2374_HDR_UNFRAG) {
|
2009-05-19 01:08:06 +08:00
|
|
|
/*
|
|
|
|
* An unfragmented datagram has been received by the ieee1394
|
|
|
|
* bus. Build an skbuff around it so we can pass it to the
|
|
|
|
* high level network layer.
|
|
|
|
*/
|
2009-06-08 04:57:53 +08:00
|
|
|
ether_type = fwnet_get_hdr_ether_type(&hdr);
|
2009-05-19 01:08:06 +08:00
|
|
|
buf++;
|
2009-06-08 04:57:53 +08:00
|
|
|
len -= RFC2374_UNFRAG_HDR_SIZE;
|
2009-05-19 01:08:06 +08:00
|
|
|
|
2013-01-20 06:03:07 +08:00
|
|
|
skb = dev_alloc_skb(len + LL_RESERVED_SPACE(net));
|
2009-05-19 01:08:06 +08:00
|
|
|
if (unlikely(!skb)) {
|
2009-06-08 04:57:53 +08:00
|
|
|
net->stats.rx_dropped++;
|
|
|
|
|
2010-08-17 05:45:54 +08:00
|
|
|
return -ENOMEM;
|
2009-05-19 01:08:06 +08:00
|
|
|
}
|
2013-01-20 06:03:07 +08:00
|
|
|
skb_reserve(skb, LL_RESERVED_SPACE(net));
|
networking: introduce and use skb_put_data()
A common pattern with skb_put() is to just want to memcpy()
some data into the new space, introduce skb_put_data() for
this.
An spatch similar to the one for skb_put_zero() converts many
of the places using it:
@@
identifier p, p2;
expression len, skb, data;
type t, t2;
@@
(
-p = skb_put(skb, len);
+p = skb_put_data(skb, data, len);
|
-p = (t)skb_put(skb, len);
+p = skb_put_data(skb, data, len);
)
(
p2 = (t2)p;
-memcpy(p2, data, len);
|
-memcpy(p, data, len);
)
@@
type t, t2;
identifier p, p2;
expression skb, data;
@@
t *p;
...
(
-p = skb_put(skb, sizeof(t));
+p = skb_put_data(skb, data, sizeof(t));
|
-p = (t *)skb_put(skb, sizeof(t));
+p = skb_put_data(skb, data, sizeof(t));
)
(
p2 = (t2)p;
-memcpy(p2, data, sizeof(*p));
|
-memcpy(p, data, sizeof(*p));
)
@@
expression skb, len, data;
@@
-memcpy(skb_put(skb, len), data, len);
+skb_put_data(skb, data, len);
(again, manually post-processed to retain some comments)
Reviewed-by: Stephen Hemminger <stephen@networkplumber.org>
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2017-06-16 20:29:20 +08:00
|
|
|
skb_put_data(skb, buf, len);
|
2009-06-08 04:57:53 +08:00
|
|
|
|
|
|
|
return fwnet_finish_incoming_packet(net, skb, source_node_id,
|
|
|
|
is_broadcast, ether_type);
|
2009-05-19 01:08:06 +08:00
|
|
|
}
|
2016-10-30 03:28:18 +08:00
|
|
|
|
2009-05-19 01:08:06 +08:00
|
|
|
/* A datagram fragment has been received, now the fun begins. */
|
2016-10-30 03:28:18 +08:00
|
|
|
|
|
|
|
if (len <= RFC2374_FRAG_HDR_SIZE)
|
|
|
|
return 0;
|
|
|
|
|
2009-05-19 01:08:06 +08:00
|
|
|
hdr.w1 = ntohl(buf[1]);
|
2009-06-08 04:57:53 +08:00
|
|
|
buf += 2;
|
|
|
|
len -= RFC2374_FRAG_HDR_SIZE;
|
|
|
|
if (lf == RFC2374_HDR_FIRSTFRAG) {
|
|
|
|
ether_type = fwnet_get_hdr_ether_type(&hdr);
|
2009-05-19 01:08:06 +08:00
|
|
|
fg_off = 0;
|
|
|
|
} else {
|
2009-06-08 04:57:53 +08:00
|
|
|
ether_type = 0;
|
|
|
|
fg_off = fwnet_get_hdr_fg_off(&hdr);
|
2009-05-19 01:08:06 +08:00
|
|
|
}
|
2009-06-08 04:57:53 +08:00
|
|
|
datagram_label = fwnet_get_hdr_dgl(&hdr);
|
firewire: net: fix fragmented datagram_size off-by-one
RFC 2734 defines the datagram_size field in fragment encapsulation
headers thus:
datagram_size: The encoded size of the entire IP datagram. The
value of datagram_size [...] SHALL be one less than the value of
Total Length in the datagram's IP header (see STD 5, RFC 791).
Accordingly, the eth1394 driver of Linux 2.6.36 and older set and got
this field with a -/+1 offset:
ether1394_tx() /* transmit */
ether1394_encapsulate_prep()
hdr->ff.dg_size = dg_size - 1;
ether1394_data_handler() /* receive */
if (hdr->common.lf == ETH1394_HDR_LF_FF)
dg_size = hdr->ff.dg_size + 1;
else
dg_size = hdr->sf.dg_size + 1;
Likewise, I observe OS X 10.4 and Windows XP Pro SP3 to transmit 1500
byte sized datagrams in fragments with datagram_size=1499 if link
fragmentation is required.
Only firewire-net sets and gets datagram_size without this offset. The
result is lacking interoperability of firewire-net with OS X, Windows
XP, and presumably Linux' eth1394. (I did not test with the latter.)
For example, FTP data transfers to a Linux firewire-net box with max_rec
smaller than the 1500 bytes MTU
- from OS X fail entirely,
- from Win XP start out with a bunch of fragmented datagrams which
time out, then continue with unfragmented datagrams because Win XP
temporarily reduces the MTU to 576 bytes.
So let's fix firewire-net's datagram_size accessors.
Note that firewire-net thereby loses interoperability with unpatched
firewire-net, but only if link fragmentation is employed. (This happens
with large broadcast datagrams, and with large datagrams on several
FireWire CardBus cards with smaller max_rec than equivalent PCI cards,
and it can be worked around by setting a small enough MTU.)
Cc: stable@vger.kernel.org
Signed-off-by: Stefan Richter <stefanr@s5r6.in-berlin.de>
2016-10-31 00:32:01 +08:00
|
|
|
dg_size = fwnet_get_hdr_dg_size(&hdr);
|
2009-06-08 04:57:53 +08:00
|
|
|
|
2016-10-30 03:28:18 +08:00
|
|
|
if (fg_off + len > dg_size)
|
|
|
|
return 0;
|
2009-06-08 04:57:53 +08:00
|
|
|
|
2009-06-14 17:45:27 +08:00
|
|
|
spin_lock_irqsave(&dev->lock, flags);
|
|
|
|
|
|
|
|
peer = fwnet_peer_find_by_node_id(dev, source_node_id, generation);
|
2010-08-17 05:45:54 +08:00
|
|
|
if (!peer) {
|
|
|
|
retval = -ENOENT;
|
|
|
|
goto fail;
|
|
|
|
}
|
2009-06-08 04:57:53 +08:00
|
|
|
|
|
|
|
pd = fwnet_pd_find(peer, datagram_label);
|
2009-05-19 01:08:06 +08:00
|
|
|
if (pd == NULL) {
|
2009-06-08 04:57:53 +08:00
|
|
|
while (peer->pdg_size >= FWNET_MAX_FRAGMENTS) {
|
2009-05-19 01:08:06 +08:00
|
|
|
/* remove the oldest */
|
2009-06-08 04:57:53 +08:00
|
|
|
fwnet_pd_delete(list_first_entry(&peer->pd_list,
|
|
|
|
struct fwnet_partial_datagram, pd_link));
|
|
|
|
peer->pdg_size--;
|
2009-05-19 01:08:06 +08:00
|
|
|
}
|
2009-06-08 04:57:53 +08:00
|
|
|
pd = fwnet_pd_new(net, peer, datagram_label,
|
|
|
|
dg_size, buf, fg_off, len);
|
|
|
|
if (pd == NULL) {
|
2009-05-19 01:08:06 +08:00
|
|
|
retval = -ENOMEM;
|
2010-08-17 05:45:54 +08:00
|
|
|
goto fail;
|
2009-05-19 01:08:06 +08:00
|
|
|
}
|
2009-06-08 04:57:53 +08:00
|
|
|
peer->pdg_size++;
|
2009-05-19 01:08:06 +08:00
|
|
|
} else {
|
2009-06-08 04:57:53 +08:00
|
|
|
if (fwnet_frag_overlap(pd, fg_off, len) ||
|
|
|
|
pd->datagram_size != dg_size) {
|
2009-05-19 01:08:06 +08:00
|
|
|
/*
|
|
|
|
* Differing datagram sizes or overlapping fragments,
|
2009-06-08 04:57:53 +08:00
|
|
|
* discard old datagram and start a new one.
|
2009-05-19 01:08:06 +08:00
|
|
|
*/
|
2009-06-08 04:57:53 +08:00
|
|
|
fwnet_pd_delete(pd);
|
|
|
|
pd = fwnet_pd_new(net, peer, datagram_label,
|
|
|
|
dg_size, buf, fg_off, len);
|
|
|
|
if (pd == NULL) {
|
|
|
|
peer->pdg_size--;
|
2010-08-17 05:45:54 +08:00
|
|
|
retval = -ENOMEM;
|
|
|
|
goto fail;
|
2009-05-19 01:08:06 +08:00
|
|
|
}
|
|
|
|
} else {
|
2009-06-08 04:57:53 +08:00
|
|
|
if (!fwnet_pd_update(peer, pd, buf, fg_off, len)) {
|
2009-05-19 01:08:06 +08:00
|
|
|
/*
|
|
|
|
* Couldn't save off fragment anyway
|
|
|
|
* so might as well obliterate the
|
|
|
|
* datagram now.
|
|
|
|
*/
|
2009-06-08 04:57:53 +08:00
|
|
|
fwnet_pd_delete(pd);
|
|
|
|
peer->pdg_size--;
|
2010-08-17 05:45:54 +08:00
|
|
|
retval = -ENOMEM;
|
|
|
|
goto fail;
|
2009-05-19 01:08:06 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
} /* new datagram or add to existing one */
|
|
|
|
|
2009-06-08 04:57:53 +08:00
|
|
|
if (lf == RFC2374_HDR_FIRSTFRAG)
|
2009-05-19 01:08:06 +08:00
|
|
|
pd->ether_type = ether_type;
|
2009-06-08 04:57:53 +08:00
|
|
|
|
|
|
|
if (fwnet_pd_is_complete(pd)) {
|
2009-05-19 01:08:06 +08:00
|
|
|
ether_type = pd->ether_type;
|
2009-06-08 04:57:53 +08:00
|
|
|
peer->pdg_size--;
|
2009-05-19 01:08:06 +08:00
|
|
|
skb = skb_get(pd->skb);
|
2009-06-08 04:57:53 +08:00
|
|
|
fwnet_pd_delete(pd);
|
|
|
|
|
2009-06-14 17:45:27 +08:00
|
|
|
spin_unlock_irqrestore(&dev->lock, flags);
|
2009-06-08 04:57:53 +08:00
|
|
|
|
|
|
|
return fwnet_finish_incoming_packet(net, skb, source_node_id,
|
|
|
|
false, ether_type);
|
2009-05-19 01:08:06 +08:00
|
|
|
}
|
|
|
|
/*
|
|
|
|
* Datagram is not complete, we're done for the
|
|
|
|
* moment.
|
|
|
|
*/
|
2010-11-14 21:35:40 +08:00
|
|
|
retval = 0;
|
2010-08-17 05:45:54 +08:00
|
|
|
fail:
|
2009-06-14 17:45:27 +08:00
|
|
|
spin_unlock_irqrestore(&dev->lock, flags);
|
2009-06-08 04:57:53 +08:00
|
|
|
|
2010-08-17 05:45:54 +08:00
|
|
|
return retval;
|
2009-05-19 01:08:06 +08:00
|
|
|
}
|
|
|
|
|
2009-06-08 04:57:53 +08:00
|
|
|
static void fwnet_receive_packet(struct fw_card *card, struct fw_request *r,
|
|
|
|
int tcode, int destination, int source, int generation,
|
2010-06-21 04:50:35 +08:00
|
|
|
unsigned long long offset, void *payload, size_t length,
|
|
|
|
void *callback_data)
|
2009-06-08 04:57:53 +08:00
|
|
|
{
|
2009-06-17 04:35:32 +08:00
|
|
|
struct fwnet_device *dev = callback_data;
|
|
|
|
int rcode;
|
2009-05-19 01:08:06 +08:00
|
|
|
|
2009-06-17 04:35:32 +08:00
|
|
|
if (destination == IEEE1394_ALL_NODES) {
|
|
|
|
kfree(r);
|
2009-06-08 04:57:53 +08:00
|
|
|
|
2009-05-19 01:08:06 +08:00
|
|
|
return;
|
|
|
|
}
|
2009-06-08 04:57:53 +08:00
|
|
|
|
2009-06-17 04:35:32 +08:00
|
|
|
if (offset != dev->handler.offset)
|
|
|
|
rcode = RCODE_ADDRESS_ERROR;
|
|
|
|
else if (tcode != TCODE_WRITE_BLOCK_REQUEST)
|
|
|
|
rcode = RCODE_TYPE_ERROR;
|
|
|
|
else if (fwnet_incoming_packet(dev, payload, length,
|
|
|
|
source, generation, false) != 0) {
|
2012-02-19 19:48:36 +08:00
|
|
|
dev_err(&dev->netdev->dev, "incoming packet failure\n");
|
2009-06-17 04:35:32 +08:00
|
|
|
rcode = RCODE_CONFLICT_ERROR;
|
|
|
|
} else
|
|
|
|
rcode = RCODE_COMPLETE;
|
2009-06-08 04:57:53 +08:00
|
|
|
|
2009-06-17 04:35:32 +08:00
|
|
|
fw_send_response(card, r, rcode);
|
2009-05-19 01:08:06 +08:00
|
|
|
}
|
|
|
|
|
2016-10-30 03:28:18 +08:00
|
|
|
static int gasp_source_id(__be32 *p)
|
|
|
|
{
|
|
|
|
return be32_to_cpu(p[0]) >> 16;
|
|
|
|
}
|
|
|
|
|
|
|
|
static u32 gasp_specifier_id(__be32 *p)
|
|
|
|
{
|
|
|
|
return (be32_to_cpu(p[0]) & 0xffff) << 8 |
|
|
|
|
(be32_to_cpu(p[1]) & 0xff000000) >> 24;
|
|
|
|
}
|
|
|
|
|
|
|
|
static u32 gasp_version(__be32 *p)
|
|
|
|
{
|
|
|
|
return be32_to_cpu(p[1]) & 0xffffff;
|
|
|
|
}
|
|
|
|
|
2009-06-08 04:57:53 +08:00
|
|
|
static void fwnet_receive_broadcast(struct fw_iso_context *context,
|
|
|
|
u32 cycle, size_t header_length, void *header, void *data)
|
|
|
|
{
|
|
|
|
struct fwnet_device *dev;
|
2009-05-19 01:08:06 +08:00
|
|
|
struct fw_iso_packet packet;
|
2009-06-08 04:57:53 +08:00
|
|
|
__be16 *hdr_ptr;
|
|
|
|
__be32 *buf_ptr;
|
2009-05-19 01:08:06 +08:00
|
|
|
int retval;
|
|
|
|
u32 length;
|
|
|
|
unsigned long offset;
|
|
|
|
unsigned long flags;
|
|
|
|
|
2009-06-08 04:57:53 +08:00
|
|
|
dev = data;
|
2009-05-19 01:08:06 +08:00
|
|
|
hdr_ptr = header;
|
2009-06-08 04:57:53 +08:00
|
|
|
length = be16_to_cpup(hdr_ptr);
|
|
|
|
|
|
|
|
spin_lock_irqsave(&dev->lock, flags);
|
|
|
|
|
|
|
|
offset = dev->rcv_buffer_size * dev->broadcast_rcv_next_ptr;
|
|
|
|
buf_ptr = dev->broadcast_rcv_buffer_ptrs[dev->broadcast_rcv_next_ptr++];
|
|
|
|
if (dev->broadcast_rcv_next_ptr == dev->num_broadcast_rcv_ptrs)
|
|
|
|
dev->broadcast_rcv_next_ptr = 0;
|
|
|
|
|
|
|
|
spin_unlock_irqrestore(&dev->lock, flags);
|
2009-05-19 01:08:06 +08:00
|
|
|
|
2016-10-30 03:28:18 +08:00
|
|
|
if (length > IEEE1394_GASP_HDR_SIZE &&
|
|
|
|
gasp_specifier_id(buf_ptr) == IANA_SPECIFIER_ID &&
|
|
|
|
(gasp_version(buf_ptr) == RFC2734_SW_VERSION
|
2013-03-25 16:26:24 +08:00
|
|
|
#if IS_ENABLED(CONFIG_IPV6)
|
2016-10-30 03:28:18 +08:00
|
|
|
|| gasp_version(buf_ptr) == RFC3146_SW_VERSION
|
2013-03-25 16:26:24 +08:00
|
|
|
#endif
|
2016-10-30 03:28:18 +08:00
|
|
|
))
|
|
|
|
fwnet_incoming_packet(dev, buf_ptr + 2,
|
|
|
|
length - IEEE1394_GASP_HDR_SIZE,
|
|
|
|
gasp_source_id(buf_ptr),
|
2012-11-29 03:04:32 +08:00
|
|
|
context->card->generation, true);
|
2009-06-08 04:57:53 +08:00
|
|
|
|
|
|
|
packet.payload_length = dev->rcv_buffer_size;
|
2009-05-19 01:08:06 +08:00
|
|
|
packet.interrupt = 1;
|
|
|
|
packet.skip = 0;
|
|
|
|
packet.tag = 3;
|
|
|
|
packet.sy = 0;
|
2009-06-08 04:57:53 +08:00
|
|
|
packet.header_length = IEEE1394_GASP_HDR_SIZE;
|
|
|
|
|
|
|
|
spin_lock_irqsave(&dev->lock, flags);
|
2009-05-19 01:08:06 +08:00
|
|
|
|
2009-06-08 04:57:53 +08:00
|
|
|
retval = fw_iso_context_queue(dev->broadcast_rcv_context, &packet,
|
|
|
|
&dev->broadcast_rcv_buffer, offset);
|
|
|
|
|
|
|
|
spin_unlock_irqrestore(&dev->lock, flags);
|
|
|
|
|
2011-05-02 15:33:56 +08:00
|
|
|
if (retval >= 0)
|
|
|
|
fw_iso_context_queue_flush(dev->broadcast_rcv_context);
|
|
|
|
else
|
2012-02-19 19:48:36 +08:00
|
|
|
dev_err(&dev->netdev->dev, "requeue failed\n");
|
2009-05-19 01:08:06 +08:00
|
|
|
}
|
|
|
|
|
2009-06-08 04:57:53 +08:00
|
|
|
static struct kmem_cache *fwnet_packet_task_cache;
|
|
|
|
|
2010-01-19 05:36:49 +08:00
|
|
|
static void fwnet_free_ptask(struct fwnet_packet_task *ptask)
|
|
|
|
{
|
|
|
|
dev_kfree_skb_any(ptask->skb);
|
|
|
|
kmem_cache_free(fwnet_packet_task_cache, ptask);
|
|
|
|
}
|
|
|
|
|
2010-11-14 21:35:40 +08:00
|
|
|
/* Caller must hold dev->lock. */
|
|
|
|
static void dec_queued_datagrams(struct fwnet_device *dev)
|
|
|
|
{
|
|
|
|
if (--dev->queued_datagrams == FWNET_MIN_QUEUED_DATAGRAMS)
|
|
|
|
netif_wake_queue(dev->netdev);
|
|
|
|
}
|
|
|
|
|
2009-06-08 04:57:53 +08:00
|
|
|
static int fwnet_send_packet(struct fwnet_packet_task *ptask);
|
|
|
|
|
|
|
|
static void fwnet_transmit_packet_done(struct fwnet_packet_task *ptask)
|
|
|
|
{
|
2010-01-19 05:36:49 +08:00
|
|
|
struct fwnet_device *dev = ptask->dev;
|
2010-11-06 19:36:13 +08:00
|
|
|
struct sk_buff *skb = ptask->skb;
|
2009-05-19 01:08:06 +08:00
|
|
|
unsigned long flags;
|
2010-01-19 05:36:49 +08:00
|
|
|
bool free;
|
2009-06-08 04:57:53 +08:00
|
|
|
|
|
|
|
spin_lock_irqsave(&dev->lock, flags);
|
|
|
|
|
2010-01-19 05:36:49 +08:00
|
|
|
ptask->outstanding_pkts--;
|
|
|
|
|
|
|
|
/* Check whether we or the networking TX soft-IRQ is last user. */
|
2010-11-07 06:18:23 +08:00
|
|
|
free = (ptask->outstanding_pkts == 0 && ptask->enqueued);
|
2010-11-06 23:57:28 +08:00
|
|
|
if (free)
|
2010-11-14 21:35:40 +08:00
|
|
|
dec_queued_datagrams(dev);
|
2010-01-19 05:36:49 +08:00
|
|
|
|
2010-11-06 19:36:13 +08:00
|
|
|
if (ptask->outstanding_pkts == 0) {
|
|
|
|
dev->netdev->stats.tx_packets++;
|
|
|
|
dev->netdev->stats.tx_bytes += skb->len;
|
|
|
|
}
|
2010-01-19 05:36:49 +08:00
|
|
|
|
|
|
|
spin_unlock_irqrestore(&dev->lock, flags);
|
2009-06-08 04:57:53 +08:00
|
|
|
|
|
|
|
if (ptask->outstanding_pkts > 0) {
|
2009-05-19 01:08:06 +08:00
|
|
|
u16 dg_size;
|
|
|
|
u16 fg_off;
|
|
|
|
u16 datagram_label;
|
|
|
|
u16 lf;
|
|
|
|
|
|
|
|
/* Update the ptask to point to the next fragment and send it */
|
2009-06-08 04:57:53 +08:00
|
|
|
lf = fwnet_get_hdr_lf(&ptask->hdr);
|
2009-05-19 01:08:06 +08:00
|
|
|
switch (lf) {
|
2009-06-08 04:57:53 +08:00
|
|
|
case RFC2374_HDR_LASTFRAG:
|
|
|
|
case RFC2374_HDR_UNFRAG:
|
2009-05-19 01:08:06 +08:00
|
|
|
default:
|
2012-02-19 19:48:36 +08:00
|
|
|
dev_err(&dev->netdev->dev,
|
|
|
|
"outstanding packet %x lf %x, header %x,%x\n",
|
|
|
|
ptask->outstanding_pkts, lf, ptask->hdr.w0,
|
|
|
|
ptask->hdr.w1);
|
2009-05-19 01:08:06 +08:00
|
|
|
BUG();
|
|
|
|
|
2009-06-08 04:57:53 +08:00
|
|
|
case RFC2374_HDR_FIRSTFRAG:
|
2009-05-19 01:08:06 +08:00
|
|
|
/* Set frag type here for future interior fragments */
|
2009-06-08 04:57:53 +08:00
|
|
|
dg_size = fwnet_get_hdr_dg_size(&ptask->hdr);
|
|
|
|
fg_off = ptask->max_payload - RFC2374_FRAG_HDR_SIZE;
|
|
|
|
datagram_label = fwnet_get_hdr_dgl(&ptask->hdr);
|
2009-05-19 01:08:06 +08:00
|
|
|
break;
|
|
|
|
|
2009-06-08 04:57:53 +08:00
|
|
|
case RFC2374_HDR_INTFRAG:
|
|
|
|
dg_size = fwnet_get_hdr_dg_size(&ptask->hdr);
|
|
|
|
fg_off = fwnet_get_hdr_fg_off(&ptask->hdr)
|
|
|
|
+ ptask->max_payload - RFC2374_FRAG_HDR_SIZE;
|
|
|
|
datagram_label = fwnet_get_hdr_dgl(&ptask->hdr);
|
2009-05-19 01:08:06 +08:00
|
|
|
break;
|
|
|
|
}
|
2010-11-06 19:36:13 +08:00
|
|
|
|
2012-11-29 03:04:32 +08:00
|
|
|
if (ptask->dest_node == IEEE1394_ALL_NODES) {
|
|
|
|
skb_pull(skb,
|
|
|
|
ptask->max_payload + IEEE1394_GASP_HDR_SIZE);
|
|
|
|
} else {
|
|
|
|
skb_pull(skb, ptask->max_payload);
|
|
|
|
}
|
2009-06-08 04:57:53 +08:00
|
|
|
if (ptask->outstanding_pkts > 1) {
|
|
|
|
fwnet_make_sf_hdr(&ptask->hdr, RFC2374_HDR_INTFRAG,
|
|
|
|
dg_size, fg_off, datagram_label);
|
2009-05-19 01:08:06 +08:00
|
|
|
} else {
|
2009-06-08 04:57:53 +08:00
|
|
|
fwnet_make_sf_hdr(&ptask->hdr, RFC2374_HDR_LASTFRAG,
|
|
|
|
dg_size, fg_off, datagram_label);
|
|
|
|
ptask->max_payload = skb->len + RFC2374_FRAG_HDR_SIZE;
|
2009-05-19 01:08:06 +08:00
|
|
|
}
|
2009-06-08 04:57:53 +08:00
|
|
|
fwnet_send_packet(ptask);
|
2009-05-19 01:08:06 +08:00
|
|
|
}
|
2010-01-19 05:36:49 +08:00
|
|
|
|
|
|
|
if (free)
|
|
|
|
fwnet_free_ptask(ptask);
|
2009-05-19 01:08:06 +08:00
|
|
|
}
|
|
|
|
|
2010-11-06 23:57:28 +08:00
|
|
|
static void fwnet_transmit_packet_failed(struct fwnet_packet_task *ptask)
|
|
|
|
{
|
|
|
|
struct fwnet_device *dev = ptask->dev;
|
|
|
|
unsigned long flags;
|
|
|
|
bool free;
|
|
|
|
|
|
|
|
spin_lock_irqsave(&dev->lock, flags);
|
|
|
|
|
|
|
|
/* One fragment failed; don't try to send remaining fragments. */
|
|
|
|
ptask->outstanding_pkts = 0;
|
|
|
|
|
|
|
|
/* Check whether we or the networking TX soft-IRQ is last user. */
|
2010-11-07 06:18:23 +08:00
|
|
|
free = ptask->enqueued;
|
2010-11-06 23:57:28 +08:00
|
|
|
if (free)
|
2010-11-14 21:35:40 +08:00
|
|
|
dec_queued_datagrams(dev);
|
2010-11-06 23:57:28 +08:00
|
|
|
|
|
|
|
dev->netdev->stats.tx_dropped++;
|
|
|
|
dev->netdev->stats.tx_errors++;
|
|
|
|
|
|
|
|
spin_unlock_irqrestore(&dev->lock, flags);
|
|
|
|
|
|
|
|
if (free)
|
|
|
|
fwnet_free_ptask(ptask);
|
|
|
|
}
|
|
|
|
|
2009-06-08 04:57:53 +08:00
|
|
|
static void fwnet_write_complete(struct fw_card *card, int rcode,
|
|
|
|
void *payload, size_t length, void *data)
|
|
|
|
{
|
2010-11-29 10:09:53 +08:00
|
|
|
struct fwnet_packet_task *ptask = data;
|
|
|
|
static unsigned long j;
|
|
|
|
static int last_rcode, errors_skipped;
|
2009-05-19 01:08:06 +08:00
|
|
|
|
2010-11-06 23:57:28 +08:00
|
|
|
if (rcode == RCODE_COMPLETE) {
|
2009-06-08 04:57:53 +08:00
|
|
|
fwnet_transmit_packet_done(ptask);
|
2010-11-06 23:57:28 +08:00
|
|
|
} else {
|
2010-11-29 10:09:53 +08:00
|
|
|
if (printk_timed_ratelimit(&j, 1000) || rcode != last_rcode) {
|
2012-02-19 19:48:36 +08:00
|
|
|
dev_err(&ptask->dev->netdev->dev,
|
|
|
|
"fwnet_write_complete failed: %x (skipped %d)\n",
|
|
|
|
rcode, errors_skipped);
|
2010-11-29 10:09:53 +08:00
|
|
|
|
|
|
|
errors_skipped = 0;
|
|
|
|
last_rcode = rcode;
|
2014-02-19 05:25:15 +08:00
|
|
|
} else {
|
2010-11-29 10:09:53 +08:00
|
|
|
errors_skipped++;
|
2014-02-19 05:25:15 +08:00
|
|
|
}
|
|
|
|
fwnet_transmit_packet_failed(ptask);
|
2010-11-06 23:57:28 +08:00
|
|
|
}
|
2009-05-19 01:08:06 +08:00
|
|
|
}
|
|
|
|
|
2009-06-08 04:57:53 +08:00
|
|
|
static int fwnet_send_packet(struct fwnet_packet_task *ptask)
|
|
|
|
{
|
|
|
|
struct fwnet_device *dev;
|
2009-05-19 01:08:06 +08:00
|
|
|
unsigned tx_len;
|
2009-06-08 04:57:53 +08:00
|
|
|
struct rfc2734_header *bufhdr;
|
2009-05-19 01:08:06 +08:00
|
|
|
unsigned long flags;
|
2010-01-19 05:36:49 +08:00
|
|
|
bool free;
|
2009-05-19 01:08:06 +08:00
|
|
|
|
2009-06-08 04:57:53 +08:00
|
|
|
dev = ptask->dev;
|
2009-05-19 01:08:06 +08:00
|
|
|
tx_len = ptask->max_payload;
|
2009-06-08 04:57:53 +08:00
|
|
|
switch (fwnet_get_hdr_lf(&ptask->hdr)) {
|
|
|
|
case RFC2374_HDR_UNFRAG:
|
networking: make skb_push & __skb_push return void pointers
It seems like a historic accident that these return unsigned char *,
and in many places that means casts are required, more often than not.
Make these functions return void * and remove all the casts across
the tree, adding a (u8 *) cast only where the unsigned char pointer
was used directly, all done with the following spatch:
@@
expression SKB, LEN;
typedef u8;
identifier fn = { skb_push, __skb_push, skb_push_rcsum };
@@
- *(fn(SKB, LEN))
+ *(u8 *)fn(SKB, LEN)
@@
expression E, SKB, LEN;
identifier fn = { skb_push, __skb_push, skb_push_rcsum };
type T;
@@
- E = ((T *)(fn(SKB, LEN)))
+ E = fn(SKB, LEN)
@@
expression SKB, LEN;
identifier fn = { skb_push, __skb_push, skb_push_rcsum };
@@
- fn(SKB, LEN)[0]
+ *(u8 *)fn(SKB, LEN)
Note that the last part there converts from push(...)[0] to the
more idiomatic *(u8 *)push(...).
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2017-06-16 20:29:23 +08:00
|
|
|
bufhdr = skb_push(ptask->skb, RFC2374_UNFRAG_HDR_SIZE);
|
2009-06-08 04:57:53 +08:00
|
|
|
put_unaligned_be32(ptask->hdr.w0, &bufhdr->w0);
|
2009-05-19 01:08:06 +08:00
|
|
|
break;
|
|
|
|
|
2009-06-08 04:57:53 +08:00
|
|
|
case RFC2374_HDR_FIRSTFRAG:
|
|
|
|
case RFC2374_HDR_INTFRAG:
|
|
|
|
case RFC2374_HDR_LASTFRAG:
|
networking: make skb_push & __skb_push return void pointers
It seems like a historic accident that these return unsigned char *,
and in many places that means casts are required, more often than not.
Make these functions return void * and remove all the casts across
the tree, adding a (u8 *) cast only where the unsigned char pointer
was used directly, all done with the following spatch:
@@
expression SKB, LEN;
typedef u8;
identifier fn = { skb_push, __skb_push, skb_push_rcsum };
@@
- *(fn(SKB, LEN))
+ *(u8 *)fn(SKB, LEN)
@@
expression E, SKB, LEN;
identifier fn = { skb_push, __skb_push, skb_push_rcsum };
type T;
@@
- E = ((T *)(fn(SKB, LEN)))
+ E = fn(SKB, LEN)
@@
expression SKB, LEN;
identifier fn = { skb_push, __skb_push, skb_push_rcsum };
@@
- fn(SKB, LEN)[0]
+ *(u8 *)fn(SKB, LEN)
Note that the last part there converts from push(...)[0] to the
more idiomatic *(u8 *)push(...).
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2017-06-16 20:29:23 +08:00
|
|
|
bufhdr = skb_push(ptask->skb, RFC2374_FRAG_HDR_SIZE);
|
2009-06-08 04:57:53 +08:00
|
|
|
put_unaligned_be32(ptask->hdr.w0, &bufhdr->w0);
|
|
|
|
put_unaligned_be32(ptask->hdr.w1, &bufhdr->w1);
|
2009-05-19 01:08:06 +08:00
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
BUG();
|
|
|
|
}
|
2009-06-08 04:57:53 +08:00
|
|
|
if (ptask->dest_node == IEEE1394_ALL_NODES) {
|
|
|
|
u8 *p;
|
2009-05-19 01:08:06 +08:00
|
|
|
int generation;
|
2009-06-08 04:57:53 +08:00
|
|
|
int node_id;
|
2013-03-25 16:26:24 +08:00
|
|
|
unsigned int sw_version;
|
2009-05-19 01:08:06 +08:00
|
|
|
|
|
|
|
/* ptask->generation may not have been set yet */
|
2009-06-08 04:57:53 +08:00
|
|
|
generation = dev->card->generation;
|
2009-05-19 01:08:06 +08:00
|
|
|
smp_rmb();
|
2009-06-08 04:57:53 +08:00
|
|
|
node_id = dev->card->node_id;
|
|
|
|
|
2013-03-25 16:26:24 +08:00
|
|
|
switch (ptask->skb->protocol) {
|
|
|
|
default:
|
|
|
|
sw_version = RFC2734_SW_VERSION;
|
|
|
|
break;
|
|
|
|
#if IS_ENABLED(CONFIG_IPV6)
|
|
|
|
case htons(ETH_P_IPV6):
|
|
|
|
sw_version = RFC3146_SW_VERSION;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2012-11-29 03:04:32 +08:00
|
|
|
p = skb_push(ptask->skb, IEEE1394_GASP_HDR_SIZE);
|
2009-06-08 04:57:53 +08:00
|
|
|
put_unaligned_be32(node_id << 16 | IANA_SPECIFIER_ID >> 8, p);
|
|
|
|
put_unaligned_be32((IANA_SPECIFIER_ID & 0xff) << 24
|
2013-03-25 16:26:24 +08:00
|
|
|
| sw_version, &p[4]);
|
2009-06-08 04:57:53 +08:00
|
|
|
|
|
|
|
/* We should not transmit if broadcast_channel.valid == 0. */
|
|
|
|
fw_send_request(dev->card, &ptask->transaction,
|
|
|
|
TCODE_STREAM_DATA,
|
|
|
|
fw_stream_packet_destination_id(3,
|
|
|
|
IEEE1394_BROADCAST_CHANNEL, 0),
|
|
|
|
generation, SCODE_100, 0ULL, ptask->skb->data,
|
|
|
|
tx_len + 8, fwnet_write_complete, ptask);
|
|
|
|
|
|
|
|
spin_lock_irqsave(&dev->lock, flags);
|
2010-01-19 05:36:49 +08:00
|
|
|
|
|
|
|
/* If the AT tasklet already ran, we may be last user. */
|
2010-11-07 06:18:23 +08:00
|
|
|
free = (ptask->outstanding_pkts == 0 && !ptask->enqueued);
|
2010-01-19 05:36:49 +08:00
|
|
|
if (!free)
|
2010-11-07 06:18:23 +08:00
|
|
|
ptask->enqueued = true;
|
|
|
|
else
|
2010-11-14 21:35:40 +08:00
|
|
|
dec_queued_datagrams(dev);
|
2010-01-19 05:36:49 +08:00
|
|
|
|
2009-06-08 04:57:53 +08:00
|
|
|
spin_unlock_irqrestore(&dev->lock, flags);
|
|
|
|
|
2010-01-19 05:36:49 +08:00
|
|
|
goto out;
|
2009-05-19 01:08:06 +08:00
|
|
|
}
|
2009-06-08 04:57:53 +08:00
|
|
|
|
|
|
|
fw_send_request(dev->card, &ptask->transaction,
|
|
|
|
TCODE_WRITE_BLOCK_REQUEST, ptask->dest_node,
|
|
|
|
ptask->generation, ptask->speed, ptask->fifo_addr,
|
|
|
|
ptask->skb->data, tx_len, fwnet_write_complete, ptask);
|
|
|
|
|
|
|
|
spin_lock_irqsave(&dev->lock, flags);
|
2010-01-19 05:36:49 +08:00
|
|
|
|
|
|
|
/* If the AT tasklet already ran, we may be last user. */
|
2010-11-07 06:18:23 +08:00
|
|
|
free = (ptask->outstanding_pkts == 0 && !ptask->enqueued);
|
2010-01-19 05:36:49 +08:00
|
|
|
if (!free)
|
2010-11-07 06:18:23 +08:00
|
|
|
ptask->enqueued = true;
|
|
|
|
else
|
2010-11-14 21:35:40 +08:00
|
|
|
dec_queued_datagrams(dev);
|
2010-01-19 05:36:49 +08:00
|
|
|
|
2009-06-08 04:57:53 +08:00
|
|
|
spin_unlock_irqrestore(&dev->lock, flags);
|
|
|
|
|
2016-05-03 22:33:13 +08:00
|
|
|
netif_trans_update(dev->netdev);
|
2010-01-19 05:36:49 +08:00
|
|
|
out:
|
|
|
|
if (free)
|
|
|
|
fwnet_free_ptask(ptask);
|
2009-06-08 04:57:53 +08:00
|
|
|
|
2009-05-19 01:08:06 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2013-03-08 09:42:26 +08:00
|
|
|
static void fwnet_fifo_stop(struct fwnet_device *dev)
|
|
|
|
{
|
|
|
|
if (dev->local_fifo == FWNET_NO_FIFO_ADDR)
|
|
|
|
return;
|
|
|
|
|
|
|
|
fw_core_remove_address_handler(&dev->handler);
|
|
|
|
dev->local_fifo = FWNET_NO_FIFO_ADDR;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int fwnet_fifo_start(struct fwnet_device *dev)
|
|
|
|
{
|
|
|
|
int retval;
|
|
|
|
|
|
|
|
if (dev->local_fifo != FWNET_NO_FIFO_ADDR)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
dev->handler.length = 4096;
|
|
|
|
dev->handler.address_callback = fwnet_receive_packet;
|
|
|
|
dev->handler.callback_data = dev;
|
|
|
|
|
|
|
|
retval = fw_core_add_address_handler(&dev->handler,
|
|
|
|
&fw_high_memory_region);
|
|
|
|
if (retval < 0)
|
|
|
|
return retval;
|
|
|
|
|
|
|
|
dev->local_fifo = dev->handler.offset;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2013-03-08 09:45:50 +08:00
|
|
|
static void __fwnet_broadcast_stop(struct fwnet_device *dev)
|
|
|
|
{
|
|
|
|
unsigned u;
|
|
|
|
|
|
|
|
if (dev->broadcast_state != FWNET_BROADCAST_ERROR) {
|
|
|
|
for (u = 0; u < FWNET_ISO_PAGE_COUNT; u++)
|
|
|
|
kunmap(dev->broadcast_rcv_buffer.pages[u]);
|
|
|
|
fw_iso_buffer_destroy(&dev->broadcast_rcv_buffer, dev->card);
|
|
|
|
}
|
|
|
|
if (dev->broadcast_rcv_context) {
|
|
|
|
fw_iso_context_destroy(dev->broadcast_rcv_context);
|
|
|
|
dev->broadcast_rcv_context = NULL;
|
|
|
|
}
|
|
|
|
kfree(dev->broadcast_rcv_buffer_ptrs);
|
|
|
|
dev->broadcast_rcv_buffer_ptrs = NULL;
|
|
|
|
dev->broadcast_state = FWNET_BROADCAST_ERROR;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void fwnet_broadcast_stop(struct fwnet_device *dev)
|
|
|
|
{
|
|
|
|
if (dev->broadcast_state == FWNET_BROADCAST_ERROR)
|
|
|
|
return;
|
|
|
|
fw_iso_context_stop(dev->broadcast_rcv_context);
|
|
|
|
__fwnet_broadcast_stop(dev);
|
|
|
|
}
|
|
|
|
|
2009-06-08 04:57:53 +08:00
|
|
|
static int fwnet_broadcast_start(struct fwnet_device *dev)
|
|
|
|
{
|
2009-05-19 01:08:06 +08:00
|
|
|
struct fw_iso_context *context;
|
|
|
|
int retval;
|
|
|
|
unsigned num_packets;
|
|
|
|
unsigned max_receive;
|
|
|
|
struct fw_iso_packet packet;
|
|
|
|
unsigned long offset;
|
2013-03-08 09:43:25 +08:00
|
|
|
void **ptrptr;
|
2009-05-19 01:08:06 +08:00
|
|
|
unsigned u;
|
|
|
|
|
2013-03-08 09:43:14 +08:00
|
|
|
if (dev->broadcast_state != FWNET_BROADCAST_ERROR)
|
|
|
|
return 0;
|
|
|
|
|
2009-06-08 04:57:53 +08:00
|
|
|
max_receive = 1U << (dev->card->max_receive + 1);
|
|
|
|
num_packets = (FWNET_ISO_PAGE_COUNT * PAGE_SIZE) / max_receive;
|
|
|
|
|
treewide: kmalloc() -> kmalloc_array()
The kmalloc() function has a 2-factor argument form, kmalloc_array(). This
patch replaces cases of:
kmalloc(a * b, gfp)
with:
kmalloc_array(a * b, gfp)
as well as handling cases of:
kmalloc(a * b * c, gfp)
with:
kmalloc(array3_size(a, b, c), gfp)
as it's slightly less ugly than:
kmalloc_array(array_size(a, b), c, gfp)
This does, however, attempt to ignore constant size factors like:
kmalloc(4 * 1024, gfp)
though any constants defined via macros get caught up in the conversion.
Any factors with a sizeof() of "unsigned char", "char", and "u8" were
dropped, since they're redundant.
The tools/ directory was manually excluded, since it has its own
implementation of kmalloc().
The Coccinelle script used for this was:
// Fix redundant parens around sizeof().
@@
type TYPE;
expression THING, E;
@@
(
kmalloc(
- (sizeof(TYPE)) * E
+ sizeof(TYPE) * E
, ...)
|
kmalloc(
- (sizeof(THING)) * E
+ sizeof(THING) * E
, ...)
)
// Drop single-byte sizes and redundant parens.
@@
expression COUNT;
typedef u8;
typedef __u8;
@@
(
kmalloc(
- sizeof(u8) * (COUNT)
+ COUNT
, ...)
|
kmalloc(
- sizeof(__u8) * (COUNT)
+ COUNT
, ...)
|
kmalloc(
- sizeof(char) * (COUNT)
+ COUNT
, ...)
|
kmalloc(
- sizeof(unsigned char) * (COUNT)
+ COUNT
, ...)
|
kmalloc(
- sizeof(u8) * COUNT
+ COUNT
, ...)
|
kmalloc(
- sizeof(__u8) * COUNT
+ COUNT
, ...)
|
kmalloc(
- sizeof(char) * COUNT
+ COUNT
, ...)
|
kmalloc(
- sizeof(unsigned char) * COUNT
+ COUNT
, ...)
)
// 2-factor product with sizeof(type/expression) and identifier or constant.
@@
type TYPE;
expression THING;
identifier COUNT_ID;
constant COUNT_CONST;
@@
(
- kmalloc
+ kmalloc_array
(
- sizeof(TYPE) * (COUNT_ID)
+ COUNT_ID, sizeof(TYPE)
, ...)
|
- kmalloc
+ kmalloc_array
(
- sizeof(TYPE) * COUNT_ID
+ COUNT_ID, sizeof(TYPE)
, ...)
|
- kmalloc
+ kmalloc_array
(
- sizeof(TYPE) * (COUNT_CONST)
+ COUNT_CONST, sizeof(TYPE)
, ...)
|
- kmalloc
+ kmalloc_array
(
- sizeof(TYPE) * COUNT_CONST
+ COUNT_CONST, sizeof(TYPE)
, ...)
|
- kmalloc
+ kmalloc_array
(
- sizeof(THING) * (COUNT_ID)
+ COUNT_ID, sizeof(THING)
, ...)
|
- kmalloc
+ kmalloc_array
(
- sizeof(THING) * COUNT_ID
+ COUNT_ID, sizeof(THING)
, ...)
|
- kmalloc
+ kmalloc_array
(
- sizeof(THING) * (COUNT_CONST)
+ COUNT_CONST, sizeof(THING)
, ...)
|
- kmalloc
+ kmalloc_array
(
- sizeof(THING) * COUNT_CONST
+ COUNT_CONST, sizeof(THING)
, ...)
)
// 2-factor product, only identifiers.
@@
identifier SIZE, COUNT;
@@
- kmalloc
+ kmalloc_array
(
- SIZE * COUNT
+ COUNT, SIZE
, ...)
// 3-factor product with 1 sizeof(type) or sizeof(expression), with
// redundant parens removed.
@@
expression THING;
identifier STRIDE, COUNT;
type TYPE;
@@
(
kmalloc(
- sizeof(TYPE) * (COUNT) * (STRIDE)
+ array3_size(COUNT, STRIDE, sizeof(TYPE))
, ...)
|
kmalloc(
- sizeof(TYPE) * (COUNT) * STRIDE
+ array3_size(COUNT, STRIDE, sizeof(TYPE))
, ...)
|
kmalloc(
- sizeof(TYPE) * COUNT * (STRIDE)
+ array3_size(COUNT, STRIDE, sizeof(TYPE))
, ...)
|
kmalloc(
- sizeof(TYPE) * COUNT * STRIDE
+ array3_size(COUNT, STRIDE, sizeof(TYPE))
, ...)
|
kmalloc(
- sizeof(THING) * (COUNT) * (STRIDE)
+ array3_size(COUNT, STRIDE, sizeof(THING))
, ...)
|
kmalloc(
- sizeof(THING) * (COUNT) * STRIDE
+ array3_size(COUNT, STRIDE, sizeof(THING))
, ...)
|
kmalloc(
- sizeof(THING) * COUNT * (STRIDE)
+ array3_size(COUNT, STRIDE, sizeof(THING))
, ...)
|
kmalloc(
- sizeof(THING) * COUNT * STRIDE
+ array3_size(COUNT, STRIDE, sizeof(THING))
, ...)
)
// 3-factor product with 2 sizeof(variable), with redundant parens removed.
@@
expression THING1, THING2;
identifier COUNT;
type TYPE1, TYPE2;
@@
(
kmalloc(
- sizeof(TYPE1) * sizeof(TYPE2) * COUNT
+ array3_size(COUNT, sizeof(TYPE1), sizeof(TYPE2))
, ...)
|
kmalloc(
- sizeof(TYPE1) * sizeof(THING2) * (COUNT)
+ array3_size(COUNT, sizeof(TYPE1), sizeof(TYPE2))
, ...)
|
kmalloc(
- sizeof(THING1) * sizeof(THING2) * COUNT
+ array3_size(COUNT, sizeof(THING1), sizeof(THING2))
, ...)
|
kmalloc(
- sizeof(THING1) * sizeof(THING2) * (COUNT)
+ array3_size(COUNT, sizeof(THING1), sizeof(THING2))
, ...)
|
kmalloc(
- sizeof(TYPE1) * sizeof(THING2) * COUNT
+ array3_size(COUNT, sizeof(TYPE1), sizeof(THING2))
, ...)
|
kmalloc(
- sizeof(TYPE1) * sizeof(THING2) * (COUNT)
+ array3_size(COUNT, sizeof(TYPE1), sizeof(THING2))
, ...)
)
// 3-factor product, only identifiers, with redundant parens removed.
@@
identifier STRIDE, SIZE, COUNT;
@@
(
kmalloc(
- (COUNT) * STRIDE * SIZE
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
kmalloc(
- COUNT * (STRIDE) * SIZE
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
kmalloc(
- COUNT * STRIDE * (SIZE)
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
kmalloc(
- (COUNT) * (STRIDE) * SIZE
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
kmalloc(
- COUNT * (STRIDE) * (SIZE)
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
kmalloc(
- (COUNT) * STRIDE * (SIZE)
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
kmalloc(
- (COUNT) * (STRIDE) * (SIZE)
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
kmalloc(
- COUNT * STRIDE * SIZE
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
)
// Any remaining multi-factor products, first at least 3-factor products,
// when they're not all constants...
@@
expression E1, E2, E3;
constant C1, C2, C3;
@@
(
kmalloc(C1 * C2 * C3, ...)
|
kmalloc(
- (E1) * E2 * E3
+ array3_size(E1, E2, E3)
, ...)
|
kmalloc(
- (E1) * (E2) * E3
+ array3_size(E1, E2, E3)
, ...)
|
kmalloc(
- (E1) * (E2) * (E3)
+ array3_size(E1, E2, E3)
, ...)
|
kmalloc(
- E1 * E2 * E3
+ array3_size(E1, E2, E3)
, ...)
)
// And then all remaining 2 factors products when they're not all constants,
// keeping sizeof() as the second factor argument.
@@
expression THING, E1, E2;
type TYPE;
constant C1, C2, C3;
@@
(
kmalloc(sizeof(THING) * C2, ...)
|
kmalloc(sizeof(TYPE) * C2, ...)
|
kmalloc(C1 * C2 * C3, ...)
|
kmalloc(C1 * C2, ...)
|
- kmalloc
+ kmalloc_array
(
- sizeof(TYPE) * (E2)
+ E2, sizeof(TYPE)
, ...)
|
- kmalloc
+ kmalloc_array
(
- sizeof(TYPE) * E2
+ E2, sizeof(TYPE)
, ...)
|
- kmalloc
+ kmalloc_array
(
- sizeof(THING) * (E2)
+ E2, sizeof(THING)
, ...)
|
- kmalloc
+ kmalloc_array
(
- sizeof(THING) * E2
+ E2, sizeof(THING)
, ...)
|
- kmalloc
+ kmalloc_array
(
- (E1) * E2
+ E1, E2
, ...)
|
- kmalloc
+ kmalloc_array
(
- (E1) * (E2)
+ E1, E2
, ...)
|
- kmalloc
+ kmalloc_array
(
- E1 * E2
+ E1, E2
, ...)
)
Signed-off-by: Kees Cook <keescook@chromium.org>
2018-06-13 04:55:00 +08:00
|
|
|
ptrptr = kmalloc_array(num_packets, sizeof(void *), GFP_KERNEL);
|
2013-03-08 09:43:55 +08:00
|
|
|
if (!ptrptr) {
|
|
|
|
retval = -ENOMEM;
|
2013-03-08 09:45:50 +08:00
|
|
|
goto failed;
|
2013-03-08 09:43:55 +08:00
|
|
|
}
|
|
|
|
dev->broadcast_rcv_buffer_ptrs = ptrptr;
|
|
|
|
|
2013-03-08 09:43:25 +08:00
|
|
|
context = fw_iso_context_create(dev->card, FW_ISO_CONTEXT_RECEIVE,
|
|
|
|
IEEE1394_BROADCAST_CHANNEL,
|
|
|
|
dev->card->link_speed, 8,
|
|
|
|
fwnet_receive_broadcast, dev);
|
|
|
|
if (IS_ERR(context)) {
|
|
|
|
retval = PTR_ERR(context);
|
2013-03-08 09:45:50 +08:00
|
|
|
goto failed;
|
2013-03-08 09:43:25 +08:00
|
|
|
}
|
2009-06-08 04:57:53 +08:00
|
|
|
|
2013-03-08 09:43:25 +08:00
|
|
|
retval = fw_iso_buffer_init(&dev->broadcast_rcv_buffer, dev->card,
|
|
|
|
FWNET_ISO_PAGE_COUNT, DMA_FROM_DEVICE);
|
|
|
|
if (retval < 0)
|
2013-03-08 09:45:50 +08:00
|
|
|
goto failed;
|
|
|
|
|
|
|
|
dev->broadcast_state = FWNET_BROADCAST_STOPPED;
|
2009-06-08 04:57:53 +08:00
|
|
|
|
2013-03-08 09:43:25 +08:00
|
|
|
for (u = 0; u < FWNET_ISO_PAGE_COUNT; u++) {
|
|
|
|
void *ptr;
|
|
|
|
unsigned v;
|
2009-05-19 01:08:06 +08:00
|
|
|
|
2013-03-08 09:43:25 +08:00
|
|
|
ptr = kmap(dev->broadcast_rcv_buffer.pages[u]);
|
|
|
|
for (v = 0; v < num_packets / FWNET_ISO_PAGE_COUNT; v++)
|
|
|
|
*ptrptr++ = (void *) ((char *)ptr + v * max_receive);
|
2009-06-08 04:57:53 +08:00
|
|
|
}
|
2013-03-08 09:43:25 +08:00
|
|
|
dev->broadcast_rcv_context = context;
|
2009-05-19 01:08:06 +08:00
|
|
|
|
|
|
|
packet.payload_length = max_receive;
|
|
|
|
packet.interrupt = 1;
|
|
|
|
packet.skip = 0;
|
|
|
|
packet.tag = 3;
|
|
|
|
packet.sy = 0;
|
2009-06-08 04:57:53 +08:00
|
|
|
packet.header_length = IEEE1394_GASP_HDR_SIZE;
|
2009-05-19 01:08:06 +08:00
|
|
|
offset = 0;
|
2009-06-08 04:57:53 +08:00
|
|
|
|
|
|
|
for (u = 0; u < num_packets; u++) {
|
|
|
|
retval = fw_iso_context_queue(context, &packet,
|
|
|
|
&dev->broadcast_rcv_buffer, offset);
|
|
|
|
if (retval < 0)
|
2013-03-08 09:45:50 +08:00
|
|
|
goto failed;
|
2009-06-08 04:57:53 +08:00
|
|
|
|
2009-05-19 01:08:06 +08:00
|
|
|
offset += max_receive;
|
|
|
|
}
|
2009-06-08 04:57:53 +08:00
|
|
|
dev->num_broadcast_rcv_ptrs = num_packets;
|
|
|
|
dev->rcv_buffer_size = max_receive;
|
|
|
|
dev->broadcast_rcv_next_ptr = 0U;
|
|
|
|
retval = fw_iso_context_start(context, -1, 0,
|
|
|
|
FW_ISO_CONTEXT_MATCH_ALL_TAGS); /* ??? sync */
|
|
|
|
if (retval < 0)
|
2013-03-08 09:45:50 +08:00
|
|
|
goto failed;
|
2009-06-08 04:57:53 +08:00
|
|
|
|
|
|
|
/* FIXME: adjust it according to the min. speed of all known peers? */
|
|
|
|
dev->broadcast_xmt_max_payload = IEEE1394_MAX_PAYLOAD_S100
|
|
|
|
- IEEE1394_GASP_HDR_SIZE - RFC2374_UNFRAG_HDR_SIZE;
|
|
|
|
dev->broadcast_state = FWNET_BROADCAST_RUNNING;
|
|
|
|
|
2009-05-19 01:08:06 +08:00
|
|
|
return 0;
|
|
|
|
|
2013-03-08 09:45:50 +08:00
|
|
|
failed:
|
|
|
|
__fwnet_broadcast_stop(dev);
|
2009-05-19 01:08:06 +08:00
|
|
|
return retval;
|
|
|
|
}
|
|
|
|
|
2010-12-18 05:22:33 +08:00
|
|
|
static void set_carrier_state(struct fwnet_device *dev)
|
|
|
|
{
|
|
|
|
if (dev->peer_count > 1)
|
|
|
|
netif_carrier_on(dev->netdev);
|
|
|
|
else
|
|
|
|
netif_carrier_off(dev->netdev);
|
|
|
|
}
|
|
|
|
|
2009-06-08 04:57:53 +08:00
|
|
|
/* ifup */
|
|
|
|
static int fwnet_open(struct net_device *net)
|
|
|
|
{
|
|
|
|
struct fwnet_device *dev = netdev_priv(net);
|
2009-05-19 01:08:06 +08:00
|
|
|
int ret;
|
|
|
|
|
2013-03-08 09:43:14 +08:00
|
|
|
ret = fwnet_broadcast_start(dev);
|
|
|
|
if (ret)
|
2013-03-25 16:25:57 +08:00
|
|
|
return ret;
|
2013-03-08 09:43:14 +08:00
|
|
|
|
2009-06-08 04:57:53 +08:00
|
|
|
netif_start_queue(net);
|
|
|
|
|
2010-12-18 05:22:33 +08:00
|
|
|
spin_lock_irq(&dev->lock);
|
|
|
|
set_carrier_state(dev);
|
|
|
|
spin_unlock_irq(&dev->lock);
|
|
|
|
|
2009-05-19 01:08:06 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2009-06-08 04:57:53 +08:00
|
|
|
/* ifdown */
|
|
|
|
static int fwnet_stop(struct net_device *net)
|
2009-05-19 01:08:06 +08:00
|
|
|
{
|
2013-03-08 09:45:57 +08:00
|
|
|
struct fwnet_device *dev = netdev_priv(net);
|
|
|
|
|
2009-06-08 04:57:53 +08:00
|
|
|
netif_stop_queue(net);
|
2013-03-08 09:45:57 +08:00
|
|
|
fwnet_broadcast_stop(dev);
|
2009-05-19 01:08:06 +08:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2009-09-01 03:50:51 +08:00
|
|
|
static netdev_tx_t fwnet_tx(struct sk_buff *skb, struct net_device *net)
|
2009-05-19 01:08:06 +08:00
|
|
|
{
|
2009-06-08 04:57:53 +08:00
|
|
|
struct fwnet_header hdr_buf;
|
|
|
|
struct fwnet_device *dev = netdev_priv(net);
|
2009-05-19 01:08:06 +08:00
|
|
|
__be16 proto;
|
|
|
|
u16 dest_node;
|
|
|
|
unsigned max_payload;
|
|
|
|
u16 dg_size;
|
|
|
|
u16 *datagram_label_ptr;
|
2009-06-08 04:57:53 +08:00
|
|
|
struct fwnet_packet_task *ptask;
|
2009-06-14 17:45:27 +08:00
|
|
|
struct fwnet_peer *peer;
|
|
|
|
unsigned long flags;
|
2009-05-19 01:08:06 +08:00
|
|
|
|
2010-11-14 21:35:40 +08:00
|
|
|
spin_lock_irqsave(&dev->lock, flags);
|
|
|
|
|
|
|
|
/* Can this happen? */
|
|
|
|
if (netif_queue_stopped(dev->netdev)) {
|
|
|
|
spin_unlock_irqrestore(&dev->lock, flags);
|
|
|
|
|
|
|
|
return NETDEV_TX_BUSY;
|
|
|
|
}
|
|
|
|
|
2009-06-08 04:57:53 +08:00
|
|
|
ptask = kmem_cache_alloc(fwnet_packet_task_cache, GFP_ATOMIC);
|
2009-05-19 01:08:06 +08:00
|
|
|
if (ptask == NULL)
|
|
|
|
goto fail;
|
|
|
|
|
|
|
|
skb = skb_share_check(skb, GFP_ATOMIC);
|
|
|
|
if (!skb)
|
|
|
|
goto fail;
|
|
|
|
|
|
|
|
/*
|
2009-06-08 04:57:53 +08:00
|
|
|
* Make a copy of the driver-specific header.
|
2009-05-19 01:08:06 +08:00
|
|
|
* We might need to rebuild the header on tx failure.
|
|
|
|
*/
|
|
|
|
memcpy(&hdr_buf, skb->data, sizeof(hdr_buf));
|
|
|
|
proto = hdr_buf.h_proto;
|
2013-03-25 16:25:38 +08:00
|
|
|
|
|
|
|
switch (proto) {
|
|
|
|
case htons(ETH_P_ARP):
|
|
|
|
case htons(ETH_P_IP):
|
2013-03-25 16:26:24 +08:00
|
|
|
#if IS_ENABLED(CONFIG_IPV6)
|
|
|
|
case htons(ETH_P_IPV6):
|
|
|
|
#endif
|
2013-03-25 16:25:38 +08:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
|
|
|
skb_pull(skb, sizeof(hdr_buf));
|
2009-05-19 01:08:06 +08:00
|
|
|
dg_size = skb->len;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Set the transmission type for the packet. ARP packets and IP
|
|
|
|
* broadcast packets are sent via GASP.
|
|
|
|
*/
|
2013-03-25 16:25:48 +08:00
|
|
|
if (fwnet_hwaddr_is_multicast(hdr_buf.h_dest)) {
|
2009-06-14 17:45:27 +08:00
|
|
|
max_payload = dev->broadcast_xmt_max_payload;
|
2009-06-08 04:57:53 +08:00
|
|
|
datagram_label_ptr = &dev->broadcast_xmt_datagramlabel;
|
|
|
|
|
2009-06-14 17:45:27 +08:00
|
|
|
ptask->fifo_addr = FWNET_NO_FIFO_ADDR;
|
|
|
|
ptask->generation = 0;
|
|
|
|
ptask->dest_node = IEEE1394_ALL_NODES;
|
|
|
|
ptask->speed = SCODE_100;
|
2009-05-19 01:08:06 +08:00
|
|
|
} else {
|
firewire net, ipv4 arp: Extend hardware address and remove driver-level packet inspection.
Inspection of upper layer protocol is considered harmful, especially
if it is about ARP or other stateful upper layer protocol; driver
cannot (and should not) have full state of them.
IPv4 over Firewire module used to inspect ARP (both in sending path
and in receiving path), and record peer's GUID, max packet size, max
speed and fifo address. This patch removes such inspection by extending
our "hardware address" definition to include other information as well:
max packet size, max speed and fifo. By doing this, The neighbour
module in networking subsystem can cache them.
Note: As we have started ignoring sspd and max_rec in ARP/NDP, those
information will not be used in the driver when sending.
When a packet is being sent, the IP layer fills our pseudo header with
the extended "hardware address", including GUID and fifo. The driver
can look-up node-id (the real but rather volatile low-level address)
by GUID, and then the module can send the packet to the wire using
parameters provided in the extendedn hardware address.
This approach is realistic because IP over IEEE1394 (RFC2734) and IPv6
over IEEE1394 (RFC3146) share same "hardware address" format
in their address resolution protocols.
Here, extended "hardware address" is defined as follows:
union fwnet_hwaddr {
u8 u[16];
struct {
__be64 uniq_id; /* EUI-64 */
u8 max_rec; /* max packet size */
u8 sspd; /* max speed */
__be16 fifo_hi; /* hi 16bits of FIFO addr */
__be32 fifo_lo; /* lo 32bits of FIFO addr */
} __packed uc;
};
Note that Hardware address is declared as union, so that we can map full
IP address into this, when implementing MCAP (Multicast Cannel Allocation
Protocol) for IPv6, but IP and ARP subsystem do not need to know this
format in detail.
One difference between original ARP (RFC826) and 1394 ARP (RFC2734)
is that 1394 ARP Request/Reply do not contain the target hardware address
field (aka ar$tha). This difference is handled in the ARP subsystem.
CC: Stephan Gatzka <stephan.gatzka@gmail.com>
Signed-off-by: YOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org>
Signed-off-by: David S. Miller <davem@davemloft.net>
2013-03-25 16:26:16 +08:00
|
|
|
union fwnet_hwaddr *ha = (union fwnet_hwaddr *)hdr_buf.h_dest;
|
|
|
|
__be64 guid = get_unaligned(&ha->uc.uniq_id);
|
2009-05-19 01:08:06 +08:00
|
|
|
u8 generation;
|
|
|
|
|
2009-06-08 04:57:53 +08:00
|
|
|
peer = fwnet_peer_find_by_guid(dev, be64_to_cpu(guid));
|
firewire net, ipv4 arp: Extend hardware address and remove driver-level packet inspection.
Inspection of upper layer protocol is considered harmful, especially
if it is about ARP or other stateful upper layer protocol; driver
cannot (and should not) have full state of them.
IPv4 over Firewire module used to inspect ARP (both in sending path
and in receiving path), and record peer's GUID, max packet size, max
speed and fifo address. This patch removes such inspection by extending
our "hardware address" definition to include other information as well:
max packet size, max speed and fifo. By doing this, The neighbour
module in networking subsystem can cache them.
Note: As we have started ignoring sspd and max_rec in ARP/NDP, those
information will not be used in the driver when sending.
When a packet is being sent, the IP layer fills our pseudo header with
the extended "hardware address", including GUID and fifo. The driver
can look-up node-id (the real but rather volatile low-level address)
by GUID, and then the module can send the packet to the wire using
parameters provided in the extendedn hardware address.
This approach is realistic because IP over IEEE1394 (RFC2734) and IPv6
over IEEE1394 (RFC3146) share same "hardware address" format
in their address resolution protocols.
Here, extended "hardware address" is defined as follows:
union fwnet_hwaddr {
u8 u[16];
struct {
__be64 uniq_id; /* EUI-64 */
u8 max_rec; /* max packet size */
u8 sspd; /* max speed */
__be16 fifo_hi; /* hi 16bits of FIFO addr */
__be32 fifo_lo; /* lo 32bits of FIFO addr */
} __packed uc;
};
Note that Hardware address is declared as union, so that we can map full
IP address into this, when implementing MCAP (Multicast Cannel Allocation
Protocol) for IPv6, but IP and ARP subsystem do not need to know this
format in detail.
One difference between original ARP (RFC826) and 1394 ARP (RFC2734)
is that 1394 ARP Request/Reply do not contain the target hardware address
field (aka ar$tha). This difference is handled in the ARP subsystem.
CC: Stephan Gatzka <stephan.gatzka@gmail.com>
Signed-off-by: YOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org>
Signed-off-by: David S. Miller <davem@davemloft.net>
2013-03-25 16:26:16 +08:00
|
|
|
if (!peer)
|
2010-11-14 21:35:40 +08:00
|
|
|
goto fail;
|
2009-05-19 01:08:06 +08:00
|
|
|
|
2009-06-14 17:45:27 +08:00
|
|
|
generation = peer->generation;
|
|
|
|
dest_node = peer->node_id;
|
|
|
|
max_payload = peer->max_payload;
|
2009-06-08 04:57:53 +08:00
|
|
|
datagram_label_ptr = &peer->datagram_label;
|
2009-05-19 01:08:06 +08:00
|
|
|
|
2022-07-26 22:49:06 +08:00
|
|
|
ptask->fifo_addr = get_unaligned_be48(ha->uc.fifo);
|
2009-06-14 17:45:27 +08:00
|
|
|
ptask->generation = generation;
|
|
|
|
ptask->dest_node = dest_node;
|
|
|
|
ptask->speed = peer->speed;
|
2009-05-19 01:08:06 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
ptask->hdr.w0 = 0;
|
|
|
|
ptask->hdr.w1 = 0;
|
|
|
|
ptask->skb = skb;
|
2009-06-08 04:57:53 +08:00
|
|
|
ptask->dev = dev;
|
|
|
|
|
2009-05-19 01:08:06 +08:00
|
|
|
/* Does it all fit in one packet? */
|
2009-06-08 04:57:53 +08:00
|
|
|
if (dg_size <= max_payload) {
|
|
|
|
fwnet_make_uf_hdr(&ptask->hdr, ntohs(proto));
|
2009-05-19 01:08:06 +08:00
|
|
|
ptask->outstanding_pkts = 1;
|
2009-06-08 04:57:53 +08:00
|
|
|
max_payload = dg_size + RFC2374_UNFRAG_HDR_SIZE;
|
2009-05-19 01:08:06 +08:00
|
|
|
} else {
|
|
|
|
u16 datagram_label;
|
|
|
|
|
2009-06-08 04:57:53 +08:00
|
|
|
max_payload -= RFC2374_FRAG_OVERHEAD;
|
2009-05-19 01:08:06 +08:00
|
|
|
datagram_label = (*datagram_label_ptr)++;
|
2009-06-08 04:57:53 +08:00
|
|
|
fwnet_make_ff_hdr(&ptask->hdr, ntohs(proto), dg_size,
|
|
|
|
datagram_label);
|
2009-05-19 01:08:06 +08:00
|
|
|
ptask->outstanding_pkts = DIV_ROUND_UP(dg_size, max_payload);
|
2009-06-08 04:57:53 +08:00
|
|
|
max_payload += RFC2374_FRAG_HDR_SIZE;
|
2009-05-19 01:08:06 +08:00
|
|
|
}
|
2009-06-14 17:45:27 +08:00
|
|
|
|
2010-11-14 21:35:40 +08:00
|
|
|
if (++dev->queued_datagrams == FWNET_MAX_QUEUED_DATAGRAMS)
|
|
|
|
netif_stop_queue(dev->netdev);
|
2010-11-07 06:18:23 +08:00
|
|
|
|
2009-06-14 17:45:27 +08:00
|
|
|
spin_unlock_irqrestore(&dev->lock, flags);
|
|
|
|
|
2009-05-19 01:08:06 +08:00
|
|
|
ptask->max_payload = max_payload;
|
2010-11-07 06:18:23 +08:00
|
|
|
ptask->enqueued = 0;
|
2010-01-19 05:36:49 +08:00
|
|
|
|
2009-06-08 04:57:53 +08:00
|
|
|
fwnet_send_packet(ptask);
|
|
|
|
|
2009-05-19 01:08:06 +08:00
|
|
|
return NETDEV_TX_OK;
|
|
|
|
|
|
|
|
fail:
|
2010-11-14 21:35:40 +08:00
|
|
|
spin_unlock_irqrestore(&dev->lock, flags);
|
|
|
|
|
2009-05-19 01:08:06 +08:00
|
|
|
if (ptask)
|
2009-06-08 04:57:53 +08:00
|
|
|
kmem_cache_free(fwnet_packet_task_cache, ptask);
|
2009-05-19 01:08:06 +08:00
|
|
|
|
|
|
|
if (skb != NULL)
|
|
|
|
dev_kfree_skb(skb);
|
|
|
|
|
2009-06-08 04:57:53 +08:00
|
|
|
net->stats.tx_dropped++;
|
|
|
|
net->stats.tx_errors++;
|
2009-05-19 01:08:06 +08:00
|
|
|
|
|
|
|
/*
|
|
|
|
* FIXME: According to a patch from 2003-02-26, "returning non-zero
|
|
|
|
* causes serious problems" here, allegedly. Before that patch,
|
|
|
|
* -ERRNO was returned which is not appropriate under Linux 2.6.
|
|
|
|
* Perhaps more needs to be done? Stop the queue in serious
|
|
|
|
* conditions and restart it elsewhere?
|
|
|
|
*/
|
|
|
|
return NETDEV_TX_OK;
|
|
|
|
}
|
|
|
|
|
2010-12-08 10:22:57 +08:00
|
|
|
static const struct ethtool_ops fwnet_ethtool_ops = {
|
|
|
|
.get_link = ethtool_op_get_link,
|
|
|
|
};
|
|
|
|
|
2009-06-08 04:57:53 +08:00
|
|
|
static const struct net_device_ops fwnet_netdev_ops = {
|
|
|
|
.ndo_open = fwnet_open,
|
|
|
|
.ndo_stop = fwnet_stop,
|
|
|
|
.ndo_start_xmit = fwnet_tx,
|
2009-05-19 01:08:06 +08:00
|
|
|
};
|
|
|
|
|
2009-06-08 04:57:53 +08:00
|
|
|
static void fwnet_init_dev(struct net_device *net)
|
|
|
|
{
|
|
|
|
net->header_ops = &fwnet_header_ops;
|
|
|
|
net->netdev_ops = &fwnet_netdev_ops;
|
2009-06-14 17:47:44 +08:00
|
|
|
net->watchdog_timeo = 2 * HZ;
|
2009-06-08 04:57:53 +08:00
|
|
|
net->flags = IFF_BROADCAST | IFF_MULTICAST;
|
|
|
|
net->features = NETIF_F_HIGHDMA;
|
|
|
|
net->addr_len = FWNET_ALEN;
|
|
|
|
net->hard_header_len = FWNET_HLEN;
|
|
|
|
net->type = ARPHRD_IEEE1394;
|
2010-11-14 21:35:40 +08:00
|
|
|
net->tx_queue_len = FWNET_TX_QUEUE_LEN;
|
2010-12-08 10:22:57 +08:00
|
|
|
net->ethtool_ops = &fwnet_ethtool_ops;
|
2009-05-19 01:08:06 +08:00
|
|
|
}
|
|
|
|
|
2009-06-14 17:45:27 +08:00
|
|
|
/* 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;
|
|
|
|
|
2009-06-17 02:43:55 +08:00
|
|
|
dev_set_drvdata(&unit->device, peer);
|
|
|
|
|
2009-06-14 17:45:27 +08:00
|
|
|
peer->dev = dev;
|
|
|
|
peer->guid = (u64)device->config_rom[3] << 32 | device->config_rom[4];
|
|
|
|
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);
|
2010-12-08 10:22:57 +08:00
|
|
|
dev->peer_count++;
|
2010-12-18 05:22:33 +08:00
|
|
|
set_carrier_state(dev);
|
2009-06-14 17:45:27 +08:00
|
|
|
spin_unlock_irq(&dev->lock);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2013-06-10 00:15:00 +08:00
|
|
|
static int fwnet_probe(struct fw_unit *unit,
|
|
|
|
const struct ieee1394_device_id *id)
|
2009-06-08 04:57:53 +08:00
|
|
|
{
|
|
|
|
struct fw_device *device = fw_parent_device(unit);
|
|
|
|
struct fw_card *card = device->card;
|
|
|
|
struct net_device *net;
|
2009-06-17 02:43:55 +08:00
|
|
|
bool allocated_netdev = false;
|
2009-06-08 04:57:53 +08:00
|
|
|
struct fwnet_device *dev;
|
2021-10-27 01:53:52 +08:00
|
|
|
union fwnet_hwaddr ha;
|
2009-06-14 17:45:27 +08:00
|
|
|
int ret;
|
2009-05-19 01:08:06 +08:00
|
|
|
|
2009-06-14 17:45:27 +08:00
|
|
|
mutex_lock(&fwnet_device_mutex);
|
2009-05-19 01:08:06 +08:00
|
|
|
|
2009-06-14 17:45:27 +08:00
|
|
|
dev = fwnet_dev_find(card);
|
|
|
|
if (dev) {
|
|
|
|
net = dev->netdev;
|
|
|
|
goto have_dev;
|
2009-05-19 01:08:06 +08:00
|
|
|
}
|
2009-06-14 17:45:27 +08:00
|
|
|
|
net: set name_assign_type in alloc_netdev()
Extend alloc_netdev{,_mq{,s}}() to take name_assign_type as argument, and convert
all users to pass NET_NAME_UNKNOWN.
Coccinelle patch:
@@
expression sizeof_priv, name, setup, txqs, rxqs, count;
@@
(
-alloc_netdev_mqs(sizeof_priv, name, setup, txqs, rxqs)
+alloc_netdev_mqs(sizeof_priv, name, NET_NAME_UNKNOWN, setup, txqs, rxqs)
|
-alloc_netdev_mq(sizeof_priv, name, setup, count)
+alloc_netdev_mq(sizeof_priv, name, NET_NAME_UNKNOWN, setup, count)
|
-alloc_netdev(sizeof_priv, name, setup)
+alloc_netdev(sizeof_priv, name, NET_NAME_UNKNOWN, setup)
)
v9: move comments here from the wrong commit
Signed-off-by: Tom Gundersen <teg@jklm.no>
Reviewed-by: David Herrmann <dh.herrmann@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2014-07-14 22:37:24 +08:00
|
|
|
net = alloc_netdev(sizeof(*dev), "firewire%d", NET_NAME_UNKNOWN,
|
|
|
|
fwnet_init_dev);
|
2009-06-08 04:57:53 +08:00
|
|
|
if (net == NULL) {
|
2014-04-29 17:49:47 +08:00
|
|
|
mutex_unlock(&fwnet_device_mutex);
|
|
|
|
return -ENOMEM;
|
2009-05-19 01:08:06 +08:00
|
|
|
}
|
|
|
|
|
2009-06-17 02:43:55 +08:00
|
|
|
allocated_netdev = true;
|
2009-06-08 04:57:53 +08:00
|
|
|
SET_NETDEV_DEV(net, card->device);
|
|
|
|
dev = netdev_priv(net);
|
2009-05-19 01:08:06 +08:00
|
|
|
|
2009-06-08 04:57:53 +08:00
|
|
|
spin_lock_init(&dev->lock);
|
|
|
|
dev->broadcast_state = FWNET_BROADCAST_ERROR;
|
|
|
|
dev->broadcast_rcv_context = NULL;
|
|
|
|
dev->broadcast_xmt_max_payload = 0;
|
|
|
|
dev->broadcast_xmt_datagramlabel = 0;
|
|
|
|
dev->local_fifo = FWNET_NO_FIFO_ADDR;
|
2010-11-07 06:18:23 +08:00
|
|
|
dev->queued_datagrams = 0;
|
2009-06-14 17:45:27 +08:00
|
|
|
INIT_LIST_HEAD(&dev->peer_list);
|
2009-06-08 04:57:53 +08:00
|
|
|
dev->card = card;
|
2009-06-14 17:45:27 +08:00
|
|
|
dev->netdev = net;
|
2009-05-19 01:08:06 +08:00
|
|
|
|
2013-03-25 16:25:57 +08:00
|
|
|
ret = fwnet_fifo_start(dev);
|
|
|
|
if (ret < 0)
|
|
|
|
goto out;
|
|
|
|
dev->local_fifo = dev->handler.offset;
|
|
|
|
|
2016-11-03 04:09:44 +08:00
|
|
|
/*
|
|
|
|
* default MTU: RFC 2734 cl. 4, RFC 3146 cl. 4
|
|
|
|
* maximum MTU: RFC 2734 cl. 4.2, fragment encapsulation header's
|
|
|
|
* maximum possible datagram_size + 1 = 0xfff + 1
|
|
|
|
*/
|
firewire: net: set initial MTU = 1500 unconditionally, fix IPv6 on some CardBus cards
firewire-net, like the older eth1394 driver, reduced the initial MTU to
less than 1500 octets if the local link layer controller's asynchronous
packet reception limit was lower.
This is bogus, since this reception limit does not have anything to do
with the transmission limit. Neither did this reduction affect the TX
path positively, nor could it prevent link fragmentation at the RX path.
Many FireWire CardBus cards have a max_rec of 9, causing an initial MTU
of 1024 - 16 = 1008. RFC 2734 and RFC 3146 allow a minimum max_rec = 8,
which would result in an initial MTU of 512 - 16 = 496. On such cards,
IPv6 could only be employed if the MTU was manually increased to 1280 or
more, i.e. IPv6 would not work without intervention from userland.
We now always initialize the MTU to 1500, which is the default according
to RFC 2734 and RFC 3146.
On a VIA VT6316 based CardBus card which was affected by this, changing
the MTU from 1008 to 1500 also increases TX bandwidth by 6 %.
RX remains unaffected.
CC: netdev@vger.kernel.org
CC: linux1394-devel@lists.sourceforge.net
CC: Jarod Wilson <jarod@redhat.com>
Signed-off-by: Stefan Richter <stefanr@s5r6.in-berlin.de>
Signed-off-by: David S. Miller <davem@davemloft.net>
2016-10-23 22:30:56 +08:00
|
|
|
net->mtu = 1500U;
|
net: use core MTU range checking in misc drivers
firewire-net:
- set min/max_mtu
- remove fwnet_change_mtu
nes:
- set max_mtu
- clean up nes_netdev_change_mtu
xpnet:
- set min/max_mtu
- remove xpnet_dev_change_mtu
hippi:
- set min/max_mtu
- remove hippi_change_mtu
batman-adv:
- set max_mtu
- remove batadv_interface_change_mtu
- initialization is a little async, not 100% certain that max_mtu is set
in the optimal place, don't have hardware to test with
rionet:
- set min/max_mtu
- remove rionet_change_mtu
slip:
- set min/max_mtu
- streamline sl_change_mtu
um/net_kern:
- remove pointless ndo_change_mtu
hsi/clients/ssi_protocol:
- use core MTU range checking
- remove now redundant ssip_pn_set_mtu
ipoib:
- set a default max MTU value
- Note: ipoib's actual max MTU can vary, depending on if the device is in
connected mode or not, so we'll just set the max_mtu value to the max
possible, and let the ndo_change_mtu function continue to validate any new
MTU change requests with checks for CM or not. Note that ipoib has no
min_mtu set, and thus, the network core's mtu > 0 check is the only lower
bounds here.
mptlan:
- use net core MTU range checking
- remove now redundant mpt_lan_change_mtu
fddi:
- min_mtu = 21, max_mtu = 4470
- remove now redundant fddi_change_mtu (including export)
fjes:
- min_mtu = 8192, max_mtu = 65536
- The max_mtu value is actually one over IP_MAX_MTU here, but the idea is to
get past the core net MTU range checks so fjes_change_mtu can validate a
new MTU against what it supports (see fjes_support_mtu in fjes_hw.c)
hsr:
- min_mtu = 0 (calls ether_setup, max_mtu is 1500)
f_phonet:
- min_mtu = 6, max_mtu = 65541
u_ether:
- min_mtu = 14, max_mtu = 15412
phonet/pep-gprs:
- min_mtu = 576, max_mtu = 65530
- remove redundant gprs_set_mtu
CC: netdev@vger.kernel.org
CC: linux-rdma@vger.kernel.org
CC: Stefan Richter <stefanr@s5r6.in-berlin.de>
CC: Faisal Latif <faisal.latif@intel.com>
CC: linux-rdma@vger.kernel.org
CC: Cliff Whickman <cpw@sgi.com>
CC: Robin Holt <robinmholt@gmail.com>
CC: Jes Sorensen <jes@trained-monkey.org>
CC: Marek Lindner <mareklindner@neomailbox.ch>
CC: Simon Wunderlich <sw@simonwunderlich.de>
CC: Antonio Quartulli <a@unstable.cc>
CC: Sathya Prakash <sathya.prakash@broadcom.com>
CC: Chaitra P B <chaitra.basappa@broadcom.com>
CC: Suganath Prabu Subramani <suganath-prabu.subramani@broadcom.com>
CC: MPT-FusionLinux.pdl@broadcom.com
CC: Sebastian Reichel <sre@kernel.org>
CC: Felipe Balbi <balbi@kernel.org>
CC: Arvid Brodin <arvid.brodin@alten.se>
CC: Remi Denis-Courmont <courmisch@gmail.com>
Signed-off-by: Jarod Wilson <jarod@redhat.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2016-10-21 01:55:22 +08:00
|
|
|
net->min_mtu = ETH_MIN_MTU;
|
2016-11-03 04:09:44 +08:00
|
|
|
net->max_mtu = 4096U;
|
2009-05-19 01:08:06 +08:00
|
|
|
|
|
|
|
/* Set our hardware address while we're at it */
|
2021-10-27 01:53:52 +08:00
|
|
|
ha.uc.uniq_id = cpu_to_be64(card->guid);
|
|
|
|
ha.uc.max_rec = dev->card->max_receive;
|
|
|
|
ha.uc.sspd = dev->card->link_speed;
|
2022-07-26 22:49:06 +08:00
|
|
|
put_unaligned_be48(dev->local_fifo, ha.uc.fifo);
|
2021-10-27 01:53:52 +08:00
|
|
|
dev_addr_set(net, ha.u);
|
firewire net, ipv4 arp: Extend hardware address and remove driver-level packet inspection.
Inspection of upper layer protocol is considered harmful, especially
if it is about ARP or other stateful upper layer protocol; driver
cannot (and should not) have full state of them.
IPv4 over Firewire module used to inspect ARP (both in sending path
and in receiving path), and record peer's GUID, max packet size, max
speed and fifo address. This patch removes such inspection by extending
our "hardware address" definition to include other information as well:
max packet size, max speed and fifo. By doing this, The neighbour
module in networking subsystem can cache them.
Note: As we have started ignoring sspd and max_rec in ARP/NDP, those
information will not be used in the driver when sending.
When a packet is being sent, the IP layer fills our pseudo header with
the extended "hardware address", including GUID and fifo. The driver
can look-up node-id (the real but rather volatile low-level address)
by GUID, and then the module can send the packet to the wire using
parameters provided in the extendedn hardware address.
This approach is realistic because IP over IEEE1394 (RFC2734) and IPv6
over IEEE1394 (RFC3146) share same "hardware address" format
in their address resolution protocols.
Here, extended "hardware address" is defined as follows:
union fwnet_hwaddr {
u8 u[16];
struct {
__be64 uniq_id; /* EUI-64 */
u8 max_rec; /* max packet size */
u8 sspd; /* max speed */
__be16 fifo_hi; /* hi 16bits of FIFO addr */
__be32 fifo_lo; /* lo 32bits of FIFO addr */
} __packed uc;
};
Note that Hardware address is declared as union, so that we can map full
IP address into this, when implementing MCAP (Multicast Cannel Allocation
Protocol) for IPv6, but IP and ARP subsystem do not need to know this
format in detail.
One difference between original ARP (RFC826) and 1394 ARP (RFC2734)
is that 1394 ARP Request/Reply do not contain the target hardware address
field (aka ar$tha). This difference is handled in the ARP subsystem.
CC: Stephan Gatzka <stephan.gatzka@gmail.com>
Signed-off-by: YOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org>
Signed-off-by: David S. Miller <davem@davemloft.net>
2013-03-25 16:26:16 +08:00
|
|
|
|
|
|
|
memset(net->broadcast, -1, net->addr_len);
|
|
|
|
|
2009-06-14 17:45:27 +08:00
|
|
|
ret = register_netdev(net);
|
2012-02-19 19:48:36 +08:00
|
|
|
if (ret)
|
2009-05-19 01:08:06 +08:00
|
|
|
goto out;
|
|
|
|
|
2009-06-14 17:45:27 +08:00
|
|
|
list_add_tail(&dev->dev_link, &fwnet_device_list);
|
2013-03-25 16:26:24 +08:00
|
|
|
dev_notice(&net->dev, "IP over IEEE 1394 on card %s\n",
|
2012-02-19 19:48:36 +08:00
|
|
|
dev_name(card->device));
|
2009-06-14 17:45:27 +08:00
|
|
|
have_dev:
|
|
|
|
ret = fwnet_add_peer(dev, unit, device);
|
2009-06-17 02:43:55 +08:00
|
|
|
if (ret && allocated_netdev) {
|
2009-06-14 17:45:27 +08:00
|
|
|
unregister_netdev(net);
|
|
|
|
list_del(&dev->dev_link);
|
2009-05-19 01:08:06 +08:00
|
|
|
out:
|
2013-03-25 16:25:57 +08:00
|
|
|
fwnet_fifo_stop(dev);
|
2009-06-08 04:57:53 +08:00
|
|
|
free_netdev(net);
|
2013-03-25 16:25:57 +08:00
|
|
|
}
|
2009-06-08 04:57:53 +08:00
|
|
|
|
2009-06-14 17:45:27 +08:00
|
|
|
mutex_unlock(&fwnet_device_mutex);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2013-06-10 00:15:00 +08:00
|
|
|
/*
|
|
|
|
* FIXME abort partially sent fragmented datagrams,
|
|
|
|
* discard partially received fragmented datagrams
|
|
|
|
*/
|
|
|
|
static void fwnet_update(struct fw_unit *unit)
|
|
|
|
{
|
|
|
|
struct fw_device *device = fw_parent_device(unit);
|
|
|
|
struct fwnet_peer *peer = dev_get_drvdata(&unit->device);
|
|
|
|
int generation;
|
|
|
|
|
|
|
|
generation = device->generation;
|
|
|
|
|
|
|
|
spin_lock_irq(&peer->dev->lock);
|
|
|
|
peer->node_id = device->node_id;
|
|
|
|
peer->generation = generation;
|
|
|
|
spin_unlock_irq(&peer->dev->lock);
|
|
|
|
}
|
|
|
|
|
2010-12-18 05:22:33 +08:00
|
|
|
static void fwnet_remove_peer(struct fwnet_peer *peer, struct fwnet_device *dev)
|
2009-06-14 17:45:27 +08:00
|
|
|
{
|
|
|
|
struct fwnet_partial_datagram *pd, *pd_next;
|
|
|
|
|
2010-12-18 05:22:33 +08:00
|
|
|
spin_lock_irq(&dev->lock);
|
2009-06-14 17:45:27 +08:00
|
|
|
list_del(&peer->peer_link);
|
2010-12-18 05:22:33 +08:00
|
|
|
dev->peer_count--;
|
|
|
|
set_carrier_state(dev);
|
|
|
|
spin_unlock_irq(&dev->lock);
|
2009-06-14 17:45:27 +08:00
|
|
|
|
|
|
|
list_for_each_entry_safe(pd, pd_next, &peer->pd_list, pd_link)
|
|
|
|
fwnet_pd_delete(pd);
|
|
|
|
|
|
|
|
kfree(peer);
|
2009-05-19 01:08:06 +08:00
|
|
|
}
|
|
|
|
|
2013-06-10 00:15:00 +08:00
|
|
|
static void fwnet_remove(struct fw_unit *unit)
|
2009-06-08 04:57:53 +08:00
|
|
|
{
|
2013-06-10 00:15:00 +08:00
|
|
|
struct fwnet_peer *peer = dev_get_drvdata(&unit->device);
|
2009-06-14 17:45:27 +08:00
|
|
|
struct fwnet_device *dev = peer->dev;
|
2009-06-08 04:57:53 +08:00
|
|
|
struct net_device *net;
|
2010-11-07 06:18:23 +08:00
|
|
|
int i;
|
2009-06-08 04:57:53 +08:00
|
|
|
|
2009-06-14 17:45:27 +08:00
|
|
|
mutex_lock(&fwnet_device_mutex);
|
2009-05-19 01:08:06 +08:00
|
|
|
|
2010-11-29 10:09:52 +08:00
|
|
|
net = dev->netdev;
|
|
|
|
|
2010-12-18 05:22:33 +08:00
|
|
|
fwnet_remove_peer(peer, dev);
|
2010-12-08 10:22:57 +08:00
|
|
|
|
2009-06-14 17:45:27 +08:00
|
|
|
if (list_empty(&dev->peer_list)) {
|
2009-06-08 04:57:53 +08:00
|
|
|
unregister_netdev(net);
|
|
|
|
|
2013-03-25 16:25:57 +08:00
|
|
|
fwnet_fifo_stop(dev);
|
|
|
|
|
2010-11-07 06:18:23 +08:00
|
|
|
for (i = 0; dev->queued_datagrams && i < 5; i++)
|
|
|
|
ssleep(1);
|
|
|
|
WARN_ON(dev->queued_datagrams);
|
2009-06-17 02:43:55 +08:00
|
|
|
list_del(&dev->dev_link);
|
|
|
|
|
2009-06-08 04:57:53 +08:00
|
|
|
free_netdev(net);
|
2009-05-19 01:08:06 +08:00
|
|
|
}
|
2009-06-08 04:57:53 +08:00
|
|
|
|
2009-06-14 17:45:27 +08:00
|
|
|
mutex_unlock(&fwnet_device_mutex);
|
2009-05-19 01:08:06 +08:00
|
|
|
}
|
|
|
|
|
2009-06-08 04:57:53 +08:00
|
|
|
static const struct ieee1394_device_id fwnet_id_table[] = {
|
|
|
|
{
|
|
|
|
.match_flags = IEEE1394_MATCH_SPECIFIER_ID |
|
|
|
|
IEEE1394_MATCH_VERSION,
|
|
|
|
.specifier_id = IANA_SPECIFIER_ID,
|
|
|
|
.version = RFC2734_SW_VERSION,
|
|
|
|
},
|
2013-03-25 16:26:24 +08:00
|
|
|
#if IS_ENABLED(CONFIG_IPV6)
|
|
|
|
{
|
|
|
|
.match_flags = IEEE1394_MATCH_SPECIFIER_ID |
|
|
|
|
IEEE1394_MATCH_VERSION,
|
|
|
|
.specifier_id = IANA_SPECIFIER_ID,
|
|
|
|
.version = RFC3146_SW_VERSION,
|
|
|
|
},
|
|
|
|
#endif
|
2009-06-08 04:57:53 +08:00
|
|
|
{ }
|
|
|
|
};
|
|
|
|
|
|
|
|
static struct fw_driver fwnet_driver = {
|
2009-05-19 01:08:06 +08:00
|
|
|
.driver = {
|
2009-06-08 04:57:53 +08:00
|
|
|
.owner = THIS_MODULE,
|
2012-02-19 05:01:54 +08:00
|
|
|
.name = KBUILD_MODNAME,
|
2009-06-08 04:57:53 +08:00
|
|
|
.bus = &fw_bus_type,
|
2009-05-19 01:08:06 +08:00
|
|
|
},
|
2013-06-10 00:15:00 +08:00
|
|
|
.probe = fwnet_probe,
|
2009-06-08 04:57:53 +08:00
|
|
|
.update = fwnet_update,
|
2013-06-10 00:15:00 +08:00
|
|
|
.remove = fwnet_remove,
|
2009-06-08 04:57:53 +08:00
|
|
|
.id_table = fwnet_id_table,
|
|
|
|
};
|
|
|
|
|
|
|
|
static const u32 rfc2374_unit_directory_data[] = {
|
|
|
|
0x00040000, /* directory_length */
|
|
|
|
0x1200005e, /* unit_specifier_id: IANA */
|
|
|
|
0x81000003, /* textual descriptor offset */
|
|
|
|
0x13000001, /* unit_sw_version: RFC 2734 */
|
|
|
|
0x81000005, /* textual descriptor offset */
|
|
|
|
0x00030000, /* descriptor_length */
|
|
|
|
0x00000000, /* text */
|
|
|
|
0x00000000, /* minimal ASCII, en */
|
|
|
|
0x49414e41, /* I A N A */
|
|
|
|
0x00030000, /* descriptor_length */
|
|
|
|
0x00000000, /* text */
|
|
|
|
0x00000000, /* minimal ASCII, en */
|
|
|
|
0x49507634, /* I P v 4 */
|
|
|
|
};
|
|
|
|
|
|
|
|
static struct fw_descriptor rfc2374_unit_directory = {
|
|
|
|
.length = ARRAY_SIZE(rfc2374_unit_directory_data),
|
|
|
|
.key = (CSR_DIRECTORY | CSR_UNIT) << 24,
|
|
|
|
.data = rfc2374_unit_directory_data
|
2009-05-19 01:08:06 +08:00
|
|
|
};
|
|
|
|
|
2013-03-25 16:26:24 +08:00
|
|
|
#if IS_ENABLED(CONFIG_IPV6)
|
|
|
|
static const u32 rfc3146_unit_directory_data[] = {
|
|
|
|
0x00040000, /* directory_length */
|
|
|
|
0x1200005e, /* unit_specifier_id: IANA */
|
|
|
|
0x81000003, /* textual descriptor offset */
|
|
|
|
0x13000002, /* unit_sw_version: RFC 3146 */
|
|
|
|
0x81000005, /* textual descriptor offset */
|
|
|
|
0x00030000, /* descriptor_length */
|
|
|
|
0x00000000, /* text */
|
|
|
|
0x00000000, /* minimal ASCII, en */
|
|
|
|
0x49414e41, /* I A N A */
|
|
|
|
0x00030000, /* descriptor_length */
|
|
|
|
0x00000000, /* text */
|
|
|
|
0x00000000, /* minimal ASCII, en */
|
|
|
|
0x49507636, /* I P v 6 */
|
|
|
|
};
|
|
|
|
|
|
|
|
static struct fw_descriptor rfc3146_unit_directory = {
|
|
|
|
.length = ARRAY_SIZE(rfc3146_unit_directory_data),
|
|
|
|
.key = (CSR_DIRECTORY | CSR_UNIT) << 24,
|
|
|
|
.data = rfc3146_unit_directory_data
|
|
|
|
};
|
|
|
|
#endif
|
|
|
|
|
2009-06-08 04:57:53 +08:00
|
|
|
static int __init fwnet_init(void)
|
|
|
|
{
|
|
|
|
int err;
|
|
|
|
|
|
|
|
err = fw_core_add_descriptor(&rfc2374_unit_directory);
|
|
|
|
if (err)
|
|
|
|
return err;
|
2009-05-19 01:08:06 +08:00
|
|
|
|
2013-03-25 16:26:24 +08:00
|
|
|
#if IS_ENABLED(CONFIG_IPV6)
|
|
|
|
err = fw_core_add_descriptor(&rfc3146_unit_directory);
|
|
|
|
if (err)
|
|
|
|
goto out;
|
|
|
|
#endif
|
|
|
|
|
2009-06-08 04:57:53 +08:00
|
|
|
fwnet_packet_task_cache = kmem_cache_create("packet_task",
|
|
|
|
sizeof(struct fwnet_packet_task), 0, 0, NULL);
|
|
|
|
if (!fwnet_packet_task_cache) {
|
|
|
|
err = -ENOMEM;
|
2013-03-25 16:26:24 +08:00
|
|
|
goto out2;
|
2009-06-08 04:57:53 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
err = driver_register(&fwnet_driver.driver);
|
|
|
|
if (!err)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
kmem_cache_destroy(fwnet_packet_task_cache);
|
2013-03-25 16:26:24 +08:00
|
|
|
out2:
|
|
|
|
#if IS_ENABLED(CONFIG_IPV6)
|
|
|
|
fw_core_remove_descriptor(&rfc3146_unit_directory);
|
2009-06-08 04:57:53 +08:00
|
|
|
out:
|
2013-03-25 16:26:24 +08:00
|
|
|
#endif
|
2009-06-08 04:57:53 +08:00
|
|
|
fw_core_remove_descriptor(&rfc2374_unit_directory);
|
|
|
|
|
|
|
|
return err;
|
2009-05-19 01:08:06 +08:00
|
|
|
}
|
2009-06-08 04:57:53 +08:00
|
|
|
module_init(fwnet_init);
|
2009-05-19 01:08:06 +08:00
|
|
|
|
2009-06-08 04:57:53 +08:00
|
|
|
static void __exit fwnet_cleanup(void)
|
|
|
|
{
|
|
|
|
driver_unregister(&fwnet_driver.driver);
|
|
|
|
kmem_cache_destroy(fwnet_packet_task_cache);
|
2013-03-25 16:26:24 +08:00
|
|
|
#if IS_ENABLED(CONFIG_IPV6)
|
|
|
|
fw_core_remove_descriptor(&rfc3146_unit_directory);
|
|
|
|
#endif
|
2009-06-08 04:57:53 +08:00
|
|
|
fw_core_remove_descriptor(&rfc2374_unit_directory);
|
2009-05-19 01:08:06 +08:00
|
|
|
}
|
2009-06-08 04:57:53 +08:00
|
|
|
module_exit(fwnet_cleanup);
|
2009-05-19 01:08:06 +08:00
|
|
|
|
2009-06-08 04:57:53 +08:00
|
|
|
MODULE_AUTHOR("Jay Fenlason <fenlason@redhat.com>");
|
2013-03-25 16:26:24 +08:00
|
|
|
MODULE_DESCRIPTION("IP over IEEE1394 as per RFC 2734/3146");
|
2009-06-08 04:57:53 +08:00
|
|
|
MODULE_LICENSE("GPL");
|
|
|
|
MODULE_DEVICE_TABLE(ieee1394, fwnet_id_table);
|