Jetpack/laird/backport/compat/backport-3.18.c

335 lines
9.1 KiB
C

/*
* Copyright (c) 2014 Hauke Mehrtens <hauke@hauke-m.de>
*
* Backport functionality introduced in Linux 3.18.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/if_ether.h>
#include <linux/if_vlan.h>
#include <linux/ip.h>
#include <linux/ipv6.h>
#include <scsi/fc/fc_fcoe.h>
#include <linux/skbuff.h>
#include <linux/errqueue.h>
#include <linux/wait.h>
#include <linux/of.h>
#include <linux/string.h>
/**
* eth_get_headlen - determine the the length of header for an ethernet frame
* @data: pointer to start of frame
* @len: total length of frame
*
* Make a best effort attempt to pull the length for all of the headers for
* a given frame in a linear buffer.
*/
int eth_get_headlen(unsigned char *data, unsigned int max_len)
{
union {
unsigned char *network;
/* l2 headers */
struct ethhdr *eth;
struct vlan_hdr *vlan;
/* l3 headers */
struct iphdr *ipv4;
struct ipv6hdr *ipv6;
} hdr;
__be16 protocol;
u8 nexthdr = 0; /* default to not TCP */
u8 hlen;
/* this should never happen, but better safe than sorry */
if (max_len < ETH_HLEN)
return max_len;
/* initialize network frame pointer */
hdr.network = data;
/* set first protocol and move network header forward */
protocol = hdr.eth->h_proto;
hdr.network += ETH_HLEN;
/* handle any vlan tag if present */
if (protocol == htons(ETH_P_8021Q)) {
if ((hdr.network - data) > (max_len - VLAN_HLEN))
return max_len;
protocol = hdr.vlan->h_vlan_encapsulated_proto;
hdr.network += VLAN_HLEN;
}
/* handle L3 protocols */
if (protocol == htons(ETH_P_IP)) {
if ((hdr.network - data) > (max_len - sizeof(struct iphdr)))
return max_len;
/* access ihl as a u8 to avoid unaligned access on ia64 */
hlen = (hdr.network[0] & 0x0F) << 2;
/* verify hlen meets minimum size requirements */
if (hlen < sizeof(struct iphdr))
return hdr.network - data;
/* record next protocol if header is present */
if (!(hdr.ipv4->frag_off & htons(IP_OFFSET)))
nexthdr = hdr.ipv4->protocol;
} else if (protocol == htons(ETH_P_IPV6)) {
if ((hdr.network - data) > (max_len - sizeof(struct ipv6hdr)))
return max_len;
/* record next protocol */
nexthdr = hdr.ipv6->nexthdr;
hlen = sizeof(struct ipv6hdr);
} else if (protocol == htons(ETH_P_FCOE)) {
if ((hdr.network - data) > (max_len - FCOE_HEADER_LEN))
return max_len;
hlen = FCOE_HEADER_LEN;
} else {
return hdr.network - data;
}
/* relocate pointer to start of L4 header */
hdr.network += hlen;
/* finally sort out TCP/UDP */
if (nexthdr == IPPROTO_TCP) {
if ((hdr.network - data) > (max_len - sizeof(struct tcphdr)))
return max_len;
/* access doff as a u8 to avoid unaligned access on ia64 */
hlen = (hdr.network[12] & 0xF0) >> 2;
/* verify hlen meets minimum size requirements */
if (hlen < sizeof(struct tcphdr))
return hdr.network - data;
hdr.network += hlen;
} else if (nexthdr == IPPROTO_UDP) {
if ((hdr.network - data) > (max_len - sizeof(struct udphdr)))
return max_len;
hdr.network += sizeof(struct udphdr);
}
/*
* If everything has gone correctly hdr.network should be the
* data section of the packet and will be the end of the header.
* If not then it probably represents the end of the last recognized
* header.
*/
if ((hdr.network - data) < max_len)
return hdr.network - data;
else
return max_len;
}
EXPORT_SYMBOL_GPL(eth_get_headlen);
#define sock_efree LINUX_BACKPORT(sock_efree)
static void sock_efree(struct sk_buff *skb)
{
sock_put(skb->sk);
}
/**
* skb_clone_sk - create clone of skb, and take reference to socket
* @skb: the skb to clone
*
* This function creates a clone of a buffer that holds a reference on
* sk_refcnt. Buffers created via this function are meant to be
* returned using sock_queue_err_skb, or free via kfree_skb.
*
* When passing buffers allocated with this function to sock_queue_err_skb
* it is necessary to wrap the call with sock_hold/sock_put in order to
* prevent the socket from being released prior to being enqueued on
* the sk_error_queue.
*/
struct sk_buff *skb_clone_sk(struct sk_buff *skb)
{
struct sock *sk = skb->sk;
struct sk_buff *clone;
if (!sk || !atomic_inc_not_zero(&sk->sk_refcnt))
return NULL;
clone = skb_clone(skb, GFP_ATOMIC);
if (!clone) {
sock_put(sk);
return NULL;
}
clone->sk = sk;
clone->destructor = sock_efree;
return clone;
}
EXPORT_SYMBOL_GPL(skb_clone_sk);
#if LINUX_VERSION_IS_GEQ(3,3,0)
/*
* skb_complete_wifi_ack() needs to get backported, because the version from
* 3.18 added the sock_hold() and sock_put() calles missing in older versions.
*/
void skb_complete_wifi_ack(struct sk_buff *skb, bool acked)
{
struct sock *sk = skb->sk;
struct sock_exterr_skb *serr;
int err;
#if LINUX_VERSION_IS_GEQ(3,3,0)
skb->wifi_acked_valid = 1;
skb->wifi_acked = acked;
#endif
serr = SKB_EXT_ERR(skb);
memset(serr, 0, sizeof(*serr));
serr->ee.ee_errno = ENOMSG;
serr->ee.ee_origin = SO_EE_ORIGIN_TXSTATUS;
/* take a reference to prevent skb_orphan() from freeing the socket */
sock_hold(sk);
err = sock_queue_err_skb(sk, skb);
if (err)
kfree_skb(skb);
sock_put(sk);
}
EXPORT_SYMBOL_GPL(skb_complete_wifi_ack);
#endif
#if LINUX_VERSION_IS_GEQ(3,17,0)
int __sched out_of_line_wait_on_bit_timeout(
void *word, int bit, wait_bit_action_f *action,
unsigned mode, unsigned long timeout)
{
wait_queue_head_t *wq = bit_waitqueue(word, bit);
DEFINE_WAIT_BIT(wait, word, bit);
wait.key.private = jiffies + timeout;
return __wait_on_bit(wq, &wait, action, mode);
}
EXPORT_SYMBOL_GPL(out_of_line_wait_on_bit_timeout);
__sched int bit_wait_timeout(struct wait_bit_key *word)
{
unsigned long now = ACCESS_ONCE(jiffies);
if (signal_pending_state(current->state, current))
return 1;
if (time_after_eq(now, word->private))
return -EAGAIN;
schedule_timeout(word->private - now);
return 0;
}
EXPORT_SYMBOL_GPL(bit_wait_timeout);
#endif
#ifdef CONFIG_OF
/**
* of_find_property_value_of_size
*
* @np: device node from which the property value is to be read.
* @propname: name of the property to be searched.
* @len: requested length of property value
*
* Search for a property in a device node and valid the requested size.
* Returns the property value on success, -EINVAL if the property does not
* exist, -ENODATA if property does not have a value, and -EOVERFLOW if the
* property data isn't large enough.
*
*/
void *of_find_property_value_of_size(const struct device_node *np,
const char *propname, u32 len)
{
struct property *prop = of_find_property(np, propname, NULL);
if (!prop)
return ERR_PTR(-EINVAL);
if (!prop->value)
return ERR_PTR(-ENODATA);
if (len > prop->length)
return ERR_PTR(-EOVERFLOW);
return prop->value;
}
/**
* of_property_read_u64_array - Find and read an array of 64 bit integers
* from a property.
*
* @np: device node from which the property value is to be read.
* @propname: name of the property to be searched.
* @out_values: pointer to return value, modified only if return value is 0.
* @sz: number of array elements to read
*
* Search for a property in a device node and read 64-bit value(s) from
* it. Returns 0 on success, -EINVAL if the property does not exist,
* -ENODATA if property does not have a value, and -EOVERFLOW if the
* property data isn't large enough.
*
* The out_values is modified only if a valid u64 value can be decoded.
*/
int of_property_read_u64_array(const struct device_node *np,
const char *propname, u64 *out_values,
size_t sz)
{
const __be32 *val = of_find_property_value_of_size(np, propname,
(sz * sizeof(*out_values)));
if (IS_ERR(val))
return PTR_ERR(val);
while (sz--) {
*out_values++ = of_read_number(val, 2);
val += 2;
}
return 0;
}
EXPORT_SYMBOL_GPL(of_property_read_u64_array);
#endif /* CONFIG_OF */
#if !(LINUX_VERSION_IS_GEQ(3,17,3) || \
(LINUX_VERSION_IS_GEQ(3,14,24) && \
LINUX_VERSION_IS_LESS(3,15,0)) || \
(LINUX_VERSION_IS_GEQ(3,12,33) && \
LINUX_VERSION_IS_LESS(3,13,0)) || \
(LINUX_VERSION_IS_GEQ(3,10,60) && \
LINUX_VERSION_IS_LESS(3,11,0)) || \
(LINUX_VERSION_IS_GEQ(3,4,106) && \
LINUX_VERSION_IS_LESS(3,5,0)) || \
(LINUX_VERSION_IS_GEQ(3,2,65) && \
LINUX_VERSION_IS_LESS(3,3,0)))
/**
* memzero_explicit - Fill a region of memory (e.g. sensitive
* keying data) with 0s.
* @s: Pointer to the start of the area.
* @count: The size of the area.
*
* Note: usually using memset() is just fine (!), but in cases
* where clearing out _local_ data at the end of a scope is
* necessary, memzero_explicit() should be used instead in
* order to prevent the compiler from optimising away zeroing.
*
* memzero_explicit() doesn't need an arch-specific version as
* it just invokes the one of memset() implicitly.
*/
void memzero_explicit(void *s, size_t count)
{
memset(s, 0, count);
barrier_data(s);
}
EXPORT_SYMBOL_GPL(memzero_explicit);
#endif
char *bin2hex(char *dst, const void *src, size_t count)
{
const unsigned char *_src = src;
while (count--)
dst = hex_byte_pack(dst, *_src++);
return dst;
}
EXPORT_SYMBOL(bin2hex);