forked from rrcarlosr/Jetpack
227 lines
5.4 KiB
C
227 lines
5.4 KiB
C
/*
|
|
* Copyright 2012 Luis R. Rodriguez <mcgrof@frijolero.org>
|
|
*
|
|
* 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.
|
|
*
|
|
* Backport functionality introduced in Linux 3.3.
|
|
*/
|
|
|
|
#include <linux/kernel.h>
|
|
#include <linux/version.h>
|
|
#include <linux/skbuff.h>
|
|
#include <linux/module.h>
|
|
#include <linux/workqueue.h>
|
|
#include <net/dst.h>
|
|
#include <net/xfrm.h>
|
|
|
|
static void __copy_skb_header(struct sk_buff *new, const struct sk_buff *old)
|
|
{
|
|
new->tstamp = old->tstamp;
|
|
new->dev = old->dev;
|
|
new->transport_header = old->transport_header;
|
|
new->network_header = old->network_header;
|
|
new->mac_header = old->mac_header;
|
|
skb_dst_copy(new, old);
|
|
new->rxhash = old->rxhash;
|
|
#if LINUX_VERSION_IS_GEQ(3,1,0)
|
|
new->ooo_okay = old->ooo_okay;
|
|
#endif
|
|
#if LINUX_VERSION_IS_GEQ(3,2,0)
|
|
new->l4_rxhash = old->l4_rxhash;
|
|
#endif
|
|
#ifdef CONFIG_XFRM
|
|
new->sp = secpath_get(old->sp);
|
|
#endif
|
|
memcpy(new->cb, old->cb, sizeof(old->cb));
|
|
new->csum = old->csum;
|
|
new->local_df = old->local_df;
|
|
new->pkt_type = old->pkt_type;
|
|
new->ip_summed = old->ip_summed;
|
|
skb_copy_queue_mapping(new, old);
|
|
new->priority = old->priority;
|
|
#if IS_ENABLED(CONFIG_IP_VS)
|
|
new->ipvs_property = old->ipvs_property;
|
|
#endif
|
|
new->protocol = old->protocol;
|
|
new->mark = old->mark;
|
|
new->skb_iif = old->skb_iif;
|
|
__nf_copy(new, old);
|
|
#if IS_ENABLED(CONFIG_NETFILTER_XT_TARGET_TRACE)
|
|
new->nf_trace = old->nf_trace;
|
|
#endif
|
|
#ifdef CONFIG_NET_SCHED
|
|
new->tc_index = old->tc_index;
|
|
#ifdef CONFIG_NET_CLS_ACT
|
|
new->tc_verd = old->tc_verd;
|
|
#endif
|
|
#endif
|
|
new->vlan_tci = old->vlan_tci;
|
|
|
|
skb_copy_secmark(new, old);
|
|
}
|
|
|
|
static void copy_skb_header(struct sk_buff *new, const struct sk_buff *old)
|
|
{
|
|
#ifndef NET_SKBUFF_DATA_USES_OFFSET
|
|
/*
|
|
* Shift between the two data areas in bytes
|
|
*/
|
|
unsigned long offset = new->data - old->data;
|
|
#endif
|
|
|
|
__copy_skb_header(new, old);
|
|
|
|
#ifndef NET_SKBUFF_DATA_USES_OFFSET
|
|
/* {transport,network,mac}_header are relative to skb->head */
|
|
new->transport_header += offset;
|
|
new->network_header += offset;
|
|
if (skb_mac_header_was_set(new))
|
|
new->mac_header += offset;
|
|
#endif
|
|
skb_shinfo(new)->gso_size = skb_shinfo(old)->gso_size;
|
|
skb_shinfo(new)->gso_segs = skb_shinfo(old)->gso_segs;
|
|
skb_shinfo(new)->gso_type = skb_shinfo(old)->gso_type;
|
|
}
|
|
|
|
static void skb_clone_fraglist(struct sk_buff *skb)
|
|
{
|
|
struct sk_buff *list;
|
|
|
|
skb_walk_frags(skb, list)
|
|
skb_get(list);
|
|
}
|
|
|
|
|
|
/**
|
|
* __pskb_copy - create copy of an sk_buff with private head.
|
|
* @skb: buffer to copy
|
|
* @headroom: headroom of new skb
|
|
* @gfp_mask: allocation priority
|
|
*
|
|
* Make a copy of both an &sk_buff and part of its data, located
|
|
* in header. Fragmented data remain shared. This is used when
|
|
* the caller wishes to modify only header of &sk_buff and needs
|
|
* private copy of the header to alter. Returns %NULL on failure
|
|
* or the pointer to the buffer on success.
|
|
* The returned buffer has a reference count of 1.
|
|
*/
|
|
|
|
struct sk_buff *__pskb_copy(struct sk_buff *skb, int headroom, gfp_t gfp_mask)
|
|
{
|
|
unsigned int size = skb_headlen(skb) + headroom;
|
|
struct sk_buff *n = alloc_skb(size, gfp_mask);
|
|
|
|
if (!n)
|
|
goto out;
|
|
|
|
/* Set the data pointer */
|
|
skb_reserve(n, headroom);
|
|
/* Set the tail pointer and length */
|
|
skb_put(n, skb_headlen(skb));
|
|
/* Copy the bytes */
|
|
skb_copy_from_linear_data(skb, n->data, n->len);
|
|
|
|
n->truesize += skb->data_len;
|
|
n->data_len = skb->data_len;
|
|
n->len = skb->len;
|
|
|
|
if (skb_shinfo(skb)->nr_frags) {
|
|
int i;
|
|
|
|
/*
|
|
* SKBTX_DEV_ZEROCOPY was added on 3.1 as well but requires ubuf
|
|
* stuff added to the skb which we do not have
|
|
*/
|
|
#if 0
|
|
if (skb_shinfo(skb)->tx_flags & SKBTX_DEV_ZEROCOPY) {
|
|
if (skb_copy_ubufs(skb, gfp_mask)) {
|
|
kfree_skb(n);
|
|
n = NULL;
|
|
goto out;
|
|
}
|
|
}
|
|
#endif
|
|
for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
|
|
skb_shinfo(n)->frags[i] = skb_shinfo(skb)->frags[i];
|
|
#if LINUX_VERSION_IS_GEQ(3,2,0)
|
|
skb_frag_ref(skb, i);
|
|
#else
|
|
get_page(skb_shinfo(skb)->frags[i].page);
|
|
#endif
|
|
}
|
|
skb_shinfo(n)->nr_frags = i;
|
|
}
|
|
|
|
if (skb_has_frag_list(skb)) {
|
|
skb_shinfo(n)->frag_list = skb_shinfo(skb)->frag_list;
|
|
skb_clone_fraglist(n);
|
|
}
|
|
|
|
copy_skb_header(n, skb);
|
|
out:
|
|
return n;
|
|
}
|
|
EXPORT_SYMBOL_GPL(__pskb_copy);
|
|
|
|
static DEFINE_SPINLOCK(wq_name_lock);
|
|
static LIST_HEAD(wq_name_list);
|
|
|
|
struct wq_name {
|
|
struct list_head list;
|
|
struct workqueue_struct *wq;
|
|
char name[24];
|
|
};
|
|
|
|
struct workqueue_struct *
|
|
backport_alloc_workqueue(const char *fmt, unsigned int flags,
|
|
int max_active, struct lock_class_key *key,
|
|
const char *lock_name, ...)
|
|
{
|
|
struct workqueue_struct *wq;
|
|
struct wq_name *n = kzalloc(sizeof(*n), GFP_KERNEL);
|
|
va_list args;
|
|
|
|
if (!n)
|
|
return NULL;
|
|
|
|
va_start(args, lock_name);
|
|
vsnprintf(n->name, sizeof(n->name), fmt, args);
|
|
va_end(args);
|
|
|
|
wq = __alloc_workqueue_key(n->name, flags, max_active, key, lock_name);
|
|
if (!wq) {
|
|
kfree(n);
|
|
return NULL;
|
|
}
|
|
|
|
n->wq = wq;
|
|
spin_lock(&wq_name_lock);
|
|
list_add(&n->list, &wq_name_list);
|
|
spin_unlock(&wq_name_lock);
|
|
|
|
return wq;
|
|
}
|
|
EXPORT_SYMBOL_GPL(backport_alloc_workqueue);
|
|
|
|
void backport_destroy_workqueue(struct workqueue_struct *wq)
|
|
{
|
|
struct wq_name *n, *tmp;
|
|
|
|
/* call original */
|
|
#undef destroy_workqueue
|
|
destroy_workqueue(wq);
|
|
|
|
spin_lock(&wq_name_lock);
|
|
list_for_each_entry_safe(n, tmp, &wq_name_list, list) {
|
|
if (n->wq == wq) {
|
|
list_del(&n->list);
|
|
kfree(n);
|
|
break;
|
|
}
|
|
}
|
|
spin_unlock(&wq_name_lock);
|
|
}
|
|
EXPORT_SYMBOL_GPL(backport_destroy_workqueue);
|