summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to '3.2.71/1056_linux-3.2.57.patch')
-rw-r--r--3.2.71/1056_linux-3.2.57.patch905
1 files changed, 905 insertions, 0 deletions
diff --git a/3.2.71/1056_linux-3.2.57.patch b/3.2.71/1056_linux-3.2.57.patch
new file mode 100644
index 0000000..7b8f174
--- /dev/null
+++ b/3.2.71/1056_linux-3.2.57.patch
@@ -0,0 +1,905 @@
+diff --git a/Makefile b/Makefile
+index ec90bfb..c92db9b 100644
+--- a/Makefile
++++ b/Makefile
+@@ -1,6 +1,6 @@
+ VERSION = 3
+ PATCHLEVEL = 2
+-SUBLEVEL = 56
++SUBLEVEL = 57
+ EXTRAVERSION =
+ NAME = Saber-toothed Squirrel
+
+diff --git a/arch/s390/kernel/head64.S b/arch/s390/kernel/head64.S
+index 99348c0..78be245 100644
+--- a/arch/s390/kernel/head64.S
++++ b/arch/s390/kernel/head64.S
+@@ -61,7 +61,7 @@ ENTRY(startup_continue)
+ .quad 0 # cr12: tracing off
+ .quad 0 # cr13: home space segment table
+ .quad 0xc0000000 # cr14: machine check handling off
+- .quad 0 # cr15: linkage stack operations
++ .quad .Llinkage_stack # cr15: linkage stack operations
+ .Lpcmsk:.quad 0x0000000180000000
+ .L4malign:.quad 0xffffffffffc00000
+ .Lscan2g:.quad 0x80000000 + 0x20000 - 8 # 2GB + 128K - 8
+@@ -69,12 +69,15 @@ ENTRY(startup_continue)
+ .Lparmaddr:
+ .quad PARMAREA
+ .align 64
+-.Lduct: .long 0,0,0,0,.Lduald,0,0,0
++.Lduct: .long 0,.Laste,.Laste,0,.Lduald,0,0,0
+ .long 0,0,0,0,0,0,0,0
++.Laste: .quad 0,0xffffffffffffffff,0,0,0,0,0,0
+ .align 128
+ .Lduald:.rept 8
+ .long 0x80000000,0,0,0 # invalid access-list entries
+ .endr
++.Llinkage_stack:
++ .long 0,0,0x89000000,0,0,0,0x8a000000,0
+
+ ENTRY(_ehead)
+
+diff --git a/arch/x86/kvm/mmu.c b/arch/x86/kvm/mmu.c
+index f1b36cf..db2ffef 100644
+--- a/arch/x86/kvm/mmu.c
++++ b/arch/x86/kvm/mmu.c
+@@ -2451,6 +2451,9 @@ static int __direct_map(struct kvm_vcpu *vcpu, gpa_t v, int write,
+ int emulate = 0;
+ gfn_t pseudo_gfn;
+
++ if (!VALID_PAGE(vcpu->arch.mmu.root_hpa))
++ return 0;
++
+ for_each_shadow_entry(vcpu, (u64)gfn << PAGE_SHIFT, iterator) {
+ if (iterator.level == level) {
+ unsigned pte_access = ACC_ALL;
+diff --git a/arch/x86/kvm/vmx.c b/arch/x86/kvm/vmx.c
+index aac5ea7..a4f6bda 100644
+--- a/arch/x86/kvm/vmx.c
++++ b/arch/x86/kvm/vmx.c
+@@ -6273,8 +6273,8 @@ static void vmx_free_vcpu(struct kvm_vcpu *vcpu)
+ struct vcpu_vmx *vmx = to_vmx(vcpu);
+
+ free_vpid(vmx);
+- free_nested(vmx);
+ free_loaded_vmcs(vmx->loaded_vmcs);
++ free_nested(vmx);
+ kfree(vmx->guest_msrs);
+ kvm_vcpu_uninit(vcpu);
+ kmem_cache_free(kvm_vcpu_cache, vmx);
+diff --git a/drivers/input/mouse/synaptics.c b/drivers/input/mouse/synaptics.c
+index 7be5fd9..bc35070 100644
+--- a/drivers/input/mouse/synaptics.c
++++ b/drivers/input/mouse/synaptics.c
+@@ -237,11 +237,22 @@ static int synaptics_identify(struct psmouse *psmouse)
+ * Read touchpad resolution and maximum reported coordinates
+ * Resolution is left zero if touchpad does not support the query
+ */
++
++static const int *quirk_min_max;
++
+ static int synaptics_resolution(struct psmouse *psmouse)
+ {
+ struct synaptics_data *priv = psmouse->private;
+ unsigned char resp[3];
+
++ if (quirk_min_max) {
++ priv->x_min = quirk_min_max[0];
++ priv->x_max = quirk_min_max[1];
++ priv->y_min = quirk_min_max[2];
++ priv->y_max = quirk_min_max[3];
++ return 0;
++ }
++
+ if (SYN_ID_MAJOR(priv->identity) < 4)
+ return 0;
+
+@@ -1364,10 +1375,54 @@ static const struct dmi_system_id __initconst olpc_dmi_table[] = {
+ { }
+ };
+
++static const struct dmi_system_id min_max_dmi_table[] __initconst = {
++#if defined(CONFIG_DMI)
++ {
++ /* Lenovo ThinkPad Helix */
++ .matches = {
++ DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
++ DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkPad Helix"),
++ },
++ .driver_data = (int []){1024, 5052, 2258, 4832},
++ },
++ {
++ /* Lenovo ThinkPad X240 */
++ .matches = {
++ DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
++ DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkPad X240"),
++ },
++ .driver_data = (int []){1232, 5710, 1156, 4696},
++ },
++ {
++ /* Lenovo ThinkPad T440s */
++ .matches = {
++ DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
++ DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkPad T440"),
++ },
++ .driver_data = (int []){1024, 5112, 2024, 4832},
++ },
++ {
++ /* Lenovo ThinkPad T540p */
++ .matches = {
++ DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
++ DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkPad T540"),
++ },
++ .driver_data = (int []){1024, 5056, 2058, 4832},
++ },
++#endif
++ { }
++};
++
+ void __init synaptics_module_init(void)
+ {
++ const struct dmi_system_id *min_max_dmi;
++
+ impaired_toshiba_kbc = dmi_check_system(toshiba_dmi_table);
+ broken_olpc_ec = dmi_check_system(olpc_dmi_table);
++
++ min_max_dmi = dmi_first_match(min_max_dmi_table);
++ if (min_max_dmi)
++ quirk_min_max = min_max_dmi->driver_data;
+ }
+
+ int synaptics_init(struct psmouse *psmouse)
+diff --git a/drivers/net/usb/asix.c b/drivers/net/usb/asix.c
+index 6729585..98ab759 100644
+--- a/drivers/net/usb/asix.c
++++ b/drivers/net/usb/asix.c
+@@ -183,6 +183,17 @@ struct ax88172_int_data {
+ __le16 res3;
+ } __packed;
+
++struct asix_rx_fixup_info {
++ struct sk_buff *ax_skb;
++ u32 header;
++ u16 size;
++ bool split_head;
++};
++
++struct asix_common_private {
++ struct asix_rx_fixup_info rx_fixup_info;
++};
++
+ static int asix_read_cmd(struct usbnet *dev, u8 cmd, u16 value, u16 index,
+ u16 size, void *data)
+ {
+@@ -304,97 +315,89 @@ asix_write_cmd_async(struct usbnet *dev, u8 cmd, u16 value, u16 index,
+ }
+ }
+
+-static int asix_rx_fixup(struct usbnet *dev, struct sk_buff *skb)
++static int asix_rx_fixup_internal(struct usbnet *dev, struct sk_buff *skb,
++ struct asix_rx_fixup_info *rx)
+ {
+- u8 *head;
+- u32 header;
+- char *packet;
+- struct sk_buff *ax_skb;
+- u16 size;
++ int offset = 0;
++
++ while (offset + sizeof(u16) <= skb->len) {
++ u16 remaining = 0;
++ unsigned char *data;
++
++ if (!rx->size) {
++ if ((skb->len - offset == sizeof(u16)) ||
++ rx->split_head) {
++ if(!rx->split_head) {
++ rx->header = get_unaligned_le16(
++ skb->data + offset);
++ rx->split_head = true;
++ offset += sizeof(u16);
++ break;
++ } else {
++ rx->header |= (get_unaligned_le16(
++ skb->data + offset)
++ << 16);
++ rx->split_head = false;
++ offset += sizeof(u16);
++ }
++ } else {
++ rx->header = get_unaligned_le32(skb->data +
++ offset);
++ offset += sizeof(u32);
++ }
+
+- head = (u8 *) skb->data;
+- memcpy(&header, head, sizeof(header));
+- le32_to_cpus(&header);
+- packet = head + sizeof(header);
+-
+- skb_pull(skb, 4);
+-
+- while (skb->len > 0) {
+- if ((header & 0x07ff) != ((~header >> 16) & 0x07ff))
+- netdev_err(dev->net, "asix_rx_fixup() Bad Header Length\n");
+-
+- /* get the packet length */
+- size = (u16) (header & 0x000007ff);
+-
+- if ((skb->len) - ((size + 1) & 0xfffe) == 0) {
+- u8 alignment = (unsigned long)skb->data & 0x3;
+- if (alignment != 0x2) {
+- /*
+- * not 16bit aligned so use the room provided by
+- * the 32 bit header to align the data
+- *
+- * note we want 16bit alignment as MAC header is
+- * 14bytes thus ip header will be aligned on
+- * 32bit boundary so accessing ipheader elements
+- * using a cast to struct ip header wont cause
+- * an unaligned accesses.
+- */
+- u8 realignment = (alignment + 2) & 0x3;
+- memmove(skb->data - realignment,
+- skb->data,
+- size);
+- skb->data -= realignment;
+- skb_set_tail_pointer(skb, size);
++ /* get the packet length */
++ rx->size = (u16) (rx->header & 0x7ff);
++ if (rx->size != ((~rx->header >> 16) & 0x7ff)) {
++ netdev_err(dev->net, "asix_rx_fixup() Bad Header Length 0x%x, offset %d\n",
++ rx->header, offset);
++ rx->size = 0;
++ return 0;
+ }
+- return 2;
++ rx->ax_skb = netdev_alloc_skb_ip_align(dev->net,
++ rx->size);
++ if (!rx->ax_skb)
++ return 0;
+ }
+
+- if (size > dev->net->mtu + ETH_HLEN + VLAN_HLEN) {
++ if (rx->size > dev->net->mtu + ETH_HLEN + VLAN_HLEN) {
+ netdev_err(dev->net, "asix_rx_fixup() Bad RX Length %d\n",
+- size);
+- return 0;
+- }
+- ax_skb = skb_clone(skb, GFP_ATOMIC);
+- if (ax_skb) {
+- u8 alignment = (unsigned long)packet & 0x3;
+- ax_skb->len = size;
+-
+- if (alignment != 0x2) {
+- /*
+- * not 16bit aligned use the room provided by
+- * the 32 bit header to align the data
+- */
+- u8 realignment = (alignment + 2) & 0x3;
+- memmove(packet - realignment, packet, size);
+- packet -= realignment;
+- }
+- ax_skb->data = packet;
+- skb_set_tail_pointer(ax_skb, size);
+- usbnet_skb_return(dev, ax_skb);
+- } else {
++ rx->size);
++ kfree_skb(rx->ax_skb);
+ return 0;
+ }
+
+- skb_pull(skb, (size + 1) & 0xfffe);
++ if (rx->size > skb->len - offset) {
++ remaining = rx->size - (skb->len - offset);
++ rx->size = skb->len - offset;
++ }
+
+- if (skb->len < sizeof(header))
+- break;
++ data = skb_put(rx->ax_skb, rx->size);
++ memcpy(data, skb->data + offset, rx->size);
++ if (!remaining)
++ usbnet_skb_return(dev, rx->ax_skb);
+
+- head = (u8 *) skb->data;
+- memcpy(&header, head, sizeof(header));
+- le32_to_cpus(&header);
+- packet = head + sizeof(header);
+- skb_pull(skb, 4);
++ offset += (rx->size + 1) & 0xfffe;
++ rx->size = remaining;
+ }
+
+- if (skb->len < 0) {
+- netdev_err(dev->net, "asix_rx_fixup() Bad SKB Length %d\n",
+- skb->len);
++ if (skb->len != offset) {
++ netdev_err(dev->net, "asix_rx_fixup() Bad SKB Length %d, %d\n",
++ skb->len, offset);
+ return 0;
+ }
++
+ return 1;
+ }
+
++static int asix_rx_fixup_common(struct usbnet *dev, struct sk_buff *skb)
++{
++ struct asix_common_private *dp = dev->driver_priv;
++ struct asix_rx_fixup_info *rx = &dp->rx_fixup_info;
++
++ return asix_rx_fixup_internal(dev, skb, rx);
++}
++
+ static struct sk_buff *asix_tx_fixup(struct usbnet *dev, struct sk_buff *skb,
+ gfp_t flags)
+ {
+@@ -1154,9 +1157,19 @@ static int ax88772_bind(struct usbnet *dev, struct usb_interface *intf)
+ dev->rx_urb_size = 2048;
+ }
+
++ dev->driver_priv = kzalloc(sizeof(struct asix_common_private), GFP_KERNEL);
++ if (!dev->driver_priv)
++ return -ENOMEM;
++
+ return 0;
+ }
+
++static void ax88772_unbind(struct usbnet *dev, struct usb_interface *intf)
++{
++ if (dev->driver_priv)
++ kfree(dev->driver_priv);
++}
++
+ static struct ethtool_ops ax88178_ethtool_ops = {
+ .get_drvinfo = asix_get_drvinfo,
+ .get_link = asix_get_link,
+@@ -1489,6 +1502,10 @@ static int ax88178_bind(struct usbnet *dev, struct usb_interface *intf)
+ dev->rx_urb_size = 2048;
+ }
+
++ dev->driver_priv = kzalloc(sizeof(struct asix_common_private), GFP_KERNEL);
++ if (!dev->driver_priv)
++ return -ENOMEM;
++
+ return 0;
+ }
+
+@@ -1535,22 +1552,25 @@ static const struct driver_info hawking_uf200_info = {
+ static const struct driver_info ax88772_info = {
+ .description = "ASIX AX88772 USB 2.0 Ethernet",
+ .bind = ax88772_bind,
++ .unbind = ax88772_unbind,
+ .status = asix_status,
+ .link_reset = ax88772_link_reset,
+ .reset = ax88772_reset,
+- .flags = FLAG_ETHER | FLAG_FRAMING_AX | FLAG_LINK_INTR,
+- .rx_fixup = asix_rx_fixup,
++ .flags = FLAG_ETHER | FLAG_FRAMING_AX | FLAG_LINK_INTR | FLAG_MULTI_PACKET,
++ .rx_fixup = asix_rx_fixup_common,
+ .tx_fixup = asix_tx_fixup,
+ };
+
+ static const struct driver_info ax88178_info = {
+ .description = "ASIX AX88178 USB 2.0 Ethernet",
+ .bind = ax88178_bind,
++ .unbind = ax88772_unbind,
+ .status = asix_status,
+ .link_reset = ax88178_link_reset,
+ .reset = ax88178_reset,
+- .flags = FLAG_ETHER | FLAG_FRAMING_AX | FLAG_LINK_INTR,
+- .rx_fixup = asix_rx_fixup,
++ .flags = FLAG_ETHER | FLAG_FRAMING_AX | FLAG_LINK_INTR |
++ FLAG_MULTI_PACKET,
++ .rx_fixup = asix_rx_fixup_common,
+ .tx_fixup = asix_tx_fixup,
+ };
+
+diff --git a/drivers/staging/speakup/kobjects.c b/drivers/staging/speakup/kobjects.c
+index 07a7f54..6829195 100644
+--- a/drivers/staging/speakup/kobjects.c
++++ b/drivers/staging/speakup/kobjects.c
+@@ -521,9 +521,9 @@ static ssize_t punc_store(struct kobject *kobj, struct kobj_attribute *attr,
+ spk_lock(flags);
+
+ if (*punc_buf == 'd' || *punc_buf == 'r')
+- x = set_mask_bits(0, var->value, 3);
++ x = spk_set_mask_bits(0, var->value, 3);
+ else
+- x = set_mask_bits(punc_buf, var->value, 3);
++ x = spk_set_mask_bits(punc_buf, var->value, 3);
+
+ spk_unlock(flags);
+ return count;
+diff --git a/drivers/staging/speakup/main.c b/drivers/staging/speakup/main.c
+index 0d70f68..a076351 100644
+--- a/drivers/staging/speakup/main.c
++++ b/drivers/staging/speakup/main.c
+@@ -2265,7 +2265,7 @@ static int __init speakup_init(void)
+ (var->var_id >= 0) && (var->var_id < MAXVARS); var++)
+ speakup_register_var(var);
+ for (i = 1; punc_info[i].mask != 0; i++)
+- set_mask_bits(0, i, 2);
++ spk_set_mask_bits(0, i, 2);
+
+ set_key_info(key_defaults, key_buf);
+ if (quiet_boot)
+diff --git a/drivers/staging/speakup/speakup.h b/drivers/staging/speakup/speakup.h
+index 412b879..f39c0a2 100644
+--- a/drivers/staging/speakup/speakup.h
++++ b/drivers/staging/speakup/speakup.h
+@@ -71,7 +71,7 @@ extern struct st_var_header *var_header_by_name(const char *name);
+ extern struct punc_var_t *get_punc_var(enum var_id_t var_id);
+ extern int set_num_var(int val, struct st_var_header *var, int how);
+ extern int set_string_var(const char *page, struct st_var_header *var, int len);
+-extern int set_mask_bits(const char *input, const int which, const int how);
++extern int spk_set_mask_bits(const char *input, const int which, const int how);
+ extern special_func special_handler;
+ extern int handle_help(struct vc_data *vc, u_char type, u_char ch, u_short key);
+ extern int synth_init(char *name);
+diff --git a/drivers/staging/speakup/varhandlers.c b/drivers/staging/speakup/varhandlers.c
+index ab7de93..75eaf27 100644
+--- a/drivers/staging/speakup/varhandlers.c
++++ b/drivers/staging/speakup/varhandlers.c
+@@ -267,11 +267,11 @@ int set_string_var(const char *page, struct st_var_header *var, int len)
+ return ret;
+ }
+
+-/* set_mask_bits sets or clears the punc/delim/repeat bits,
++/* spk_set_mask_bits sets or clears the punc/delim/repeat bits,
+ * if input is null uses the defaults.
+ * values for how: 0 clears bits of chars supplied,
+ * 1 clears allk, 2 sets bits for chars */
+-int set_mask_bits(const char *input, const int which, const int how)
++int spk_set_mask_bits(const char *input, const int which, const int how)
+ {
+ u_char *cp;
+ short mask = punc_info[which].mask;
+diff --git a/fs/cifs/file.c b/fs/cifs/file.c
+index c55808e..aa05d5e 100644
+--- a/fs/cifs/file.c
++++ b/fs/cifs/file.c
+@@ -2107,7 +2107,7 @@ cifs_iovec_write(struct file *file, const struct iovec *iov,
+ {
+ unsigned int written;
+ unsigned long num_pages, npages, i;
+- size_t copied, len, cur_len;
++ size_t bytes, copied, len, cur_len;
+ ssize_t total_written = 0;
+ struct kvec *to_send;
+ struct page **pages;
+@@ -2165,17 +2165,45 @@ cifs_iovec_write(struct file *file, const struct iovec *iov,
+ do {
+ size_t save_len = cur_len;
+ for (i = 0; i < npages; i++) {
+- copied = min_t(const size_t, cur_len, PAGE_CACHE_SIZE);
++ bytes = min_t(const size_t, cur_len, PAGE_CACHE_SIZE);
+ copied = iov_iter_copy_from_user(pages[i], &it, 0,
+- copied);
++ bytes);
+ cur_len -= copied;
+ iov_iter_advance(&it, copied);
+ to_send[i+1].iov_base = kmap(pages[i]);
+ to_send[i+1].iov_len = copied;
++ /*
++ * If we didn't copy as much as we expected, then that
++ * may mean we trod into an unmapped area. Stop copying
++ * at that point. On the next pass through the big
++ * loop, we'll likely end up getting a zero-length
++ * write and bailing out of it.
++ */
++ if (copied < bytes)
++ break;
+ }
+
+ cur_len = save_len - cur_len;
+
++ /*
++ * If we have no data to send, then that probably means that
++ * the copy above failed altogether. That's most likely because
++ * the address in the iovec was bogus. Set the rc to -EFAULT,
++ * free anything we allocated and bail out.
++ */
++ if (!cur_len) {
++ kunmap(pages[0]);
++ if (!total_written)
++ total_written = -EFAULT;
++ break;
++ }
++
++ /*
++ * i + 1 now represents the number of pages we actually used in
++ * the copy phase above.
++ */
++ npages = min(npages, i + 1);
++
+ do {
+ if (open_file->invalidHandle) {
+ rc = cifs_reopen_file(open_file, false);
+diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c
+index 45778a6..dc9f0ec 100644
+--- a/fs/ext4/inode.c
++++ b/fs/ext4/inode.c
+@@ -38,6 +38,7 @@
+ #include <linux/printk.h>
+ #include <linux/slab.h>
+ #include <linux/ratelimit.h>
++#include <linux/bitops.h>
+
+ #include "ext4_jbd2.h"
+ #include "xattr.h"
+@@ -3694,18 +3695,20 @@ int ext4_get_inode_loc(struct inode *inode, struct ext4_iloc *iloc)
+ void ext4_set_inode_flags(struct inode *inode)
+ {
+ unsigned int flags = EXT4_I(inode)->i_flags;
++ unsigned int new_fl = 0;
+
+- inode->i_flags &= ~(S_SYNC|S_APPEND|S_IMMUTABLE|S_NOATIME|S_DIRSYNC);
+ if (flags & EXT4_SYNC_FL)
+- inode->i_flags |= S_SYNC;
++ new_fl |= S_SYNC;
+ if (flags & EXT4_APPEND_FL)
+- inode->i_flags |= S_APPEND;
++ new_fl |= S_APPEND;
+ if (flags & EXT4_IMMUTABLE_FL)
+- inode->i_flags |= S_IMMUTABLE;
++ new_fl |= S_IMMUTABLE;
+ if (flags & EXT4_NOATIME_FL)
+- inode->i_flags |= S_NOATIME;
++ new_fl |= S_NOATIME;
+ if (flags & EXT4_DIRSYNC_FL)
+- inode->i_flags |= S_DIRSYNC;
++ new_fl |= S_DIRSYNC;
++ set_mask_bits(&inode->i_flags,
++ S_SYNC|S_APPEND|S_IMMUTABLE|S_NOATIME|S_DIRSYNC, new_fl);
+ }
+
+ /* Propagate flags from i_flags to EXT4_I(inode)->i_flags */
+diff --git a/include/linux/bitops.h b/include/linux/bitops.h
+index fc8a3ff..87a375f 100644
+--- a/include/linux/bitops.h
++++ b/include/linux/bitops.h
+@@ -168,6 +168,21 @@ static inline unsigned long __ffs64(u64 word)
+
+ #ifdef __KERNEL__
+
++#ifndef set_mask_bits
++#define set_mask_bits(ptr, _mask, _bits) \
++({ \
++ const typeof(*ptr) mask = (_mask), bits = (_bits); \
++ typeof(*ptr) old, new; \
++ \
++ do { \
++ old = ACCESS_ONCE(*ptr); \
++ new = (old & ~mask) | bits; \
++ } while (cmpxchg(ptr, old, new) != old); \
++ \
++ new; \
++})
++#endif
++
+ #ifndef find_last_bit
+ /**
+ * find_last_bit - find the last set bit in a memory region
+diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h
+index 85180bf..13bd6d0 100644
+--- a/include/linux/skbuff.h
++++ b/include/linux/skbuff.h
+@@ -2143,6 +2143,8 @@ extern int skb_shift(struct sk_buff *tgt, struct sk_buff *skb,
+
+ extern struct sk_buff *skb_segment(struct sk_buff *skb, u32 features);
+
++unsigned int skb_gso_transport_seglen(const struct sk_buff *skb);
++
+ static inline void *skb_header_pointer(const struct sk_buff *skb, int offset,
+ int len, void *buffer)
+ {
+@@ -2555,5 +2557,22 @@ static inline bool skb_is_recycleable(const struct sk_buff *skb, int skb_size)
+
+ return true;
+ }
++
++/**
++ * skb_gso_network_seglen - Return length of individual segments of a gso packet
++ *
++ * @skb: GSO skb
++ *
++ * skb_gso_network_seglen is used to determine the real size of the
++ * individual segments, including Layer3 (IP, IPv6) and L4 headers (TCP/UDP).
++ *
++ * The MAC/L2 header is not accounted for.
++ */
++static inline unsigned int skb_gso_network_seglen(const struct sk_buff *skb)
++{
++ unsigned int hdr_len = skb_transport_header(skb) -
++ skb_network_header(skb);
++ return hdr_len + skb_gso_transport_seglen(skb);
++}
+ #endif /* __KERNEL__ */
+ #endif /* _LINUX_SKBUFF_H */
+diff --git a/ipc/msg.c b/ipc/msg.c
+index 7385de2..25f1a61 100644
+--- a/ipc/msg.c
++++ b/ipc/msg.c
+@@ -296,7 +296,9 @@ static void freeque(struct ipc_namespace *ns, struct kern_ipc_perm *ipcp)
+ }
+ atomic_sub(msq->q_cbytes, &ns->msg_bytes);
+ security_msg_queue_free(msq);
++ ipc_lock_by_ptr(&msq->q_perm);
+ ipc_rcu_putref(msq);
++ ipc_unlock(&msq->q_perm);
+ }
+
+ /*
+diff --git a/net/core/skbuff.c b/net/core/skbuff.c
+index 5d6cb54..8ac4a0f 100644
+--- a/net/core/skbuff.c
++++ b/net/core/skbuff.c
+@@ -45,6 +45,8 @@
+ #include <linux/in.h>
+ #include <linux/inet.h>
+ #include <linux/slab.h>
++#include <linux/tcp.h>
++#include <linux/udp.h>
+ #include <linux/netdevice.h>
+ #ifdef CONFIG_NET_CLS_ACT
+ #include <net/pkt_sched.h>
+@@ -3181,3 +3183,26 @@ void __skb_warn_lro_forwarding(const struct sk_buff *skb)
+ " while LRO is enabled\n", skb->dev->name);
+ }
+ EXPORT_SYMBOL(__skb_warn_lro_forwarding);
++
++/**
++ * skb_gso_transport_seglen - Return length of individual segments of a gso packet
++ *
++ * @skb: GSO skb
++ *
++ * skb_gso_transport_seglen is used to determine the real size of the
++ * individual segments, including Layer4 headers (TCP/UDP).
++ *
++ * The MAC/L2 or network (IP, IPv6) headers are not accounted for.
++ */
++unsigned int skb_gso_transport_seglen(const struct sk_buff *skb)
++{
++ const struct skb_shared_info *shinfo = skb_shinfo(skb);
++ unsigned int hdr_len;
++
++ if (likely(shinfo->gso_type & (SKB_GSO_TCPV4 | SKB_GSO_TCPV6)))
++ hdr_len = tcp_hdrlen(skb);
++ else
++ hdr_len = sizeof(struct udphdr);
++ return hdr_len + shinfo->gso_size;
++}
++EXPORT_SYMBOL_GPL(skb_gso_transport_seglen);
+diff --git a/net/ipv4/ip_forward.c b/net/ipv4/ip_forward.c
+index 29a07b6..e0d9f02 100644
+--- a/net/ipv4/ip_forward.c
++++ b/net/ipv4/ip_forward.c
+@@ -39,6 +39,68 @@
+ #include <net/route.h>
+ #include <net/xfrm.h>
+
++static bool ip_may_fragment(const struct sk_buff *skb)
++{
++ return unlikely((ip_hdr(skb)->frag_off & htons(IP_DF)) == 0) ||
++ !skb->local_df;
++}
++
++static bool ip_exceeds_mtu(const struct sk_buff *skb, unsigned int mtu)
++{
++ if (skb->len <= mtu || skb->local_df)
++ return false;
++
++ if (skb_is_gso(skb) && skb_gso_network_seglen(skb) <= mtu)
++ return false;
++
++ return true;
++}
++
++static bool ip_gso_exceeds_dst_mtu(const struct sk_buff *skb)
++{
++ unsigned int mtu;
++
++ if (skb->local_df || !skb_is_gso(skb))
++ return false;
++
++ mtu = dst_mtu(skb_dst(skb));
++
++ /* if seglen > mtu, do software segmentation for IP fragmentation on
++ * output. DF bit cannot be set since ip_forward would have sent
++ * icmp error.
++ */
++ return skb_gso_network_seglen(skb) > mtu;
++}
++
++/* called if GSO skb needs to be fragmented on forward */
++static int ip_forward_finish_gso(struct sk_buff *skb)
++{
++ struct sk_buff *segs;
++ int ret = 0;
++
++ segs = skb_gso_segment(skb, 0);
++ if (IS_ERR(segs)) {
++ kfree_skb(skb);
++ return -ENOMEM;
++ }
++
++ consume_skb(skb);
++
++ do {
++ struct sk_buff *nskb = segs->next;
++ int err;
++
++ segs->next = NULL;
++ err = dst_output(segs);
++
++ if (err && ret == 0)
++ ret = err;
++ segs = nskb;
++ } while (segs);
++
++ return ret;
++}
++
+ static int ip_forward_finish(struct sk_buff *skb)
+ {
+ struct ip_options * opt = &(IPCB(skb)->opt);
+@@ -48,6 +110,9 @@ static int ip_forward_finish(struct sk_buff *skb)
+ if (unlikely(opt->optlen))
+ ip_forward_options(skb);
+
++ if (ip_gso_exceeds_dst_mtu(skb))
++ return ip_forward_finish_gso(skb);
++
+ return dst_output(skb);
+ }
+
+@@ -87,8 +152,7 @@ int ip_forward(struct sk_buff *skb)
+ if (opt->is_strictroute && opt->nexthop != rt->rt_gateway)
+ goto sr_failed;
+
+- if (unlikely(skb->len > dst_mtu(&rt->dst) && !skb_is_gso(skb) &&
+- (ip_hdr(skb)->frag_off & htons(IP_DF))) && !skb->local_df) {
++ if (!ip_may_fragment(skb) && ip_exceeds_mtu(skb, dst_mtu(&rt->dst))) {
+ IP_INC_STATS(dev_net(rt->dst.dev), IPSTATS_MIB_FRAGFAILS);
+ icmp_send(skb, ICMP_DEST_UNREACH, ICMP_FRAG_NEEDED,
+ htonl(dst_mtu(&rt->dst)));
+diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c
+index d3fde7e..cd4b529 100644
+--- a/net/ipv6/ip6_output.c
++++ b/net/ipv6/ip6_output.c
+@@ -381,6 +381,17 @@ static inline int ip6_forward_finish(struct sk_buff *skb)
+ return dst_output(skb);
+ }
+
++static bool ip6_pkt_too_big(const struct sk_buff *skb, unsigned int mtu)
++{
++ if (skb->len <= mtu || skb->local_df)
++ return false;
++
++ if (skb_is_gso(skb) && skb_gso_network_seglen(skb) <= mtu)
++ return false;
++
++ return true;
++}
++
+ int ip6_forward(struct sk_buff *skb)
+ {
+ struct dst_entry *dst = skb_dst(skb);
+@@ -504,7 +515,7 @@ int ip6_forward(struct sk_buff *skb)
+ if (mtu < IPV6_MIN_MTU)
+ mtu = IPV6_MIN_MTU;
+
+- if (skb->len > mtu && !skb_is_gso(skb)) {
++ if (ip6_pkt_too_big(skb, mtu)) {
+ /* Again, force OUTPUT device used as source address */
+ skb->dev = dst->dev;
+ icmpv6_send(skb, ICMPV6_PKT_TOOBIG, 0, mtu);
+diff --git a/net/netfilter/nf_conntrack_proto_dccp.c b/net/netfilter/nf_conntrack_proto_dccp.c
+index 2e664a6..8aa94ee 100644
+--- a/net/netfilter/nf_conntrack_proto_dccp.c
++++ b/net/netfilter/nf_conntrack_proto_dccp.c
+@@ -431,7 +431,7 @@ static bool dccp_new(struct nf_conn *ct, const struct sk_buff *skb,
+ const char *msg;
+ u_int8_t state;
+
+- dh = skb_header_pointer(skb, dataoff, sizeof(_dh), &dh);
++ dh = skb_header_pointer(skb, dataoff, sizeof(_dh), &_dh);
+ BUG_ON(dh == NULL);
+
+ state = dccp_state_table[CT_DCCP_ROLE_CLIENT][dh->dccph_type][CT_DCCP_NONE];
+@@ -483,7 +483,7 @@ static int dccp_packet(struct nf_conn *ct, const struct sk_buff *skb,
+ u_int8_t type, old_state, new_state;
+ enum ct_dccp_roles role;
+
+- dh = skb_header_pointer(skb, dataoff, sizeof(_dh), &dh);
++ dh = skb_header_pointer(skb, dataoff, sizeof(_dh), &_dh);
+ BUG_ON(dh == NULL);
+ type = dh->dccph_type;
+
+@@ -575,7 +575,7 @@ static int dccp_error(struct net *net, struct nf_conn *tmpl,
+ unsigned int cscov;
+ const char *msg;
+
+- dh = skb_header_pointer(skb, dataoff, sizeof(_dh), &dh);
++ dh = skb_header_pointer(skb, dataoff, sizeof(_dh), &_dh);
+ if (dh == NULL) {
+ msg = "nf_ct_dccp: short packet ";
+ goto out_invalid;
+diff --git a/scripts/package/builddeb b/scripts/package/builddeb
+index 3c6c0b1..bee55f6 100644
+--- a/scripts/package/builddeb
++++ b/scripts/package/builddeb
+@@ -41,9 +41,9 @@ create_package() {
+ parisc*)
+ debarch=hppa ;;
+ mips*)
+- debarch=mips$(grep -q CPU_LITTLE_ENDIAN=y .config && echo el) ;;
++ debarch=mips$(grep -q CPU_LITTLE_ENDIAN=y $KCONFIG_CONFIG && echo el || true) ;;
+ arm*)
+- debarch=arm$(grep -q CONFIG_AEABI=y .config && echo el) ;;
++ debarch=arm$(grep -q CONFIG_AEABI=y $KCONFIG_CONFIG && echo el || true) ;;
+ *)
+ echo "" >&2
+ echo "** ** ** WARNING ** ** **" >&2
+@@ -62,7 +62,7 @@ create_package() {
+ fi
+
+ # Create the package
+- dpkg-gencontrol -isp $forcearch -p$pname -P"$pdir"
++ dpkg-gencontrol -isp $forcearch -Vkernel:debarch="${debarch:-$(dpkg --print-architecture)}" -p$pname -P"$pdir"
+ dpkg --build "$pdir" ..
+ }
+
+@@ -105,12 +105,12 @@ fi
+ if [ "$ARCH" = "um" ] ; then
+ $MAKE linux
+ cp System.map "$tmpdir/usr/lib/uml/modules/$version/System.map"
+- cp .config "$tmpdir/usr/share/doc/$packagename/config"
++ cp $KCONFIG_CONFIG "$tmpdir/usr/share/doc/$packagename/config"
+ gzip "$tmpdir/usr/share/doc/$packagename/config"
+ cp $KBUILD_IMAGE "$tmpdir/usr/bin/linux-$version"
+ else
+ cp System.map "$tmpdir/boot/System.map-$version"
+- cp .config "$tmpdir/boot/config-$version"
++ cp $KCONFIG_CONFIG "$tmpdir/boot/config-$version"
+ # Not all arches include the boot path in KBUILD_IMAGE
+ if [ -e $KBUILD_IMAGE ]; then
+ cp $KBUILD_IMAGE "$tmpdir/boot/vmlinuz-$version"
+@@ -119,7 +119,7 @@ else
+ fi
+ fi
+
+-if grep -q '^CONFIG_MODULES=y' .config ; then
++if grep -q '^CONFIG_MODULES=y' $KCONFIG_CONFIG ; then
+ INSTALL_MOD_PATH="$tmpdir" make KBUILD_SRC= modules_install
+ if [ "$ARCH" = "um" ] ; then
+ mv "$tmpdir/lib/modules/$version"/* "$tmpdir/usr/lib/uml/modules/$version/"
+@@ -240,21 +240,21 @@ fi
+ # Build header package
+ (cd $srctree; find . -name Makefile -o -name Kconfig\* -o -name \*.pl > "$objtree/debian/hdrsrcfiles")
+ (cd $srctree; find arch/$SRCARCH/include include scripts -type f >> "$objtree/debian/hdrsrcfiles")
+-(cd $objtree; find .config Module.symvers include scripts -type f >> "$objtree/debian/hdrobjfiles")
++(cd $objtree; find Module.symvers include scripts -type f >> "$objtree/debian/hdrobjfiles")
+ destdir=$kernel_headers_dir/usr/src/linux-headers-$version
+ mkdir -p "$destdir"
+ (cd $srctree; tar -c -f - -T "$objtree/debian/hdrsrcfiles") | (cd $destdir; tar -xf -)
+ (cd $objtree; tar -c -f - -T "$objtree/debian/hdrobjfiles") | (cd $destdir; tar -xf -)
++(cd $objtree; cp $KCONFIG_CONFIG $destdir/.config) # copy .config manually to be where it's expected to be
+ rm -f "$objtree/debian/hdrsrcfiles" "$objtree/debian/hdrobjfiles"
+-arch=$(dpkg --print-architecture)
+
+ cat <<EOF >> debian/control
+
+ Package: $kernel_headers_packagename
+ Provides: linux-headers, linux-headers-2.6
+-Architecture: $arch
+-Description: Linux kernel headers for $KERNELRELEASE on $arch
+- This package provides kernel header files for $KERNELRELEASE on $arch
++Architecture: any
++Description: Linux kernel headers for $KERNELRELEASE on \${kernel:debarch}
++ This package provides kernel header files for $KERNELRELEASE on \${kernel:debarch}
+ .
+ This is useful for people who need to build external modules
+ EOF