diff options
author | Anthony G. Basile <blueness@gentoo.org> | 2011-11-05 17:22:43 -0400 |
---|---|---|
committer | Anthony G. Basile <blueness@gentoo.org> | 2011-11-05 17:22:43 -0400 |
commit | f9b8c98db5bdb4fc65048206c32cf80aa061068b (patch) | |
tree | f0431d58907cf1157098a8363d5dfb4c9fb686eb | |
parent | Update RSBAC patch to 3.0.8 from upstream (diff) | |
download | hardened-patchset-f9b8c98db5bdb4fc65048206c32cf80aa061068b.tar.gz hardened-patchset-f9b8c98db5bdb4fc65048206c32cf80aa061068b.tar.bz2 hardened-patchset-f9b8c98db5bdb4fc65048206c32cf80aa061068b.zip |
Add missing patch for linux-3.0.8
-rw-r--r-- | 3.0.8/0000_README | 4 | ||||
-rw-r--r-- | 3.0.8/1007_linux-3.0.8.patch | 1472 |
2 files changed, 1476 insertions, 0 deletions
diff --git a/3.0.8/0000_README b/3.0.8/0000_README index 42eee1c..9cfe0c0 100644 --- a/3.0.8/0000_README +++ b/3.0.8/0000_README @@ -3,6 +3,10 @@ README Individual Patch Descriptions: ----------------------------------------------------------------------------- +Patch: 1007_linux-3.0.8.patch +From: http://www.kernel.org +Desc: Linux 3.0.8 + Patch: 4420_grsecurity-2.2.2-3.0.8-201110250925.patch From: http://www.grsecurity.net Desc: hardened-sources base patch from upstream grsecurity diff --git a/3.0.8/1007_linux-3.0.8.patch b/3.0.8/1007_linux-3.0.8.patch new file mode 100644 index 0000000..62a4bb6 --- /dev/null +++ b/3.0.8/1007_linux-3.0.8.patch @@ -0,0 +1,1472 @@ +diff --git a/Makefile b/Makefile +index 11c4249..9f6e3cd 100644 +--- a/Makefile ++++ b/Makefile +@@ -1,6 +1,6 @@ + VERSION = 3 + PATCHLEVEL = 0 +-SUBLEVEL = 7 ++SUBLEVEL = 8 + EXTRAVERSION = + NAME = Sneaky Weasel + +diff --git a/arch/arm/kernel/perf_event_v7.c b/arch/arm/kernel/perf_event_v7.c +index 4960686..4372763 100644 +--- a/arch/arm/kernel/perf_event_v7.c ++++ b/arch/arm/kernel/perf_event_v7.c +@@ -264,8 +264,8 @@ static const unsigned armv7_a9_perf_map[PERF_COUNT_HW_MAX] = { + [PERF_COUNT_HW_CPU_CYCLES] = ARMV7_PERFCTR_CPU_CYCLES, + [PERF_COUNT_HW_INSTRUCTIONS] = + ARMV7_PERFCTR_INST_OUT_OF_RENAME_STAGE, +- [PERF_COUNT_HW_CACHE_REFERENCES] = ARMV7_PERFCTR_COHERENT_LINE_HIT, +- [PERF_COUNT_HW_CACHE_MISSES] = ARMV7_PERFCTR_COHERENT_LINE_MISS, ++ [PERF_COUNT_HW_CACHE_REFERENCES] = ARMV7_PERFCTR_DCACHE_ACCESS, ++ [PERF_COUNT_HW_CACHE_MISSES] = ARMV7_PERFCTR_DCACHE_REFILL, + [PERF_COUNT_HW_BRANCH_INSTRUCTIONS] = ARMV7_PERFCTR_PC_WRITE, + [PERF_COUNT_HW_BRANCH_MISSES] = ARMV7_PERFCTR_PC_BRANCH_MIS_PRED, + [PERF_COUNT_HW_BUS_CYCLES] = ARMV7_PERFCTR_CLOCK_CYCLES, +diff --git a/arch/arm/mm/init.c b/arch/arm/mm/init.c +index c19571c..4a4eba5 100644 +--- a/arch/arm/mm/init.c ++++ b/arch/arm/mm/init.c +@@ -473,6 +473,13 @@ static void __init free_unused_memmap(struct meminfo *mi) + */ + bank_start = min(bank_start, + ALIGN(prev_bank_end, PAGES_PER_SECTION)); ++#else ++ /* ++ * Align down here since the VM subsystem insists that the ++ * memmap entries are valid from the bank start aligned to ++ * MAX_ORDER_NR_PAGES. ++ */ ++ bank_start = round_down(bank_start, MAX_ORDER_NR_PAGES); + #endif + /* + * If we had a previous bank, and there is a space +diff --git a/arch/x86/mm/init.c b/arch/x86/mm/init.c +index 3032644..87488b9 100644 +--- a/arch/x86/mm/init.c ++++ b/arch/x86/mm/init.c +@@ -63,9 +63,8 @@ static void __init find_early_table_space(unsigned long end, int use_pse, + #ifdef CONFIG_X86_32 + /* for fixmap */ + tables += roundup(__end_of_fixed_addresses * sizeof(pte_t), PAGE_SIZE); +- +- good_end = max_pfn_mapped << PAGE_SHIFT; + #endif ++ good_end = max_pfn_mapped << PAGE_SHIFT; + + base = memblock_find_in_range(start, good_end, tables, PAGE_SIZE); + if (base == MEMBLOCK_ERROR) +diff --git a/crypto/ghash-generic.c b/crypto/ghash-generic.c +index be44256..7835b8f 100644 +--- a/crypto/ghash-generic.c ++++ b/crypto/ghash-generic.c +@@ -67,6 +67,9 @@ static int ghash_update(struct shash_desc *desc, + struct ghash_ctx *ctx = crypto_shash_ctx(desc->tfm); + u8 *dst = dctx->buffer; + ++ if (!ctx->gf128) ++ return -ENOKEY; ++ + if (dctx->bytes) { + int n = min(srclen, dctx->bytes); + u8 *pos = dst + (GHASH_BLOCK_SIZE - dctx->bytes); +@@ -119,6 +122,9 @@ static int ghash_final(struct shash_desc *desc, u8 *dst) + struct ghash_ctx *ctx = crypto_shash_ctx(desc->tfm); + u8 *buf = dctx->buffer; + ++ if (!ctx->gf128) ++ return -ENOKEY; ++ + ghash_flush(ctx, dctx); + memcpy(dst, buf, GHASH_BLOCK_SIZE); + +diff --git a/drivers/firewire/sbp2.c b/drivers/firewire/sbp2.c +index 41841a3..17cef86 100644 +--- a/drivers/firewire/sbp2.c ++++ b/drivers/firewire/sbp2.c +@@ -1198,6 +1198,10 @@ static int sbp2_remove(struct device *dev) + { + struct fw_unit *unit = fw_unit(dev); + struct sbp2_target *tgt = dev_get_drvdata(&unit->device); ++ struct sbp2_logical_unit *lu; ++ ++ list_for_each_entry(lu, &tgt->lu_list, link) ++ cancel_delayed_work_sync(&lu->work); + + sbp2_target_put(tgt); + return 0; +diff --git a/drivers/gpu/drm/radeon/atom.c b/drivers/gpu/drm/radeon/atom.c +index ebdb0fd..9a0aee2 100644 +--- a/drivers/gpu/drm/radeon/atom.c ++++ b/drivers/gpu/drm/radeon/atom.c +@@ -277,7 +277,12 @@ static uint32_t atom_get_src_int(atom_exec_context *ctx, uint8_t attr, + case ATOM_ARG_FB: + idx = U8(*ptr); + (*ptr)++; +- val = gctx->scratch[((gctx->fb_base + idx) / 4)]; ++ if ((gctx->fb_base + (idx * 4)) > gctx->scratch_size_bytes) { ++ DRM_ERROR("ATOM: fb read beyond scratch region: %d vs. %d\n", ++ gctx->fb_base + (idx * 4), gctx->scratch_size_bytes); ++ val = 0; ++ } else ++ val = gctx->scratch[(gctx->fb_base / 4) + idx]; + if (print) + DEBUG("FB[0x%02X]", idx); + break; +@@ -531,7 +536,11 @@ static void atom_put_dst(atom_exec_context *ctx, int arg, uint8_t attr, + case ATOM_ARG_FB: + idx = U8(*ptr); + (*ptr)++; +- gctx->scratch[((gctx->fb_base + idx) / 4)] = val; ++ if ((gctx->fb_base + (idx * 4)) > gctx->scratch_size_bytes) { ++ DRM_ERROR("ATOM: fb write beyond scratch region: %d vs. %d\n", ++ gctx->fb_base + (idx * 4), gctx->scratch_size_bytes); ++ } else ++ gctx->scratch[(gctx->fb_base / 4) + idx] = val; + DEBUG("FB[0x%02X]", idx); + break; + case ATOM_ARG_PLL: +@@ -1367,11 +1376,13 @@ int atom_allocate_fb_scratch(struct atom_context *ctx) + + usage_bytes = firmware_usage->asFirmwareVramReserveInfo[0].usFirmwareUseInKb * 1024; + } ++ ctx->scratch_size_bytes = 0; + if (usage_bytes == 0) + usage_bytes = 20 * 1024; + /* allocate some scratch memory */ + ctx->scratch = kzalloc(usage_bytes, GFP_KERNEL); + if (!ctx->scratch) + return -ENOMEM; ++ ctx->scratch_size_bytes = usage_bytes; + return 0; + } +diff --git a/drivers/gpu/drm/radeon/atom.h b/drivers/gpu/drm/radeon/atom.h +index a589a55..93cfe20 100644 +--- a/drivers/gpu/drm/radeon/atom.h ++++ b/drivers/gpu/drm/radeon/atom.h +@@ -137,6 +137,7 @@ struct atom_context { + int cs_equal, cs_above; + int io_mode; + uint32_t *scratch; ++ int scratch_size_bytes; + }; + + extern int atom_debug; +diff --git a/drivers/gpu/drm/ttm/ttm_bo.c b/drivers/gpu/drm/ttm/ttm_bo.c +index b7f0726..e2b2d78 100644 +--- a/drivers/gpu/drm/ttm/ttm_bo.c ++++ b/drivers/gpu/drm/ttm/ttm_bo.c +@@ -392,10 +392,12 @@ static int ttm_bo_handle_move_mem(struct ttm_buffer_object *bo, + * Create and bind a ttm if required. + */ + +- if (!(new_man->flags & TTM_MEMTYPE_FLAG_FIXED) && (bo->ttm == NULL)) { +- ret = ttm_bo_add_ttm(bo, false); +- if (ret) +- goto out_err; ++ if (!(new_man->flags & TTM_MEMTYPE_FLAG_FIXED)) { ++ if (bo->ttm == NULL) { ++ ret = ttm_bo_add_ttm(bo, false); ++ if (ret) ++ goto out_err; ++ } + + ret = ttm_tt_set_placement_caching(bo->ttm, mem->placement); + if (ret) +diff --git a/drivers/gpu/drm/ttm/ttm_bo_util.c b/drivers/gpu/drm/ttm/ttm_bo_util.c +index 77dbf40..ae3c6f5 100644 +--- a/drivers/gpu/drm/ttm/ttm_bo_util.c ++++ b/drivers/gpu/drm/ttm/ttm_bo_util.c +@@ -635,13 +635,13 @@ int ttm_bo_move_accel_cleanup(struct ttm_buffer_object *bo, + if (ret) + return ret; + +- ttm_bo_free_old_node(bo); + if ((man->flags & TTM_MEMTYPE_FLAG_FIXED) && + (bo->ttm != NULL)) { + ttm_tt_unbind(bo->ttm); + ttm_tt_destroy(bo->ttm); + bo->ttm = NULL; + } ++ ttm_bo_free_old_node(bo); + } else { + /** + * This should help pipeline ordinary buffer moves. +diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h +index a756ee6..c946d90 100644 +--- a/drivers/hid/hid-ids.h ++++ b/drivers/hid/hid-ids.h +@@ -568,6 +568,9 @@ + #define USB_DEVICE_ID_SAMSUNG_IR_REMOTE 0x0001 + #define USB_DEVICE_ID_SAMSUNG_WIRELESS_KBD_MOUSE 0x0600 + ++#define USB_VENDOR_ID_SIGMA_MICRO 0x1c4f ++#define USB_DEVICE_ID_SIGMA_MICRO_KEYBOARD 0x0002 ++ + #define USB_VENDOR_ID_SKYCABLE 0x1223 + #define USB_DEVICE_ID_SKYCABLE_WIRELESS_PRESENTER 0x3F07 + +diff --git a/drivers/hid/hid-magicmouse.c b/drivers/hid/hid-magicmouse.c +index 0ec91c1..56d0539 100644 +--- a/drivers/hid/hid-magicmouse.c ++++ b/drivers/hid/hid-magicmouse.c +@@ -501,9 +501,17 @@ static int magicmouse_probe(struct hid_device *hdev, + } + report->size = 6; + ++ /* ++ * Some devices repond with 'invalid report id' when feature ++ * report switching it into multitouch mode is sent to it. ++ * ++ * This results in -EIO from the _raw low-level transport callback, ++ * but there seems to be no other way of switching the mode. ++ * Thus the super-ugly hacky success check below. ++ */ + ret = hdev->hid_output_raw_report(hdev, feature, sizeof(feature), + HID_FEATURE_REPORT); +- if (ret != sizeof(feature)) { ++ if (ret != -EIO && ret != sizeof(feature)) { + hid_err(hdev, "unable to request touch data (%d)\n", ret); + goto err_stop_hw; + } +diff --git a/drivers/hid/usbhid/hid-quirks.c b/drivers/hid/usbhid/hid-quirks.c +index 621959d..4bdb5d4 100644 +--- a/drivers/hid/usbhid/hid-quirks.c ++++ b/drivers/hid/usbhid/hid-quirks.c +@@ -89,6 +89,7 @@ static const struct hid_blacklist { + + { USB_VENDOR_ID_CHICONY, USB_DEVICE_ID_CHICONY_MULTI_TOUCH, HID_QUIRK_MULTI_INPUT }, + { USB_VENDOR_ID_CHICONY, USB_DEVICE_ID_CHICONY_WIRELESS, HID_QUIRK_MULTI_INPUT }, ++ { USB_VENDOR_ID_SIGMA_MICRO, USB_DEVICE_ID_SIGMA_MICRO_KEYBOARD, HID_QUIRK_NO_INIT_REPORTS }, + { 0, 0 } + }; + +diff --git a/drivers/hwmon/w83627ehf.c b/drivers/hwmon/w83627ehf.c +index f2b377c..36d7f27 100644 +--- a/drivers/hwmon/w83627ehf.c ++++ b/drivers/hwmon/w83627ehf.c +@@ -390,7 +390,7 @@ temp_from_reg(u16 reg, s16 regval) + { + if (is_word_sized(reg)) + return LM75_TEMP_FROM_REG(regval); +- return regval * 1000; ++ return ((s8)regval) * 1000; + } + + static inline u16 +@@ -398,7 +398,8 @@ temp_to_reg(u16 reg, long temp) + { + if (is_word_sized(reg)) + return LM75_TEMP_TO_REG(temp); +- return DIV_ROUND_CLOSEST(SENSORS_LIMIT(temp, -127000, 128000), 1000); ++ return (s8)DIV_ROUND_CLOSEST(SENSORS_LIMIT(temp, -127000, 128000), ++ 1000); + } + + /* Some of analog inputs have internal scaling (2x), 8mV is ADC LSB */ +@@ -1715,7 +1716,8 @@ static void w83627ehf_device_remove_files(struct device *dev) + } + + /* Get the monitoring functions started */ +-static inline void __devinit w83627ehf_init_device(struct w83627ehf_data *data) ++static inline void __devinit w83627ehf_init_device(struct w83627ehf_data *data, ++ enum kinds kind) + { + int i; + u8 tmp, diode; +@@ -1746,10 +1748,16 @@ static inline void __devinit w83627ehf_init_device(struct w83627ehf_data *data) + w83627ehf_write_value(data, W83627EHF_REG_VBAT, tmp | 0x01); + + /* Get thermal sensor types */ +- diode = w83627ehf_read_value(data, W83627EHF_REG_DIODE); ++ switch (kind) { ++ case w83627ehf: ++ diode = w83627ehf_read_value(data, W83627EHF_REG_DIODE); ++ break; ++ default: ++ diode = 0x70; ++ } + for (i = 0; i < 3; i++) { + if ((tmp & (0x02 << i))) +- data->temp_type[i] = (diode & (0x10 << i)) ? 1 : 2; ++ data->temp_type[i] = (diode & (0x10 << i)) ? 1 : 3; + else + data->temp_type[i] = 4; /* thermistor */ + } +@@ -2016,7 +2024,7 @@ static int __devinit w83627ehf_probe(struct platform_device *pdev) + } + + /* Initialize the chip */ +- w83627ehf_init_device(data); ++ w83627ehf_init_device(data, sio_data->kind); + + data->vrm = vid_which_vrm(); + superio_enter(sio_data->sioreg); +diff --git a/drivers/media/video/uvc/uvc_entity.c b/drivers/media/video/uvc/uvc_entity.c +index 48fea37..29e2399 100644 +--- a/drivers/media/video/uvc/uvc_entity.c ++++ b/drivers/media/video/uvc/uvc_entity.c +@@ -49,7 +49,7 @@ static int uvc_mc_register_entity(struct uvc_video_chain *chain, + if (remote == NULL) + return -EINVAL; + +- source = (UVC_ENTITY_TYPE(remote) != UVC_TT_STREAMING) ++ source = (UVC_ENTITY_TYPE(remote) == UVC_TT_STREAMING) + ? (remote->vdev ? &remote->vdev->entity : NULL) + : &remote->subdev.entity; + if (source == NULL) +diff --git a/drivers/platform/x86/samsung-laptop.c b/drivers/platform/x86/samsung-laptop.c +index d347116..1658575 100644 +--- a/drivers/platform/x86/samsung-laptop.c ++++ b/drivers/platform/x86/samsung-laptop.c +@@ -601,6 +601,16 @@ static struct dmi_system_id __initdata samsung_dmi_table[] = { + .callback = dmi_check_cb, + }, + { ++ .ident = "N150/N210/N220", ++ .matches = { ++ DMI_MATCH(DMI_SYS_VENDOR, ++ "SAMSUNG ELECTRONICS CO., LTD."), ++ DMI_MATCH(DMI_PRODUCT_NAME, "N150/N210/N220"), ++ DMI_MATCH(DMI_BOARD_NAME, "N150/N210/N220"), ++ }, ++ .callback = dmi_check_cb, ++ }, ++ { + .ident = "N150/N210/N220/N230", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, +diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c +index fc7e57b..53e7d72 100644 +--- a/fs/cifs/cifsfs.c ++++ b/fs/cifs/cifsfs.c +@@ -566,6 +566,12 @@ cifs_get_root(struct smb_vol *vol, struct super_block *sb) + struct inode *dir = dentry->d_inode; + struct dentry *child; + ++ if (!dir) { ++ dput(dentry); ++ dentry = ERR_PTR(-ENOENT); ++ break; ++ } ++ + /* skip separators */ + while (*s == sep) + s++; +@@ -581,10 +587,6 @@ cifs_get_root(struct smb_vol *vol, struct super_block *sb) + mutex_unlock(&dir->i_mutex); + dput(dentry); + dentry = child; +- if (!dentry->d_inode) { +- dput(dentry); +- dentry = ERR_PTR(-ENOENT); +- } + } while (!IS_ERR(dentry)); + _FreeXid(xid); + kfree(full_path); +diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c +index 168a80f..5cb8614 100644 +--- a/fs/fuse/dev.c ++++ b/fs/fuse/dev.c +@@ -258,10 +258,14 @@ void fuse_queue_forget(struct fuse_conn *fc, struct fuse_forget_link *forget, + forget->forget_one.nlookup = nlookup; + + spin_lock(&fc->lock); +- fc->forget_list_tail->next = forget; +- fc->forget_list_tail = forget; +- wake_up(&fc->waitq); +- kill_fasync(&fc->fasync, SIGIO, POLL_IN); ++ if (fc->connected) { ++ fc->forget_list_tail->next = forget; ++ fc->forget_list_tail = forget; ++ wake_up(&fc->waitq); ++ kill_fasync(&fc->fasync, SIGIO, POLL_IN); ++ } else { ++ kfree(forget); ++ } + spin_unlock(&fc->lock); + } + +diff --git a/fs/hfsplus/hfsplus_fs.h b/fs/hfsplus/hfsplus_fs.h +index d685752..4e7f64b 100644 +--- a/fs/hfsplus/hfsplus_fs.h ++++ b/fs/hfsplus/hfsplus_fs.h +@@ -13,6 +13,7 @@ + #include <linux/fs.h> + #include <linux/mutex.h> + #include <linux/buffer_head.h> ++#include <linux/blkdev.h> + #include "hfsplus_raw.h" + + #define DBG_BNODE_REFS 0x00000001 +@@ -110,7 +111,9 @@ struct hfsplus_vh; + struct hfs_btree; + + struct hfsplus_sb_info { ++ void *s_vhdr_buf; + struct hfsplus_vh *s_vhdr; ++ void *s_backup_vhdr_buf; + struct hfsplus_vh *s_backup_vhdr; + struct hfs_btree *ext_tree; + struct hfs_btree *cat_tree; +@@ -258,6 +261,15 @@ struct hfsplus_readdir_data { + struct hfsplus_cat_key key; + }; + ++/* ++ * Find minimum acceptible I/O size for an hfsplus sb. ++ */ ++static inline unsigned short hfsplus_min_io_size(struct super_block *sb) ++{ ++ return max_t(unsigned short, bdev_logical_block_size(sb->s_bdev), ++ HFSPLUS_SECTOR_SIZE); ++} ++ + #define hfs_btree_open hfsplus_btree_open + #define hfs_btree_close hfsplus_btree_close + #define hfs_btree_write hfsplus_btree_write +@@ -436,8 +448,8 @@ int hfsplus_compare_dentry(const struct dentry *parent, + /* wrapper.c */ + int hfsplus_read_wrapper(struct super_block *); + int hfs_part_find(struct super_block *, sector_t *, sector_t *); +-int hfsplus_submit_bio(struct block_device *bdev, sector_t sector, +- void *data, int rw); ++int hfsplus_submit_bio(struct super_block *sb, sector_t sector, ++ void *buf, void **data, int rw); + + /* time macros */ + #define __hfsp_mt2ut(t) (be32_to_cpu(t) - 2082844800U) +diff --git a/fs/hfsplus/part_tbl.c b/fs/hfsplus/part_tbl.c +index 40ad88c..eb355d8 100644 +--- a/fs/hfsplus/part_tbl.c ++++ b/fs/hfsplus/part_tbl.c +@@ -88,11 +88,12 @@ static int hfs_parse_old_pmap(struct super_block *sb, struct old_pmap *pm, + return -ENOENT; + } + +-static int hfs_parse_new_pmap(struct super_block *sb, struct new_pmap *pm, +- sector_t *part_start, sector_t *part_size) ++static int hfs_parse_new_pmap(struct super_block *sb, void *buf, ++ struct new_pmap *pm, sector_t *part_start, sector_t *part_size) + { + struct hfsplus_sb_info *sbi = HFSPLUS_SB(sb); + int size = be32_to_cpu(pm->pmMapBlkCnt); ++ int buf_size = hfsplus_min_io_size(sb); + int res; + int i = 0; + +@@ -107,11 +108,14 @@ static int hfs_parse_new_pmap(struct super_block *sb, struct new_pmap *pm, + if (++i >= size) + return -ENOENT; + +- res = hfsplus_submit_bio(sb->s_bdev, +- *part_start + HFS_PMAP_BLK + i, +- pm, READ); +- if (res) +- return res; ++ pm = (struct new_pmap *)((u8 *)pm + HFSPLUS_SECTOR_SIZE); ++ if ((u8 *)pm - (u8 *)buf >= buf_size) { ++ res = hfsplus_submit_bio(sb, ++ *part_start + HFS_PMAP_BLK + i, ++ buf, (void **)&pm, READ); ++ if (res) ++ return res; ++ } + } while (pm->pmSig == cpu_to_be16(HFS_NEW_PMAP_MAGIC)); + + return -ENOENT; +@@ -124,15 +128,15 @@ static int hfs_parse_new_pmap(struct super_block *sb, struct new_pmap *pm, + int hfs_part_find(struct super_block *sb, + sector_t *part_start, sector_t *part_size) + { +- void *data; ++ void *buf, *data; + int res; + +- data = kmalloc(HFSPLUS_SECTOR_SIZE, GFP_KERNEL); +- if (!data) ++ buf = kmalloc(hfsplus_min_io_size(sb), GFP_KERNEL); ++ if (!buf) + return -ENOMEM; + +- res = hfsplus_submit_bio(sb->s_bdev, *part_start + HFS_PMAP_BLK, +- data, READ); ++ res = hfsplus_submit_bio(sb, *part_start + HFS_PMAP_BLK, ++ buf, &data, READ); + if (res) + goto out; + +@@ -141,13 +145,13 @@ int hfs_part_find(struct super_block *sb, + res = hfs_parse_old_pmap(sb, data, part_start, part_size); + break; + case HFS_NEW_PMAP_MAGIC: +- res = hfs_parse_new_pmap(sb, data, part_start, part_size); ++ res = hfs_parse_new_pmap(sb, buf, data, part_start, part_size); + break; + default: + res = -ENOENT; + break; + } + out: +- kfree(data); ++ kfree(buf); + return res; + } +diff --git a/fs/hfsplus/super.c b/fs/hfsplus/super.c +index 84a47b7..c3a76fd 100644 +--- a/fs/hfsplus/super.c ++++ b/fs/hfsplus/super.c +@@ -197,17 +197,17 @@ int hfsplus_sync_fs(struct super_block *sb, int wait) + write_backup = 1; + } + +- error2 = hfsplus_submit_bio(sb->s_bdev, ++ error2 = hfsplus_submit_bio(sb, + sbi->part_start + HFSPLUS_VOLHEAD_SECTOR, +- sbi->s_vhdr, WRITE_SYNC); ++ sbi->s_vhdr_buf, NULL, WRITE_SYNC); + if (!error) + error = error2; + if (!write_backup) + goto out; + +- error2 = hfsplus_submit_bio(sb->s_bdev, ++ error2 = hfsplus_submit_bio(sb, + sbi->part_start + sbi->sect_count - 2, +- sbi->s_backup_vhdr, WRITE_SYNC); ++ sbi->s_backup_vhdr_buf, NULL, WRITE_SYNC); + if (!error) + error2 = error; + out: +@@ -251,8 +251,8 @@ static void hfsplus_put_super(struct super_block *sb) + hfs_btree_close(sbi->ext_tree); + iput(sbi->alloc_file); + iput(sbi->hidden_dir); +- kfree(sbi->s_vhdr); +- kfree(sbi->s_backup_vhdr); ++ kfree(sbi->s_vhdr_buf); ++ kfree(sbi->s_backup_vhdr_buf); + unload_nls(sbi->nls); + kfree(sb->s_fs_info); + sb->s_fs_info = NULL; +@@ -508,8 +508,8 @@ out_close_cat_tree: + out_close_ext_tree: + hfs_btree_close(sbi->ext_tree); + out_free_vhdr: +- kfree(sbi->s_vhdr); +- kfree(sbi->s_backup_vhdr); ++ kfree(sbi->s_vhdr_buf); ++ kfree(sbi->s_backup_vhdr_buf); + out_unload_nls: + unload_nls(sbi->nls); + unload_nls(nls); +diff --git a/fs/hfsplus/wrapper.c b/fs/hfsplus/wrapper.c +index 4ac88ff..7b8112d 100644 +--- a/fs/hfsplus/wrapper.c ++++ b/fs/hfsplus/wrapper.c +@@ -31,25 +31,67 @@ static void hfsplus_end_io_sync(struct bio *bio, int err) + complete(bio->bi_private); + } + +-int hfsplus_submit_bio(struct block_device *bdev, sector_t sector, +- void *data, int rw) ++/* ++ * hfsplus_submit_bio - Perfrom block I/O ++ * @sb: super block of volume for I/O ++ * @sector: block to read or write, for blocks of HFSPLUS_SECTOR_SIZE bytes ++ * @buf: buffer for I/O ++ * @data: output pointer for location of requested data ++ * @rw: direction of I/O ++ * ++ * The unit of I/O is hfsplus_min_io_size(sb), which may be bigger than ++ * HFSPLUS_SECTOR_SIZE, and @buf must be sized accordingly. On reads ++ * @data will return a pointer to the start of the requested sector, ++ * which may not be the same location as @buf. ++ * ++ * If @sector is not aligned to the bdev logical block size it will ++ * be rounded down. For writes this means that @buf should contain data ++ * that starts at the rounded-down address. As long as the data was ++ * read using hfsplus_submit_bio() and the same buffer is used things ++ * will work correctly. ++ */ ++int hfsplus_submit_bio(struct super_block *sb, sector_t sector, ++ void *buf, void **data, int rw) + { + DECLARE_COMPLETION_ONSTACK(wait); + struct bio *bio; + int ret = 0; ++ unsigned int io_size; ++ loff_t start; ++ int offset; ++ ++ /* ++ * Align sector to hardware sector size and find offset. We ++ * assume that io_size is a power of two, which _should_ ++ * be true. ++ */ ++ io_size = hfsplus_min_io_size(sb); ++ start = (loff_t)sector << HFSPLUS_SECTOR_SHIFT; ++ offset = start & (io_size - 1); ++ sector &= ~((io_size >> HFSPLUS_SECTOR_SHIFT) - 1); + + bio = bio_alloc(GFP_NOIO, 1); + bio->bi_sector = sector; +- bio->bi_bdev = bdev; ++ bio->bi_bdev = sb->s_bdev; + bio->bi_end_io = hfsplus_end_io_sync; + bio->bi_private = &wait; + +- /* +- * We always submit one sector at a time, so bio_add_page must not fail. +- */ +- if (bio_add_page(bio, virt_to_page(data), HFSPLUS_SECTOR_SIZE, +- offset_in_page(data)) != HFSPLUS_SECTOR_SIZE) +- BUG(); ++ if (!(rw & WRITE) && data) ++ *data = (u8 *)buf + offset; ++ ++ while (io_size > 0) { ++ unsigned int page_offset = offset_in_page(buf); ++ unsigned int len = min_t(unsigned int, PAGE_SIZE - page_offset, ++ io_size); ++ ++ ret = bio_add_page(bio, virt_to_page(buf), len, page_offset); ++ if (ret != len) { ++ ret = -EIO; ++ goto out; ++ } ++ io_size -= len; ++ buf = (u8 *)buf + len; ++ } + + submit_bio(rw, bio); + wait_for_completion(&wait); +@@ -57,8 +99,9 @@ int hfsplus_submit_bio(struct block_device *bdev, sector_t sector, + if (!bio_flagged(bio, BIO_UPTODATE)) + ret = -EIO; + ++out: + bio_put(bio); +- return ret; ++ return ret < 0 ? ret : 0; + } + + static int hfsplus_read_mdb(void *bufptr, struct hfsplus_wd *wd) +@@ -147,17 +190,17 @@ int hfsplus_read_wrapper(struct super_block *sb) + } + + error = -ENOMEM; +- sbi->s_vhdr = kmalloc(HFSPLUS_SECTOR_SIZE, GFP_KERNEL); +- if (!sbi->s_vhdr) ++ sbi->s_vhdr_buf = kmalloc(hfsplus_min_io_size(sb), GFP_KERNEL); ++ if (!sbi->s_vhdr_buf) + goto out; +- sbi->s_backup_vhdr = kmalloc(HFSPLUS_SECTOR_SIZE, GFP_KERNEL); +- if (!sbi->s_backup_vhdr) ++ sbi->s_backup_vhdr_buf = kmalloc(hfsplus_min_io_size(sb), GFP_KERNEL); ++ if (!sbi->s_backup_vhdr_buf) + goto out_free_vhdr; + + reread: +- error = hfsplus_submit_bio(sb->s_bdev, +- part_start + HFSPLUS_VOLHEAD_SECTOR, +- sbi->s_vhdr, READ); ++ error = hfsplus_submit_bio(sb, part_start + HFSPLUS_VOLHEAD_SECTOR, ++ sbi->s_vhdr_buf, (void **)&sbi->s_vhdr, ++ READ); + if (error) + goto out_free_backup_vhdr; + +@@ -186,9 +229,9 @@ reread: + goto reread; + } + +- error = hfsplus_submit_bio(sb->s_bdev, +- part_start + part_size - 2, +- sbi->s_backup_vhdr, READ); ++ error = hfsplus_submit_bio(sb, part_start + part_size - 2, ++ sbi->s_backup_vhdr_buf, ++ (void **)&sbi->s_backup_vhdr, READ); + if (error) + goto out_free_backup_vhdr; + +@@ -232,9 +275,9 @@ reread: + return 0; + + out_free_backup_vhdr: +- kfree(sbi->s_backup_vhdr); ++ kfree(sbi->s_backup_vhdr_buf); + out_free_vhdr: +- kfree(sbi->s_vhdr); ++ kfree(sbi->s_vhdr_buf); + out: + return error; + } +diff --git a/fs/xfs/linux-2.6/xfs_linux.h b/fs/xfs/linux-2.6/xfs_linux.h +index 8633521..8731516 100644 +--- a/fs/xfs/linux-2.6/xfs_linux.h ++++ b/fs/xfs/linux-2.6/xfs_linux.h +@@ -70,6 +70,8 @@ + #include <linux/ctype.h> + #include <linux/writeback.h> + #include <linux/capability.h> ++#include <linux/kthread.h> ++#include <linux/freezer.h> + #include <linux/list_sort.h> + + #include <asm/page.h> +diff --git a/fs/xfs/linux-2.6/xfs_super.c b/fs/xfs/linux-2.6/xfs_super.c +index a1a881e..347cae9 100644 +--- a/fs/xfs/linux-2.6/xfs_super.c ++++ b/fs/xfs/linux-2.6/xfs_super.c +@@ -1412,37 +1412,35 @@ xfs_fs_fill_super( + sb->s_time_gran = 1; + set_posix_acl_flag(sb); + +- error = xfs_syncd_init(mp); +- if (error) +- goto out_filestream_unmount; +- + xfs_inode_shrinker_register(mp); + + error = xfs_mountfs(mp); + if (error) +- goto out_syncd_stop; ++ goto out_filestream_unmount; ++ ++ error = xfs_syncd_init(mp); ++ if (error) ++ goto out_unmount; + + root = igrab(VFS_I(mp->m_rootip)); + if (!root) { + error = ENOENT; +- goto fail_unmount; ++ goto out_syncd_stop; + } + if (is_bad_inode(root)) { + error = EINVAL; +- goto fail_vnrele; ++ goto out_syncd_stop; + } + sb->s_root = d_alloc_root(root); + if (!sb->s_root) { + error = ENOMEM; +- goto fail_vnrele; ++ goto out_iput; + } + + return 0; + +- out_syncd_stop: +- xfs_inode_shrinker_unregister(mp); +- xfs_syncd_stop(mp); + out_filestream_unmount: ++ xfs_inode_shrinker_unregister(mp); + xfs_filestream_unmount(mp); + out_free_sb: + xfs_freesb(mp); +@@ -1456,17 +1454,12 @@ xfs_fs_fill_super( + out: + return -error; + +- fail_vnrele: +- if (sb->s_root) { +- dput(sb->s_root); +- sb->s_root = NULL; +- } else { +- iput(root); +- } +- +- fail_unmount: +- xfs_inode_shrinker_unregister(mp); ++ out_iput: ++ iput(root); ++ out_syncd_stop: + xfs_syncd_stop(mp); ++ out_unmount: ++ xfs_inode_shrinker_unregister(mp); + + /* + * Blow away any referenced inode in the filestreams cache. +@@ -1667,24 +1660,13 @@ xfs_init_workqueues(void) + */ + xfs_syncd_wq = alloc_workqueue("xfssyncd", WQ_CPU_INTENSIVE, 8); + if (!xfs_syncd_wq) +- goto out; +- +- xfs_ail_wq = alloc_workqueue("xfsail", WQ_CPU_INTENSIVE, 8); +- if (!xfs_ail_wq) +- goto out_destroy_syncd; +- ++ return -ENOMEM; + return 0; +- +-out_destroy_syncd: +- destroy_workqueue(xfs_syncd_wq); +-out: +- return -ENOMEM; + } + + STATIC void + xfs_destroy_workqueues(void) + { +- destroy_workqueue(xfs_ail_wq); + destroy_workqueue(xfs_syncd_wq); + } + +diff --git a/fs/xfs/quota/xfs_dquot_item.c b/fs/xfs/quota/xfs_dquot_item.c +index 9e0e2fa..8126fc2 100644 +--- a/fs/xfs/quota/xfs_dquot_item.c ++++ b/fs/xfs/quota/xfs_dquot_item.c +@@ -183,13 +183,14 @@ xfs_qm_dqunpin_wait( + * search the buffer cache can be a time consuming thing, and AIL lock is a + * spinlock. + */ +-STATIC void ++STATIC bool + xfs_qm_dquot_logitem_pushbuf( + struct xfs_log_item *lip) + { + struct xfs_dq_logitem *qlip = DQUOT_ITEM(lip); + struct xfs_dquot *dqp = qlip->qli_dquot; + struct xfs_buf *bp; ++ bool ret = true; + + ASSERT(XFS_DQ_IS_LOCKED(dqp)); + +@@ -201,17 +202,20 @@ xfs_qm_dquot_logitem_pushbuf( + if (completion_done(&dqp->q_flush) || + !(lip->li_flags & XFS_LI_IN_AIL)) { + xfs_dqunlock(dqp); +- return; ++ return true; + } + + bp = xfs_incore(dqp->q_mount->m_ddev_targp, qlip->qli_format.qlf_blkno, + dqp->q_mount->m_quotainfo->qi_dqchunklen, XBF_TRYLOCK); + xfs_dqunlock(dqp); + if (!bp) +- return; ++ return true; + if (XFS_BUF_ISDELAYWRITE(bp)) + xfs_buf_delwri_promote(bp); ++ if (XFS_BUF_ISPINNED(bp)) ++ ret = false; + xfs_buf_relse(bp); ++ return ret; + } + + /* +diff --git a/fs/xfs/xfs_buf_item.c b/fs/xfs/xfs_buf_item.c +index 7b7e005..a7342e8 100644 +--- a/fs/xfs/xfs_buf_item.c ++++ b/fs/xfs/xfs_buf_item.c +@@ -632,7 +632,7 @@ xfs_buf_item_push( + * the xfsbufd to get this buffer written. We have to unlock the buffer + * to allow the xfsbufd to write it, too. + */ +-STATIC void ++STATIC bool + xfs_buf_item_pushbuf( + struct xfs_log_item *lip) + { +@@ -646,6 +646,7 @@ xfs_buf_item_pushbuf( + + xfs_buf_delwri_promote(bp); + xfs_buf_relse(bp); ++ return true; + } + + STATIC void +diff --git a/fs/xfs/xfs_inode_item.c b/fs/xfs/xfs_inode_item.c +index b1e88d5..391044c 100644 +--- a/fs/xfs/xfs_inode_item.c ++++ b/fs/xfs/xfs_inode_item.c +@@ -713,13 +713,14 @@ xfs_inode_item_committed( + * marked delayed write. If that's the case, we'll promote it and that will + * allow the caller to write the buffer by triggering the xfsbufd to run. + */ +-STATIC void ++STATIC bool + xfs_inode_item_pushbuf( + struct xfs_log_item *lip) + { + struct xfs_inode_log_item *iip = INODE_ITEM(lip); + struct xfs_inode *ip = iip->ili_inode; + struct xfs_buf *bp; ++ bool ret = true; + + ASSERT(xfs_isilocked(ip, XFS_ILOCK_SHARED)); + +@@ -730,7 +731,7 @@ xfs_inode_item_pushbuf( + if (completion_done(&ip->i_flush) || + !(lip->li_flags & XFS_LI_IN_AIL)) { + xfs_iunlock(ip, XFS_ILOCK_SHARED); +- return; ++ return true; + } + + bp = xfs_incore(ip->i_mount->m_ddev_targp, iip->ili_format.ilf_blkno, +@@ -738,10 +739,13 @@ xfs_inode_item_pushbuf( + + xfs_iunlock(ip, XFS_ILOCK_SHARED); + if (!bp) +- return; ++ return true; + if (XFS_BUF_ISDELAYWRITE(bp)) + xfs_buf_delwri_promote(bp); ++ if (XFS_BUF_ISPINNED(bp)) ++ ret = false; + xfs_buf_relse(bp); ++ return ret; + } + + /* +diff --git a/fs/xfs/xfs_trans.c b/fs/xfs/xfs_trans.c +index c83f63b..efc147f 100644 +--- a/fs/xfs/xfs_trans.c ++++ b/fs/xfs/xfs_trans.c +@@ -1426,6 +1426,7 @@ xfs_trans_committed( + static inline void + xfs_log_item_batch_insert( + struct xfs_ail *ailp, ++ struct xfs_ail_cursor *cur, + struct xfs_log_item **log_items, + int nr_items, + xfs_lsn_t commit_lsn) +@@ -1434,7 +1435,7 @@ xfs_log_item_batch_insert( + + spin_lock(&ailp->xa_lock); + /* xfs_trans_ail_update_bulk drops ailp->xa_lock */ +- xfs_trans_ail_update_bulk(ailp, log_items, nr_items, commit_lsn); ++ xfs_trans_ail_update_bulk(ailp, cur, log_items, nr_items, commit_lsn); + + for (i = 0; i < nr_items; i++) + IOP_UNPIN(log_items[i], 0); +@@ -1452,6 +1453,13 @@ xfs_log_item_batch_insert( + * as an iclog write error even though we haven't started any IO yet. Hence in + * this case all we need to do is IOP_COMMITTED processing, followed by an + * IOP_UNPIN(aborted) call. ++ * ++ * The AIL cursor is used to optimise the insert process. If commit_lsn is not ++ * at the end of the AIL, the insert cursor avoids the need to walk ++ * the AIL to find the insertion point on every xfs_log_item_batch_insert() ++ * call. This saves a lot of needless list walking and is a net win, even ++ * though it slightly increases that amount of AIL lock traffic to set it up ++ * and tear it down. + */ + void + xfs_trans_committed_bulk( +@@ -1463,8 +1471,13 @@ xfs_trans_committed_bulk( + #define LOG_ITEM_BATCH_SIZE 32 + struct xfs_log_item *log_items[LOG_ITEM_BATCH_SIZE]; + struct xfs_log_vec *lv; ++ struct xfs_ail_cursor cur; + int i = 0; + ++ spin_lock(&ailp->xa_lock); ++ xfs_trans_ail_cursor_last(ailp, &cur, commit_lsn); ++ spin_unlock(&ailp->xa_lock); ++ + /* unpin all the log items */ + for (lv = log_vector; lv; lv = lv->lv_next ) { + struct xfs_log_item *lip = lv->lv_item; +@@ -1493,7 +1506,9 @@ xfs_trans_committed_bulk( + /* + * Not a bulk update option due to unusual item_lsn. + * Push into AIL immediately, rechecking the lsn once +- * we have the ail lock. Then unpin the item. ++ * we have the ail lock. Then unpin the item. This does ++ * not affect the AIL cursor the bulk insert path is ++ * using. + */ + spin_lock(&ailp->xa_lock); + if (XFS_LSN_CMP(item_lsn, lip->li_lsn) > 0) +@@ -1507,7 +1522,7 @@ xfs_trans_committed_bulk( + /* Item is a candidate for bulk AIL insert. */ + log_items[i++] = lv->lv_item; + if (i >= LOG_ITEM_BATCH_SIZE) { +- xfs_log_item_batch_insert(ailp, log_items, ++ xfs_log_item_batch_insert(ailp, &cur, log_items, + LOG_ITEM_BATCH_SIZE, commit_lsn); + i = 0; + } +@@ -1515,7 +1530,11 @@ xfs_trans_committed_bulk( + + /* make sure we insert the remainder! */ + if (i) +- xfs_log_item_batch_insert(ailp, log_items, i, commit_lsn); ++ xfs_log_item_batch_insert(ailp, &cur, log_items, i, commit_lsn); ++ ++ spin_lock(&ailp->xa_lock); ++ xfs_trans_ail_cursor_done(ailp, &cur); ++ spin_unlock(&ailp->xa_lock); + } + + /* +diff --git a/fs/xfs/xfs_trans.h b/fs/xfs/xfs_trans.h +index 06a9759..53597f4 100644 +--- a/fs/xfs/xfs_trans.h ++++ b/fs/xfs/xfs_trans.h +@@ -350,7 +350,7 @@ typedef struct xfs_item_ops { + void (*iop_unlock)(xfs_log_item_t *); + xfs_lsn_t (*iop_committed)(xfs_log_item_t *, xfs_lsn_t); + void (*iop_push)(xfs_log_item_t *); +- void (*iop_pushbuf)(xfs_log_item_t *); ++ bool (*iop_pushbuf)(xfs_log_item_t *); + void (*iop_committing)(xfs_log_item_t *, xfs_lsn_t); + } xfs_item_ops_t; + +diff --git a/fs/xfs/xfs_trans_ail.c b/fs/xfs/xfs_trans_ail.c +index 5fc2380..a4c281b 100644 +--- a/fs/xfs/xfs_trans_ail.c ++++ b/fs/xfs/xfs_trans_ail.c +@@ -28,8 +28,6 @@ + #include "xfs_trans_priv.h" + #include "xfs_error.h" + +-struct workqueue_struct *xfs_ail_wq; /* AIL workqueue */ +- + #ifdef DEBUG + /* + * Check that the list is sorted as it should be. +@@ -272,9 +270,9 @@ xfs_trans_ail_cursor_clear( + } + + /* +- * Return the item in the AIL with the current lsn. +- * Return the current tree generation number for use +- * in calls to xfs_trans_next_ail(). ++ * Initialise the cursor to the first item in the AIL with the given @lsn. ++ * This searches the list from lowest LSN to highest. Pass a @lsn of zero ++ * to initialise the cursor to the first item in the AIL. + */ + xfs_log_item_t * + xfs_trans_ail_cursor_first( +@@ -300,31 +298,97 @@ out: + } + + /* +- * splice the log item list into the AIL at the given LSN. ++ * Initialise the cursor to the last item in the AIL with the given @lsn. ++ * This searches the list from highest LSN to lowest. If there is no item with ++ * the value of @lsn, then it sets the cursor to the last item with an LSN lower ++ * than @lsn. ++ */ ++static struct xfs_log_item * ++__xfs_trans_ail_cursor_last( ++ struct xfs_ail *ailp, ++ xfs_lsn_t lsn) ++{ ++ xfs_log_item_t *lip; ++ ++ list_for_each_entry_reverse(lip, &ailp->xa_ail, li_ail) { ++ if (XFS_LSN_CMP(lip->li_lsn, lsn) <= 0) ++ return lip; ++ } ++ return NULL; ++} ++ ++/* ++ * Initialise the cursor to the last item in the AIL with the given @lsn. ++ * This searches the list from highest LSN to lowest. ++ */ ++struct xfs_log_item * ++xfs_trans_ail_cursor_last( ++ struct xfs_ail *ailp, ++ struct xfs_ail_cursor *cur, ++ xfs_lsn_t lsn) ++{ ++ xfs_trans_ail_cursor_init(ailp, cur); ++ cur->item = __xfs_trans_ail_cursor_last(ailp, lsn); ++ return cur->item; ++} ++ ++/* ++ * splice the log item list into the AIL at the given LSN. We splice to the ++ * tail of the given LSN to maintain insert order for push traversals. The ++ * cursor is optional, allowing repeated updates to the same LSN to avoid ++ * repeated traversals. + */ + static void + xfs_ail_splice( +- struct xfs_ail *ailp, +- struct list_head *list, +- xfs_lsn_t lsn) ++ struct xfs_ail *ailp, ++ struct xfs_ail_cursor *cur, ++ struct list_head *list, ++ xfs_lsn_t lsn) + { +- xfs_log_item_t *next_lip; ++ struct xfs_log_item *lip = cur ? cur->item : NULL; ++ struct xfs_log_item *next_lip; + +- /* If the list is empty, just insert the item. */ +- if (list_empty(&ailp->xa_ail)) { +- list_splice(list, &ailp->xa_ail); +- return; ++ /* ++ * Get a new cursor if we don't have a placeholder or the existing one ++ * has been invalidated. ++ */ ++ if (!lip || (__psint_t)lip & 1) { ++ lip = __xfs_trans_ail_cursor_last(ailp, lsn); ++ ++ if (!lip) { ++ /* The list is empty, so just splice and return. */ ++ if (cur) ++ cur->item = NULL; ++ list_splice(list, &ailp->xa_ail); ++ return; ++ } + } + +- list_for_each_entry_reverse(next_lip, &ailp->xa_ail, li_ail) { +- if (XFS_LSN_CMP(next_lip->li_lsn, lsn) <= 0) +- break; ++ /* ++ * Our cursor points to the item we want to insert _after_, so we have ++ * to update the cursor to point to the end of the list we are splicing ++ * in so that it points to the correct location for the next splice. ++ * i.e. before the splice ++ * ++ * lsn -> lsn -> lsn + x -> lsn + x ... ++ * ^ ++ * | cursor points here ++ * ++ * After the splice we have: ++ * ++ * lsn -> lsn -> lsn -> lsn -> .... -> lsn -> lsn + x -> lsn + x ... ++ * ^ ^ ++ * | cursor points here | needs to move here ++ * ++ * So we set the cursor to the last item in the list to be spliced ++ * before we execute the splice, resulting in the cursor pointing to ++ * the correct item after the splice occurs. ++ */ ++ if (cur) { ++ next_lip = list_entry(list->prev, struct xfs_log_item, li_ail); ++ cur->item = next_lip; + } +- +- ASSERT(&next_lip->li_ail == &ailp->xa_ail || +- XFS_LSN_CMP(next_lip->li_lsn, lsn) <= 0); +- +- list_splice_init(list, &next_lip->li_ail); ++ list_splice(list, &lip->li_ail); + } + + /* +@@ -340,16 +404,10 @@ xfs_ail_delete( + xfs_trans_ail_cursor_clear(ailp, lip); + } + +-/* +- * xfs_ail_worker does the work of pushing on the AIL. It will requeue itself +- * to run at a later time if there is more work to do to complete the push. +- */ +-STATIC void +-xfs_ail_worker( +- struct work_struct *work) ++static long ++xfsaild_push( ++ struct xfs_ail *ailp) + { +- struct xfs_ail *ailp = container_of(to_delayed_work(work), +- struct xfs_ail, xa_work); + xfs_mount_t *mp = ailp->xa_mount; + struct xfs_ail_cursor *cur = &ailp->xa_cursors; + xfs_log_item_t *lip; +@@ -412,8 +470,13 @@ xfs_ail_worker( + + case XFS_ITEM_PUSHBUF: + XFS_STATS_INC(xs_push_ail_pushbuf); +- IOP_PUSHBUF(lip); +- ailp->xa_last_pushed_lsn = lsn; ++ ++ if (!IOP_PUSHBUF(lip)) { ++ stuck++; ++ flush_log = 1; ++ } else { ++ ailp->xa_last_pushed_lsn = lsn; ++ } + push_xfsbufd = 1; + break; + +@@ -425,7 +488,6 @@ xfs_ail_worker( + + case XFS_ITEM_LOCKED: + XFS_STATS_INC(xs_push_ail_locked); +- ailp->xa_last_pushed_lsn = lsn; + stuck++; + break; + +@@ -486,20 +548,6 @@ out_done: + /* We're past our target or empty, so idle */ + ailp->xa_last_pushed_lsn = 0; + +- /* +- * We clear the XFS_AIL_PUSHING_BIT first before checking +- * whether the target has changed. If the target has changed, +- * this pushes the requeue race directly onto the result of the +- * atomic test/set bit, so we are guaranteed that either the +- * the pusher that changed the target or ourselves will requeue +- * the work (but not both). +- */ +- clear_bit(XFS_AIL_PUSHING_BIT, &ailp->xa_flags); +- smp_rmb(); +- if (XFS_LSN_CMP(ailp->xa_target, target) == 0 || +- test_and_set_bit(XFS_AIL_PUSHING_BIT, &ailp->xa_flags)) +- return; +- + tout = 50; + } else if (XFS_LSN_CMP(lsn, target) >= 0) { + /* +@@ -522,9 +570,30 @@ out_done: + tout = 20; + } + +- /* There is more to do, requeue us. */ +- queue_delayed_work(xfs_syncd_wq, &ailp->xa_work, +- msecs_to_jiffies(tout)); ++ return tout; ++} ++ ++static int ++xfsaild( ++ void *data) ++{ ++ struct xfs_ail *ailp = data; ++ long tout = 0; /* milliseconds */ ++ ++ while (!kthread_should_stop()) { ++ if (tout && tout <= 20) ++ __set_current_state(TASK_KILLABLE); ++ else ++ __set_current_state(TASK_INTERRUPTIBLE); ++ schedule_timeout(tout ? ++ msecs_to_jiffies(tout) : MAX_SCHEDULE_TIMEOUT); ++ ++ try_to_freeze(); ++ ++ tout = xfsaild_push(ailp); ++ } ++ ++ return 0; + } + + /* +@@ -559,8 +628,9 @@ xfs_ail_push( + */ + smp_wmb(); + xfs_trans_ail_copy_lsn(ailp, &ailp->xa_target, &threshold_lsn); +- if (!test_and_set_bit(XFS_AIL_PUSHING_BIT, &ailp->xa_flags)) +- queue_delayed_work(xfs_syncd_wq, &ailp->xa_work, 0); ++ smp_wmb(); ++ ++ wake_up_process(ailp->xa_task); + } + + /* +@@ -645,6 +715,7 @@ xfs_trans_unlocked_item( + void + xfs_trans_ail_update_bulk( + struct xfs_ail *ailp, ++ struct xfs_ail_cursor *cur, + struct xfs_log_item **log_items, + int nr_items, + xfs_lsn_t lsn) __releases(ailp->xa_lock) +@@ -674,7 +745,7 @@ xfs_trans_ail_update_bulk( + list_add(&lip->li_ail, &tmp); + } + +- xfs_ail_splice(ailp, &tmp, lsn); ++ xfs_ail_splice(ailp, cur, &tmp, lsn); + + if (!mlip_changed) { + spin_unlock(&ailp->xa_lock); +@@ -794,9 +865,18 @@ xfs_trans_ail_init( + ailp->xa_mount = mp; + INIT_LIST_HEAD(&ailp->xa_ail); + spin_lock_init(&ailp->xa_lock); +- INIT_DELAYED_WORK(&ailp->xa_work, xfs_ail_worker); ++ ++ ailp->xa_task = kthread_run(xfsaild, ailp, "xfsaild/%s", ++ ailp->xa_mount->m_fsname); ++ if (IS_ERR(ailp->xa_task)) ++ goto out_free_ailp; ++ + mp->m_ail = ailp; + return 0; ++ ++out_free_ailp: ++ kmem_free(ailp); ++ return ENOMEM; + } + + void +@@ -805,6 +885,6 @@ xfs_trans_ail_destroy( + { + struct xfs_ail *ailp = mp->m_ail; + +- cancel_delayed_work_sync(&ailp->xa_work); ++ kthread_stop(ailp->xa_task); + kmem_free(ailp); + } +diff --git a/fs/xfs/xfs_trans_priv.h b/fs/xfs/xfs_trans_priv.h +index 6b164e9..fe2e3cb 100644 +--- a/fs/xfs/xfs_trans_priv.h ++++ b/fs/xfs/xfs_trans_priv.h +@@ -64,24 +64,19 @@ struct xfs_ail_cursor { + */ + struct xfs_ail { + struct xfs_mount *xa_mount; ++ struct task_struct *xa_task; + struct list_head xa_ail; + xfs_lsn_t xa_target; + struct xfs_ail_cursor xa_cursors; + spinlock_t xa_lock; +- struct delayed_work xa_work; + xfs_lsn_t xa_last_pushed_lsn; +- unsigned long xa_flags; + }; + +-#define XFS_AIL_PUSHING_BIT 0 +- + /* + * From xfs_trans_ail.c + */ +- +-extern struct workqueue_struct *xfs_ail_wq; /* AIL workqueue */ +- + void xfs_trans_ail_update_bulk(struct xfs_ail *ailp, ++ struct xfs_ail_cursor *cur, + struct xfs_log_item **log_items, int nr_items, + xfs_lsn_t lsn) __releases(ailp->xa_lock); + static inline void +@@ -90,7 +85,7 @@ xfs_trans_ail_update( + struct xfs_log_item *lip, + xfs_lsn_t lsn) __releases(ailp->xa_lock) + { +- xfs_trans_ail_update_bulk(ailp, &lip, 1, lsn); ++ xfs_trans_ail_update_bulk(ailp, NULL, &lip, 1, lsn); + } + + void xfs_trans_ail_delete_bulk(struct xfs_ail *ailp, +@@ -111,10 +106,13 @@ xfs_lsn_t xfs_ail_min_lsn(struct xfs_ail *ailp); + void xfs_trans_unlocked_item(struct xfs_ail *, + xfs_log_item_t *); + +-struct xfs_log_item *xfs_trans_ail_cursor_first(struct xfs_ail *ailp, ++struct xfs_log_item * xfs_trans_ail_cursor_first(struct xfs_ail *ailp, ++ struct xfs_ail_cursor *cur, ++ xfs_lsn_t lsn); ++struct xfs_log_item * xfs_trans_ail_cursor_last(struct xfs_ail *ailp, + struct xfs_ail_cursor *cur, + xfs_lsn_t lsn); +-struct xfs_log_item *xfs_trans_ail_cursor_next(struct xfs_ail *ailp, ++struct xfs_log_item * xfs_trans_ail_cursor_next(struct xfs_ail *ailp, + struct xfs_ail_cursor *cur); + void xfs_trans_ail_cursor_done(struct xfs_ail *ailp, + struct xfs_ail_cursor *cur); +diff --git a/kernel/posix-cpu-timers.c b/kernel/posix-cpu-timers.c +index c8008dd..640ded8 100644 +--- a/kernel/posix-cpu-timers.c ++++ b/kernel/posix-cpu-timers.c +@@ -274,9 +274,7 @@ void thread_group_cputimer(struct task_struct *tsk, struct task_cputime *times) + struct task_cputime sum; + unsigned long flags; + +- spin_lock_irqsave(&cputimer->lock, flags); + if (!cputimer->running) { +- cputimer->running = 1; + /* + * The POSIX timer interface allows for absolute time expiry + * values through the TIMER_ABSTIME flag, therefore we have +@@ -284,8 +282,11 @@ void thread_group_cputimer(struct task_struct *tsk, struct task_cputime *times) + * it. + */ + thread_group_cputime(tsk, &sum); ++ spin_lock_irqsave(&cputimer->lock, flags); ++ cputimer->running = 1; + update_gt_cputime(&cputimer->cputime, &sum); +- } ++ } else ++ spin_lock_irqsave(&cputimer->lock, flags); + *times = cputimer->cputime; + spin_unlock_irqrestore(&cputimer->lock, flags); + } +diff --git a/kernel/sys.c b/kernel/sys.c +index 5c942cf..f88dadc 100644 +--- a/kernel/sys.c ++++ b/kernel/sys.c +@@ -1135,7 +1135,7 @@ DECLARE_RWSEM(uts_sem); + static int override_release(char __user *release, int len) + { + int ret = 0; +- char buf[len]; ++ char buf[65]; + + if (current->personality & UNAME26) { + char *rest = UTS_RELEASE; +diff --git a/mm/migrate.c b/mm/migrate.c +index 666e4e6..14d0a6a 100644 +--- a/mm/migrate.c ++++ b/mm/migrate.c +@@ -120,10 +120,10 @@ static int remove_migration_pte(struct page *new, struct vm_area_struct *vma, + + ptep = pte_offset_map(pmd, addr); + +- if (!is_swap_pte(*ptep)) { +- pte_unmap(ptep); +- goto out; +- } ++ /* ++ * Peek to check is_swap_pte() before taking ptlock? No, we ++ * can race mremap's move_ptes(), which skips anon_vma lock. ++ */ + + ptl = pte_lockptr(mm, pmd); + } +diff --git a/net/x25/af_x25.c b/net/x25/af_x25.c +index 4680b1e..373e14f 100644 +--- a/net/x25/af_x25.c ++++ b/net/x25/af_x25.c +@@ -295,7 +295,8 @@ static struct sock *x25_find_listener(struct x25_address *addr, + * Found a listening socket, now check the incoming + * call user data vs this sockets call user data + */ +- if(skb->len > 0 && x25_sk(s)->cudmatchlength > 0) { ++ if (x25_sk(s)->cudmatchlength > 0 && ++ skb->len >= x25_sk(s)->cudmatchlength) { + if((memcmp(x25_sk(s)->calluserdata.cuddata, + skb->data, + x25_sk(s)->cudmatchlength)) == 0) { +diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c +index 486f6de..981b6fd 100644 +--- a/sound/pci/hda/hda_intel.c ++++ b/sound/pci/hda/hda_intel.c +@@ -2352,6 +2352,7 @@ static struct snd_pci_quirk position_fix_list[] __devinitdata = { + SND_PCI_QUIRK(0x1028, 0x01cc, "Dell D820", POS_FIX_LPIB), + SND_PCI_QUIRK(0x1028, 0x01de, "Dell Precision 390", POS_FIX_LPIB), + SND_PCI_QUIRK(0x1028, 0x01f6, "Dell Latitude 131L", POS_FIX_LPIB), ++ SND_PCI_QUIRK(0x1028, 0x02c6, "Dell Inspiron 1010", POS_FIX_LPIB), + SND_PCI_QUIRK(0x1028, 0x0470, "Dell Inspiron 1120", POS_FIX_LPIB), + SND_PCI_QUIRK(0x103c, 0x306d, "HP dv3", POS_FIX_LPIB), + SND_PCI_QUIRK(0x1043, 0x813d, "ASUS P5AD2", POS_FIX_LPIB), +diff --git a/sound/pci/hda/patch_conexant.c b/sound/pci/hda/patch_conexant.c +index 7bbc5f2..cf1fa36 100644 +--- a/sound/pci/hda/patch_conexant.c ++++ b/sound/pci/hda/patch_conexant.c +@@ -3097,6 +3097,7 @@ static const struct snd_pci_quirk cxt5066_cfg_tbl[] = { + SND_PCI_QUIRK(0x17aa, 0x21c5, "Thinkpad Edge 13", CXT5066_THINKPAD), + SND_PCI_QUIRK(0x17aa, 0x21c6, "Thinkpad Edge 13", CXT5066_ASUS), + SND_PCI_QUIRK(0x17aa, 0x215e, "Lenovo Thinkpad", CXT5066_THINKPAD), ++ SND_PCI_QUIRK(0x17aa, 0x21cf, "Lenovo T520 & W520", CXT5066_AUTO), + SND_PCI_QUIRK(0x17aa, 0x21da, "Lenovo X220", CXT5066_THINKPAD), + SND_PCI_QUIRK(0x17aa, 0x21db, "Lenovo X220-tablet", CXT5066_THINKPAD), + SND_PCI_QUIRK(0x17aa, 0x3a0d, "Lenovo U350", CXT5066_ASUS), |