// SPDX-License-Identifier: GPL-2.0-only/* * IPv4 over IEEE 1394, per RFC 2734 * IPv6 over IEEE 1394, per RFC 3146 * * Copyright (C) 2009 Jay Fenlason <fenlason@redhat.com> * * based on eth1394 by Ben Collins et al */ #include <linux/bug.h>#include <linux/compiler.h>#include <linux/delay.h>#include <linux/device.h>#include <linux/ethtool.h>#include <linux/firewire.h>#include <linux/firewire-constants.h>#include <linux/highmem.h>#include <linux/in.h>#include <linux/ip.h>#include <linux/jiffies.h>#include <linux/module.h>#include <linux/moduleparam.h>#include <linux/mutex.h>#include <linux/netdevice.h>#include <linux/skbuff.h>#include <linux/slab.h>#include <linux/spinlock.h> #include <linux/unaligned.h>#include <net/arp.h>#include <net/firewire.h> /* 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 /* ? */ #define IEEE1394_BROADCAST_CHANNEL 31#define IEEE1394_ALL_NODES (0xffc0 | 0x003f)#define IEEE1394_MAX_PAYLOAD_S100 512#define FWNET_NO_FIFO_ADDR (~0ULL) #define IANA_SPECIFIER_ID 0x00005eU#define RFC2734_SW_VERSION 0x000001U#define RFC3146_SW_VERSION 0x000002U #define IEEE1394_GASP_HDR_SIZE 8 #define RFC2374_UNFRAG_HDR_SIZE 4#define RFC2374_FRAG_HDR_SIZE 8#define RFC2374_FRAG_OVERHEAD 4 #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 */ static bool fwnet_hwaddr_is_multicast(u8 *ha){ return !!(*ha & 1);} /* IPv4 and IPv6 encapsulation header */struct rfc2734_header { u32 w0; u32 w1;}; #define fwnet_get_hdr_lf(h) (((h)->w0 & 0xc0000000) >> 30)#define fwnet_get_hdr_ether_type(h) (((h)->w0 & 0x0000ffff))#define fwnet_get_hdr_dg_size(h) ((((h)->w0 & 0x0fff0000) >> 16) + 1)#define fwnet_get_hdr_fg_off(h) (((h)->w0 & 0x00000fff))#define fwnet_get_hdr_dgl(h) (((h)->w1 & 0xffff0000) >> 16) #define fwnet_set_hdr_lf(lf) ((lf) << 30)#define fwnet_set_hdr_ether_type(et) (et)#define fwnet_set_hdr_dg_size(dgs) (((dgs) - 1) << 16)#define fwnet_set_hdr_fg_off(fgo) (fgo) #define fwnet_set_hdr_dgl(dgl) ((dgl) << 16) 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);} 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);} 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);} /* This list keeps track of what parts of the datagram have been filled in */struct fwnet_fragment_info { struct list_head fi_link; u16 offset; u16 len;}; struct fwnet_partial_datagram { struct list_head pd_link; struct list_head fi_list; struct sk_buff *skb; /* FIXME Why not use skb->data? */ char *pbuf; u16 datagram_label; u16 ether_type; u16 datagram_size;}; static DEFINE_MUTEX(fwnet_device_mutex);static LIST_HEAD(fwnet_device_list); struct fwnet_device { struct list_head dev_link; spinlock_t lock; enum { FWNET_BROADCAST_ERROR, FWNET_BROADCAST_RUNNING, FWNET_BROADCAST_STOPPED, } broadcast_state; 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; /* * The CSR address that remote nodes must send datagrams to for us to * receive them. */ struct fw_address_handler handler; u64 local_fifo; /* Number of tx datagrams that have been queued but not yet acked */ int queued_datagrams; int peer_count; struct list_head peer_list; struct fw_card *card; struct net_device *netdev;}; struct fwnet_peer { struct list_head peer_link; struct fwnet_device *dev; u64 guid; /* guarded by dev->lock */ struct list_head pd_list; /* received partial datagrams */ unsigned pdg_size; /* pd_list size */ u16 datagram_label; /* outgoing datagram label */ u16 max_payload; /* includes RFC2374_FRAG_HDR_SIZE overhead */ int node_id; int generation; unsigned speed;}; /* This is our task struct. It's used for the packet complete callback. */struct fwnet_packet_task { struct fw_transaction transaction; struct rfc2734_header hdr; struct sk_buff *skb; struct fwnet_device *dev; int outstanding_pkts; u64 fifo_addr; u16 dest_node; u16 max_payload; u8 generation; u8 speed; u8 enqueued;}; /* * 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; h = skb_push(skb, sizeof(*h)); put_unaligned_be16(type, &h->h_proto); if (net->flags & (IFF_LOOPBACK | IFF_NOARP)) { memset(h->h_dest, 0, net->addr_len); return net->hard_header_len; } if (daddr) { memcpy(h->h_dest, daddr, net->addr_len); return net->hard_header_len; } return -net->hard_header_len;} static int fwnet_header_cache(const struct neighbour *neigh, struct hh_cache *hh, __be16 type){ struct net_device *net; struct fwnet_header *h; if (type == cpu_to_be16(ETH_P_802_3)) return -1; net = neigh->dev; h = (struct fwnet_header *)((u8 *)hh->hh_data + HH_DATA_OFF(sizeof(*h))); h->h_proto = type; memcpy(h->h_dest, neigh->ha, net->addr_len); /* Pairs with the READ_ONCE() in neigh_resolve_output(), * neigh_hh_output() and neigh_update_hhs(). */ smp_store_release(&hh->hh_len, FWNET_HLEN); return 0;} /* Called by Address Resolution module to notify changes in address. */static void fwnet_header_cache_update(struct hh_cache *hh, const struct net_device *net, const unsigned char *haddr){ memcpy((u8 *)hh->hh_data + HH_DATA_OFF(FWNET_HLEN), haddr, net->addr_len);} static int fwnet_header_parse(const struct sk_buff *skb, const struct net_device *dev, unsigned char *haddr){ memcpy(haddr, dev->dev_addr, FWNET_ALEN); return FWNET_ALEN;} 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,}; /* FIXME: is this correct for all cases? */static bool fwnet_frag_overlap(struct fwnet_partial_datagram *pd, unsigned offset, unsigned len){ struct fwnet_fragment_info *fi; unsigned end = offset + len; list_for_each_entry(fi, &pd->fi_list, fi_link) if (offset < fi->offset + fi->len && end > fi->offset) return true; return false;} /* Assumes that new fragment does not overlap any existing fragments */static struct fwnet_fragment_info *fwnet_frag_new( struct fwnet_partial_datagram *pd, unsigned offset, unsigned len){ struct fwnet_fragment_info *fi, *fi2, *new; struct list_head *list; list = &pd->fi_list; list_for_each_entry(fi, &pd->fi_list, fi_link) { if (fi->offset + fi->len == offset) { /* The new fragment can be tacked on to the end */ /* Did the new fragment plug a hole? */ if (!list_is_last(&fi->fi_link, &pd->fi_list)) { fi2 = list_next_entry(fi, fi_link); if (offset + len == fi2->offset) { /* glue fragments together */ fi->len += len + fi2->len; list_del(&fi2->fi_link); kfree(fi2); return fi; } } fi->len += len; return fi; } if (offset + len == fi->offset) { /* The new fragment can be tacked on to the beginning */ /* Did the new fragment plug a hole? */ if (!list_is_first(&fi->fi_link, &pd->fi_list)) { fi2 = list_prev_entry(fi, fi_link); if (fi2->offset + fi2->len == offset) { /* glue fragments together */ fi2->len += fi->len + len; list_del(&fi->fi_link); kfree(fi); return fi2; } } fi->offset = offset; fi->len += len; return fi; } if (offset > fi->offset + fi->len) { list = &fi->fi_link; break; } if (offset + len < fi->offset) { list = fi->fi_link.prev; break; } } new = kmalloc_obj(*new, GFP_ATOMIC); if (!new) return NULL; new->offset = offset; new->len = len; list_add(&new->fi_link, list); return new;} 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; new = kmalloc_obj(*new, GFP_ATOMIC); if (!new) goto fail; INIT_LIST_HEAD(&new->fi_list); fi = fwnet_frag_new(new, frag_off, frag_len); if (fi == NULL) goto fail_w_new; new->datagram_label = datagram_label; new->datagram_size = dg_size; new->skb = dev_alloc_skb(dg_size + LL_RESERVED_SPACE(net)); if (new->skb == NULL) goto fail_w_fi; skb_reserve(new->skb, LL_RESERVED_SPACE(net)); new->pbuf = skb_put(new->skb, dg_size); memcpy(new->pbuf + frag_off, frag_buf, frag_len); list_add_tail(&new->pd_link, &peer->pd_list); return new; fail_w_fi: kfree(fi);fail_w_new: kfree(new);fail: return NULL;} static struct fwnet_partial_datagram *fwnet_pd_find(struct fwnet_peer *peer, u16 datagram_label){ struct fwnet_partial_datagram *pd; list_for_each_entry(pd, &peer->pd_list, pd_link) if (pd->datagram_label == datagram_label) return pd; return NULL;} static void fwnet_pd_delete(struct fwnet_partial_datagram *old){ struct fwnet_fragment_info *fi, *n; list_for_each_entry_safe(fi, n, &old->fi_list, fi_link) kfree(fi); list_del(&old->pd_link); dev_kfree_skb_any(old->skb); kfree(old);} 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) return false; memcpy(pd->pbuf + frag_off, frag_buf, frag_len); /* * Move list entry to beginning of list so that oldest partial * datagrams percolate to the end of the list */ list_move_tail(&pd->pd_link, &peer->pd_list); return true;} static bool fwnet_pd_is_complete(struct fwnet_partial_datagram *pd){ struct fwnet_fragment_info *fi; fi = list_entry(pd->fi_list.next, struct fwnet_fragment_info, fi_link); return fi->len == pd->datagram_size;} /* caller must hold dev->lock */static struct fwnet_peer *fwnet_peer_find_by_guid(struct fwnet_device *dev, u64 guid){ struct fwnet_peer *peer; list_for_each_entry(peer, &dev->peer_list, peer_link) if (peer->guid == guid) return peer; return NULL;} /* caller must hold dev->lock */static struct fwnet_peer *fwnet_peer_find_by_node_id(struct fwnet_device *dev, int node_id, int generation){ struct fwnet_peer *peer; list_for_each_entry(peer, &dev->peer_list, peer_link) if (peer->node_id == node_id && peer->generation == generation) return peer; return NULL;} /* See IEEE 1394-2008 table 6-4, table 8-8, table 16-18. */static unsigned fwnet_max_payload(unsigned max_rec, unsigned speed){ max_rec = min(max_rec, speed + 8); max_rec = clamp(max_rec, 8U, 11U); /* 512...4096 */ return (1 << (max_rec + 1)) - RFC2374_FRAG_HDR_SIZE;} static int fwnet_finish_incoming_packet(struct net_device *net, struct sk_buff *skb, u16 source_node_id, bool is_broadcast, u16 ether_type){ int status, len; switch (ether_type) { case ETH_P_ARP: case ETH_P_IP:#if IS_ENABLED(CONFIG_IPV6) case ETH_P_IPV6:#endif break; default: goto err; } /* Write metadata, and then pass to the receive level */ skb->dev = net; skb->ip_summed = CHECKSUM_NONE;Lines 1–500 of 1711. Load more
// SPDX-License-Identifier: GPL-2.0-only /* * IPv4 over IEEE 1394, per RFC 2734 * IPv6 over IEEE 1394, per RFC 3146 * * Copyright (C) 2009 Jay Fenlason <fenlason@redhat.com> * * based on eth1394 by Ben Collins et al */ #include <linux/bug.h> #include <linux/compiler.h> #include <linux/delay.h> #include <linux/device.h> #include <linux/ethtool.h> #include <linux/firewire.h> #include <linux/firewire-constants.h> #include <linux/highmem.h> #include <linux/in.h> #include <linux/ip.h> #include <linux/jiffies.h> #include <linux/module.h> #include <linux/moduleparam.h> #include <linux/mutex.h> #include <linux/netdevice.h> #include <linux/skbuff.h> #include <linux/slab.h> #include <linux/spinlock.h> #include <linux/unaligned.h> #include <net/arp.h> #include <net/firewire.h> /* 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 /* ? */ #define IEEE1394_BROADCAST_CHANNEL 31 #define IEEE1394_ALL_NODES (0xffc0 | 0x003f) #define IEEE1394_MAX_PAYLOAD_S100 512 #define FWNET_NO_FIFO_ADDR (~0ULL) #define IANA_SPECIFIER_ID 0x00005eU #define RFC2734_SW_VERSION 0x000001U #define RFC3146_SW_VERSION 0x000002U #define IEEE1394_GASP_HDR_SIZE 8 #define RFC2374_UNFRAG_HDR_SIZE 4 #define RFC2374_FRAG_HDR_SIZE 8 #define RFC2374_FRAG_OVERHEAD 4 #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 */ static bool fwnet_hwaddr_is_multicast(u8 *ha) { return !!(*ha & 1); } /* IPv4 and IPv6 encapsulation header */ struct rfc2734_header { u32 w0; u32 w1; }; #define fwnet_get_hdr_lf(h) (((h)->w0 & 0xc0000000) >> 30) #define fwnet_get_hdr_ether_type(h) (((h)->w0 & 0x0000ffff)) #define fwnet_get_hdr_dg_size(h) ((((h)->w0 & 0x0fff0000) >> 16) + 1) #define fwnet_get_hdr_fg_off(h) (((h)->w0 & 0x00000fff)) #define fwnet_get_hdr_dgl(h) (((h)->w1 & 0xffff0000) >> 16) #define fwnet_set_hdr_lf(lf) ((lf) << 30) #define fwnet_set_hdr_ether_type(et) (et) #define fwnet_set_hdr_dg_size(dgs) (((dgs) - 1) << 16) #define fwnet_set_hdr_fg_off(fgo) (fgo) #define fwnet_set_hdr_dgl(dgl) ((dgl) << 16) 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); } 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); } 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); } /* This list keeps track of what parts of the datagram have been filled in */ struct fwnet_fragment_info { struct list_head fi_link; u16 offset; u16 len; }; struct fwnet_partial_datagram { struct list_head pd_link; struct list_head fi_list; struct sk_buff *skb; /* FIXME Why not use skb->data? */ char *pbuf; u16 datagram_label; u16 ether_type; u16 datagram_size; }; static DEFINE_MUTEX(fwnet_device_mutex); static LIST_HEAD(fwnet_device_list); struct fwnet_device { struct list_head dev_link; spinlock_t lock; enum { FWNET_BROADCAST_ERROR, FWNET_BROADCAST_RUNNING, FWNET_BROADCAST_STOPPED, } broadcast_state; 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; /* * The CSR address that remote nodes must send datagrams to for us to * receive them. */ struct fw_address_handler handler; u64 local_fifo; /* Number of tx datagrams that have been queued but not yet acked */ int queued_datagrams; int peer_count; struct list_head peer_list; struct fw_card *card; struct net_device *netdev; }; struct fwnet_peer { struct list_head peer_link; struct fwnet_device *dev; u64 guid; /* guarded by dev->lock */ struct list_head pd_list; /* received partial datagrams */ unsigned pdg_size; /* pd_list size */ u16 datagram_label; /* outgoing datagram label */ u16 max_payload; /* includes RFC2374_FRAG_HDR_SIZE overhead */ int node_id; int generation; unsigned speed; }; /* This is our task struct. It's used for the packet complete callback. */ struct fwnet_packet_task { struct fw_transaction transaction; struct rfc2734_header hdr; struct sk_buff *skb; struct fwnet_device *dev; int outstanding_pkts; u64 fifo_addr; u16 dest_node; u16 max_payload; u8 generation; u8 speed; u8 enqueued; }; /* * 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; h = skb_push(skb, sizeof(*h)); put_unaligned_be16(type, &h->h_proto); if (net->flags & (IFF_LOOPBACK | IFF_NOARP)) { memset(h->h_dest, 0, net->addr_len); return net->hard_header_len; } if (daddr) { memcpy(h->h_dest, daddr, net->addr_len); return net->hard_header_len; } return -net->hard_header_len; } static int fwnet_header_cache(const struct neighbour *neigh, struct hh_cache *hh, __be16 type) { struct net_device *net; struct fwnet_header *h; if (type == cpu_to_be16(ETH_P_802_3)) return -1; net = neigh->dev; h = (struct fwnet_header *)((u8 *)hh->hh_data + HH_DATA_OFF(sizeof(*h))); h->h_proto = type; memcpy(h->h_dest, neigh->ha, net->addr_len); /* Pairs with the READ_ONCE() in neigh_resolve_output(), * neigh_hh_output() and neigh_update_hhs(). */ smp_store_release(&hh->hh_len, FWNET_HLEN); return 0; } /* Called by Address Resolution module to notify changes in address. */ static void fwnet_header_cache_update(struct hh_cache *hh, const struct net_device *net, const unsigned char *haddr) { memcpy((u8 *)hh->hh_data + HH_DATA_OFF(FWNET_HLEN), haddr, net->addr_len); } static int fwnet_header_parse(const struct sk_buff *skb, const struct net_device *dev, unsigned char *haddr) { memcpy(haddr, dev->dev_addr, FWNET_ALEN); return FWNET_ALEN; } 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, }; /* FIXME: is this correct for all cases? */ static bool fwnet_frag_overlap(struct fwnet_partial_datagram *pd, unsigned offset, unsigned len) { struct fwnet_fragment_info *fi; unsigned end = offset + len; list_for_each_entry(fi, &pd->fi_list, fi_link) if (offset < fi->offset + fi->len && end > fi->offset) return true; return false; } /* Assumes that new fragment does not overlap any existing fragments */ static struct fwnet_fragment_info *fwnet_frag_new( struct fwnet_partial_datagram *pd, unsigned offset, unsigned len) { struct fwnet_fragment_info *fi, *fi2, *new; struct list_head *list; list = &pd->fi_list; list_for_each_entry(fi, &pd->fi_list, fi_link) { if (fi->offset + fi->len == offset) { /* The new fragment can be tacked on to the end */ /* Did the new fragment plug a hole? */ if (!list_is_last(&fi->fi_link, &pd->fi_list)) { fi2 = list_next_entry(fi, fi_link); if (offset + len == fi2->offset) { /* glue fragments together */ fi->len += len + fi2->len; list_del(&fi2->fi_link); kfree(fi2); return fi; } } fi->len += len; return fi; } if (offset + len == fi->offset) { /* The new fragment can be tacked on to the beginning */ /* Did the new fragment plug a hole? */ if (!list_is_first(&fi->fi_link, &pd->fi_list)) { fi2 = list_prev_entry(fi, fi_link); if (fi2->offset + fi2->len == offset) { /* glue fragments together */ fi2->len += fi->len + len; list_del(&fi->fi_link); kfree(fi); return fi2; } } fi->offset = offset; fi->len += len; return fi; } if (offset > fi->offset + fi->len) { list = &fi->fi_link; break; } if (offset + len < fi->offset) { list = fi->fi_link.prev; break; } } new = kmalloc_obj(*new, GFP_ATOMIC); if (!new) return NULL; new->offset = offset; new->len = len; list_add(&new->fi_link, list); return new; } 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; new = kmalloc_obj(*new, GFP_ATOMIC); if (!new) goto fail; INIT_LIST_HEAD(&new->fi_list); fi = fwnet_frag_new(new, frag_off, frag_len); if (fi == NULL) goto fail_w_new; new->datagram_label = datagram_label; new->datagram_size = dg_size; new->skb = dev_alloc_skb(dg_size + LL_RESERVED_SPACE(net)); if (new->skb == NULL) goto fail_w_fi; skb_reserve(new->skb, LL_RESERVED_SPACE(net)); new->pbuf = skb_put(new->skb, dg_size); memcpy(new->pbuf + frag_off, frag_buf, frag_len); list_add_tail(&new->pd_link, &peer->pd_list); return new; fail_w_fi: kfree(fi); fail_w_new: kfree(new); fail: return NULL; } static struct fwnet_partial_datagram *fwnet_pd_find(struct fwnet_peer *peer, u16 datagram_label) { struct fwnet_partial_datagram *pd; list_for_each_entry(pd, &peer->pd_list, pd_link) if (pd->datagram_label == datagram_label) return pd; return NULL; } static void fwnet_pd_delete(struct fwnet_partial_datagram *old) { struct fwnet_fragment_info *fi, *n; list_for_each_entry_safe(fi, n, &old->fi_list, fi_link) kfree(fi); list_del(&old->pd_link); dev_kfree_skb_any(old->skb); kfree(old); } 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) return false; memcpy(pd->pbuf + frag_off, frag_buf, frag_len); /* * Move list entry to beginning of list so that oldest partial * datagrams percolate to the end of the list */ list_move_tail(&pd->pd_link, &peer->pd_list); return true; } static bool fwnet_pd_is_complete(struct fwnet_partial_datagram *pd) { struct fwnet_fragment_info *fi; fi = list_entry(pd->fi_list.next, struct fwnet_fragment_info, fi_link); return fi->len == pd->datagram_size; } /* caller must hold dev->lock */ static struct fwnet_peer *fwnet_peer_find_by_guid(struct fwnet_device *dev, u64 guid) { struct fwnet_peer *peer; list_for_each_entry(peer, &dev->peer_list, peer_link) if (peer->guid == guid) return peer; return NULL; } /* caller must hold dev->lock */ static struct fwnet_peer *fwnet_peer_find_by_node_id(struct fwnet_device *dev, int node_id, int generation) { struct fwnet_peer *peer; list_for_each_entry(peer, &dev->peer_list, peer_link) if (peer->node_id == node_id && peer->generation == generation) return peer; return NULL; } /* See IEEE 1394-2008 table 6-4, table 8-8, table 16-18. */ static unsigned fwnet_max_payload(unsigned max_rec, unsigned speed) { max_rec = min(max_rec, speed + 8); max_rec = clamp(max_rec, 8U, 11U); /* 512...4096 */ return (1 << (max_rec + 1)) - RFC2374_FRAG_HDR_SIZE; } static int fwnet_finish_incoming_packet(struct net_device *net, struct sk_buff *skb, u16 source_node_id, bool is_broadcast, u16 ether_type) { int status, len; switch (ether_type) { case ETH_P_ARP: case ETH_P_IP: #if IS_ENABLED(CONFIG_IPV6) case ETH_P_IPV6: #endif break; default: goto err; } /* Write metadata, and then pass to the receive level */ skb->dev = net; skb->ip_summed = CHECKSUM_NONE;