diff options
Diffstat (limited to '3.2.71/1056_linux-3.2.57.patch')
-rw-r--r-- | 3.2.71/1056_linux-3.2.57.patch | 905 |
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 |