diff mbox

[v2,06/11] don't put symlink bodies in pagecache into highmem

Message ID 1449639295-20512-6-git-send-email-viro@ZenIV.linux.org.uk (mailing list archive)
State New, archived
Headers show

Commit Message

Al Viro Dec. 9, 2015, 5:34 a.m. UTC
From: Al Viro <viro@zeniv.linux.org.uk>

kmap() in page_follow_link_light() needed to go - allowing to hold
an arbitrary number of kmaps for long is a great way to deadlocking
the system.

new helper (inode_nohighmem(inode)) needs to be used for pagecache
symlinks inodes; done for all in-tree cases.  page_follow_link_light()
instrumented to yell about anything missed.

Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
---
 Documentation/filesystems/porting |  5 +++++
 fs/affs/inode.c                   |  1 +
 fs/affs/namei.c                   |  1 +
 fs/affs/symlink.c                 |  4 +---
 fs/afs/inode.c                    |  1 +
 fs/befs/linuxvfs.c                |  5 ++---
 fs/btrfs/inode.c                  |  2 ++
 fs/coda/cnode.c                   |  2 ++
 fs/coda/symlink.c                 |  4 +---
 fs/cramfs/inode.c                 |  1 +
 fs/efs/inode.c                    |  1 +
 fs/efs/symlink.c                  |  4 +---
 fs/exofs/inode.c                  |  1 +
 fs/exofs/namei.c                  |  1 +
 fs/ext2/inode.c                   |  1 +
 fs/ext2/namei.c                   |  1 +
 fs/ext4/inode.c                   |  1 +
 fs/ext4/namei.c                   |  1 +
 fs/ext4/symlink.c                 | 10 +++-------
 fs/f2fs/inode.c                   |  1 +
 fs/f2fs/namei.c                   |  5 ++---
 fs/freevxfs/vxfs_inode.c          |  1 +
 fs/hfsplus/inode.c                |  2 ++
 fs/hpfs/inode.c                   |  1 +
 fs/hpfs/namei.c                   |  5 ++---
 fs/hugetlbfs/inode.c              |  1 +
 fs/inode.c                        |  6 ++++++
 fs/isofs/inode.c                  |  1 +
 fs/isofs/rock.c                   |  4 +---
 fs/jfs/inode.c                    |  1 +
 fs/jfs/namei.c                    |  1 +
 fs/logfs/dir.c                    |  1 +
 fs/logfs/inode.c                  |  1 +
 fs/minix/inode.c                  |  1 +
 fs/namei.c                        |  9 +++------
 fs/ncpfs/inode.c                  |  1 +
 fs/nfs/inode.c                    |  5 +++--
 fs/nfs/symlink.c                  |  2 +-
 fs/nilfs2/inode.c                 |  1 +
 fs/nilfs2/namei.c                 |  1 +
 fs/ocfs2/inode.c                  |  1 +
 fs/ocfs2/namei.c                  |  1 +
 fs/qnx4/inode.c                   |  1 +
 fs/qnx6/inode.c                   |  1 +
 fs/ramfs/inode.c                  |  1 +
 fs/reiserfs/inode.c               |  1 +
 fs/reiserfs/namei.c               |  1 +
 fs/romfs/super.c                  |  1 +
 fs/squashfs/inode.c               |  2 ++
 fs/sysv/inode.c                   |  1 +
 fs/udf/inode.c                    |  1 +
 fs/udf/namei.c                    |  1 +
 fs/udf/symlink.c                  |  4 +---
 fs/ufs/inode.c                    |  1 +
 fs/ufs/namei.c                    |  1 +
 include/linux/fs.h                |  1 +
 mm/shmem.c                        |  9 +++------
 57 files changed, 81 insertions(+), 46 deletions(-)

Comments

Tomeu Vizoso Jan. 14, 2016, 1:22 p.m. UTC | #1
On 9 December 2015 at 06:34, Al Viro <viro@zeniv.linux.org.uk> wrote:
> From: Al Viro <viro@zeniv.linux.org.uk>
>
> kmap() in page_follow_link_light() needed to go - allowing to hold
> an arbitrary number of kmaps for long is a great way to deadlocking
> the system.
>
> new helper (inode_nohighmem(inode)) needs to be used for pagecache
> symlinks inodes; done for all in-tree cases.  page_follow_link_light()
> instrumented to yell about anything missed.

Hi, starting with with this change, I get this oops when installing
packages into a rootfs in NFS:

[  144.455632] Unable to handle kernel NULL pointer dereference at
virtual address 00000000
[  144.463726] pgd = ee760000
[  144.466441] [00000000] *pgd=7b83f831
[  144.470036] Internal error: Oops: 17 [#1] SMP ARM
[  144.474732] Modules linked in:
[  144.477798] CPU: 2 PID: 1 Comm: systemd Not tainted
4.4.0-rc4-00006-g21fc61c73c39-dirty #3521
[  144.486307] Hardware name: Rockchip (Device Tree)
[  144.491004] task: ee078000 ti: ee062000 task.ti: ee062000
[  144.496399] PC is at strlen+0x0/0x2c
[  144.499972] LR is at readlink_copy+0x24/0x94
[  144.504236] pc : [<c049019c>]    lr : [<c031ae40>]    psr: 00000013
[  144.504236] sp : ee063f38  ip : 00000000  fp : ec8a66b0
[  144.515694] r10: 001acf88  r9 : 00000063  r8 : ee063f74
[  144.520909] r7 : 001acef8  r6 : 001acf88  r5 : 00000000  r4 : 00000063
[  144.527424] r3 : c0ee8e94  r2 : 00000000  r1 : 00000063  r0 : 00000000
[  144.533939] Flags: nzcv  IRQs on  FIQs on  Mode SVC_32  ISA ARM  Segment none
[  144.541060] Control: 10c5387d  Table: 2e76006a  DAC: 00000051
[  144.546794] Process systemd (pid: 1, stack limit = 0xee062220)
[  144.552614] Stack: (0xee063f38 to 0xee064000)
[  144.556962] 3f20:
    001acf88 ec8a66b0
[  144.565126] 3f40: 00000063 001acef8 ee063f74 c031aedc 001acf88
eff69f20 ffffffea 00004000
[  144.573290] 3f60: ffffff9c c0315e94 00000000 00000000 00000025
00000000 ee5f89d0 ec8367f8
[  144.581453] 3f80: 56978623 001acf88 00000064 00000063 0000014c
c0210d44 ee062000 00000000
[  144.589616] 3fa0: 001acef8 c0210b80 001acf88 00000064 ffffff9c
001acef8 001acf88 00000063
[  144.597780] 3fc0: 001acf88 00000064 00000063 0000014c be8930b8
001bcc94 0018695b 001acef8
[  144.605943] 3fe0: 0000014c be893094 b6f3bf7b b6ec98e6 20000030
ffffff9c ffffffff ffeffffe
[  144.614109] [<c049019c>] (strlen) from [<c031ae40>] (readlink_copy+0x24/0x94)
[  144.621234] [<c031ae40>] (readlink_copy) from [<c031aedc>]
(generic_readlink+0x2c/0x78)
[  144.629224] [<c031aedc>] (generic_readlink) from [<c0315e94>]
(SyS_readlinkat+0x98/0xe0)
[  144.637301] [<c0315e94>] (SyS_readlinkat) from [<c0210b80>]
(ret_fast_syscall+0x0/0x3c)
[  144.645290] Code: e7d23003 e3130020 1afffffb e12fff1e (e5d02000)
[  144.651388] ---[ end trace 299126b4c29ad1e5 ]---
[  144.658936] Kernel panic - not syncing: Attempted to kill init!
exitcode=0x0000000b
[  144.658936]
[  144.668056] CPU1: stopping
[  144.670759] CPU: 1 PID: 0 Comm: swapper/1 Tainted: G      D
4.4.0-rc4-00006-g21fc61c73c39-dirty #3521
[  144.680651] Hardware name: Rockchip (Device Tree)
[  144.685355] [<c02191a0>] (unwind_backtrace) from [<c0214898>]
(show_stack+0x10/0x14)
[  144.693085] [<c0214898>] (show_stack) from [<c0489ac4>]
(dump_stack+0x84/0x94)
[  144.700295] [<c0489ac4>] (dump_stack) from [<c0217950>]
(handle_IPI+0x188/0x1a8)
[  144.707678] [<c0217950>] (handle_IPI) from [<c020a740>]
(gic_handle_irq+0x90/0x94)
[  144.715233] [<c020a740>] (gic_handle_irq) from [<c02153d4>]
(__irq_svc+0x54/0x70)
[  144.722698] Exception stack(0xee0a7f80 to 0xee0a7fc8)
[  144.727739] 7f80: 00000001 00000000 00000000 c0223ae0 ee0a6000
c0ef04a4 00000000 00000000
[  144.735900] 7fa0: ee0a7fd8 c0ef0504 c09f7624 c0ef050c 2e0ca000
ee0a7fd0 c0211668 c021166c
[  144.744060] 7fc0: 60070013 ffffffff
[  144.747543] [<c02153d4>] (__irq_svc) from [<c021166c>]
(arch_cpu_idle+0x38/0x3c)
[  144.754926] [<c021166c>] (arch_cpu_idle) from [<c028158c>]
(cpu_startup_entry+0x1ec/0x248)
[  144.763175] [<c028158c>] (cpu_startup_entry) from [<0020ab4c>] (0x20ab4c)
[  144.769948] CPU0: stopping
[  144.772650] CPU: 0 PID: 0 Comm: swapper/0 Tainted: G      D
4.4.0-rc4-00006-g21fc61c73c39-dirty #3521
[  144.782543] Hardware name: Rockchip (Device Tree)
[  144.787241] [<c02191a0>] (unwind_backtrace) from [<c0214898>]
(show_stack+0x10/0x14)
[  144.794970] [<c0214898>] (show_stack) from [<c0489ac4>]
(dump_stack+0x84/0x94)
[  144.802179] [<c0489ac4>] (dump_stack) from [<c0217950>]
(handle_IPI+0x188/0x1a8)
[  144.809560] [<c0217950>] (handle_IPI) from [<c020a740>]
(gic_handle_irq+0x90/0x94)
[  144.817114] [<c020a740>] (gic_handle_irq) from [<c02153d4>]
(__irq_svc+0x54/0x70)
[  144.824579] Exception stack(0xc0eeff48 to 0xc0eeff90)
[  144.829620] ff40:                   00000001 00000000 00000000
c0223ae0 c0eee000 c0ef04a4
[  144.837782] ff60: 00000000 00000000 c0eeffa0 c0ef0504 c09f7624
c0ef050c 60000013 c0eeff98
[  144.845942] ff80: c0211668 c021166c 60000013 ffffffff
[  144.850983] [<c02153d4>] (__irq_svc) from [<c021166c>]
(arch_cpu_idle+0x38/0x3c)
[  144.858365] [<c021166c>] (arch_cpu_idle) from [<c028158c>]
(cpu_startup_entry+0x1ec/0x248)
[  144.866614] [<c028158c>] (cpu_startup_entry) from [<c0de6c88>]
(start_kernel+0x3a8/0x3b4)
[  144.874775] CPU3: stopping
[  144.877477] CPU: 3 PID: 8117 Comm: apt-get Tainted: G      D
 4.4.0-rc4-00006-g21fc61c73c39-dirty #3521
[  144.887455] Hardware name: Rockchip (Device Tree)
[  144.892154] [<c02191a0>] (unwind_backtrace) from [<c0214898>]
(show_stack+0x10/0x14)
[  144.899885] [<c0214898>] (show_stack) from [<c0489ac4>]
(dump_stack+0x84/0x94)
[  144.907094] [<c0489ac4>] (dump_stack) from [<c0217950>]
(handle_IPI+0x188/0x1a8)
[  144.914475] [<c0217950>] (handle_IPI) from [<c020a740>]
(gic_handle_irq+0x90/0x94)
[  144.922028] [<c020a740>] (gic_handle_irq) from [<c02153d4>]
(__irq_svc+0x54/0x70)
[  144.929494] Exception stack(0xed57da88 to 0xed57dad0)
[  144.934535] da80:                   c1020dc4 a0000013 c1020dc4
000079a8 00000000 ee273dd0
[  144.942696] daa0: f0bbd000 ee5d2300 00000000 00000000 0000000c
00000000 00000000 ed57dad8
[  144.950856] dac0: c059f2ec c09ef788 60000013 ffffffff
[  144.955899] [<c02153d4>] (__irq_svc) from [<c09ef788>]
(_raw_spin_unlock_irqrestore+0x1c/0x20)
[  144.964496] [<c09ef788>] (_raw_spin_unlock_irqrestore) from
[<c059f2ec>] (uart_chars_in_buffer+0x2c/0x34)
[  144.974046] [<c059f2ec>] (uart_chars_in_buffer) from [<c0588520>]
(n_tty_poll+0x180/0x19c)
[  144.982294] [<c0588520>] (n_tty_poll) from [<c0585418>] (tty_poll+0x6c/0x80)
[  144.989329] [<c0585418>] (tty_poll) from [<c0322f08>] (do_select+0x2dc/0x618)
[  144.996451] [<c0322f08>] (do_select) from [<c0323378>]
(core_sys_select+0x134/0x3f4)
[  145.004179] [<c0323378>] (core_sys_select) from [<c0323940>]
(SyS_pselect6+0x1d8/0x31c)
[  145.012168] [<c0323940>] (SyS_pselect6) from [<c0210b80>]
(ret_fast_syscall+0x0/0x3c)
[  145.019985] ---[ end Kernel panic - not syncing: Attempted to kill
init! exitcode=0x0000000b

Without it, the symlink is read correctly and there's no oops.

Regards,

Tomeu

> Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
> ---
>  Documentation/filesystems/porting |  5 +++++
>  fs/affs/inode.c                   |  1 +
>  fs/affs/namei.c                   |  1 +
>  fs/affs/symlink.c                 |  4 +---
>  fs/afs/inode.c                    |  1 +
>  fs/befs/linuxvfs.c                |  5 ++---
>  fs/btrfs/inode.c                  |  2 ++
>  fs/coda/cnode.c                   |  2 ++
>  fs/coda/symlink.c                 |  4 +---
>  fs/cramfs/inode.c                 |  1 +
>  fs/efs/inode.c                    |  1 +
>  fs/efs/symlink.c                  |  4 +---
>  fs/exofs/inode.c                  |  1 +
>  fs/exofs/namei.c                  |  1 +
>  fs/ext2/inode.c                   |  1 +
>  fs/ext2/namei.c                   |  1 +
>  fs/ext4/inode.c                   |  1 +
>  fs/ext4/namei.c                   |  1 +
>  fs/ext4/symlink.c                 | 10 +++-------
>  fs/f2fs/inode.c                   |  1 +
>  fs/f2fs/namei.c                   |  5 ++---
>  fs/freevxfs/vxfs_inode.c          |  1 +
>  fs/hfsplus/inode.c                |  2 ++
>  fs/hpfs/inode.c                   |  1 +
>  fs/hpfs/namei.c                   |  5 ++---
>  fs/hugetlbfs/inode.c              |  1 +
>  fs/inode.c                        |  6 ++++++
>  fs/isofs/inode.c                  |  1 +
>  fs/isofs/rock.c                   |  4 +---
>  fs/jfs/inode.c                    |  1 +
>  fs/jfs/namei.c                    |  1 +
>  fs/logfs/dir.c                    |  1 +
>  fs/logfs/inode.c                  |  1 +
>  fs/minix/inode.c                  |  1 +
>  fs/namei.c                        |  9 +++------
>  fs/ncpfs/inode.c                  |  1 +
>  fs/nfs/inode.c                    |  5 +++--
>  fs/nfs/symlink.c                  |  2 +-
>  fs/nilfs2/inode.c                 |  1 +
>  fs/nilfs2/namei.c                 |  1 +
>  fs/ocfs2/inode.c                  |  1 +
>  fs/ocfs2/namei.c                  |  1 +
>  fs/qnx4/inode.c                   |  1 +
>  fs/qnx6/inode.c                   |  1 +
>  fs/ramfs/inode.c                  |  1 +
>  fs/reiserfs/inode.c               |  1 +
>  fs/reiserfs/namei.c               |  1 +
>  fs/romfs/super.c                  |  1 +
>  fs/squashfs/inode.c               |  2 ++
>  fs/sysv/inode.c                   |  1 +
>  fs/udf/inode.c                    |  1 +
>  fs/udf/namei.c                    |  1 +
>  fs/udf/symlink.c                  |  4 +---
>  fs/ufs/inode.c                    |  1 +
>  fs/ufs/namei.c                    |  1 +
>  include/linux/fs.h                |  1 +
>  mm/shmem.c                        |  9 +++------
>  57 files changed, 81 insertions(+), 46 deletions(-)
>
> diff --git a/Documentation/filesystems/porting b/Documentation/filesystems/porting
> index f24d1b8..3eb7c35 100644
> --- a/Documentation/filesystems/porting
> +++ b/Documentation/filesystems/porting
> @@ -504,3 +504,8 @@ in your dentry operations instead.
>  [mandatory]
>         __fd_install() & fd_install() can now sleep. Callers should not
>         hold a spinlock or other resources that do not allow a schedule.
> +--
> +[mandatory]
> +       any symlink that might use page_follow_link_light/page_put_link() must
> +       have inode_nohighmem(inode) called before anything might start playing with
> +       its pagecache.
> diff --git a/fs/affs/inode.c b/fs/affs/inode.c
> index 1734950..0fdb0f5 100644
> --- a/fs/affs/inode.c
> +++ b/fs/affs/inode.c
> @@ -140,6 +140,7 @@ struct inode *affs_iget(struct super_block *sb, unsigned long ino)
>                 break;
>         case ST_SOFTLINK:
>                 inode->i_mode |= S_IFLNK;
> +               inode_nohighmem(inode);
>                 inode->i_op = &affs_symlink_inode_operations;
>                 inode->i_data.a_ops = &affs_symlink_aops;
>                 break;
> diff --git a/fs/affs/namei.c b/fs/affs/namei.c
> index 181e05b..00d3002 100644
> --- a/fs/affs/namei.c
> +++ b/fs/affs/namei.c
> @@ -344,6 +344,7 @@ affs_symlink(struct inode *dir, struct dentry *dentry, const char *symname)
>                 return -ENOSPC;
>
>         inode->i_op = &affs_symlink_inode_operations;
> +       inode_nohighmem(inode);
>         inode->i_data.a_ops = &affs_symlink_aops;
>         inode->i_mode = S_IFLNK | 0777;
>         mode_to_prot(inode);
> diff --git a/fs/affs/symlink.c b/fs/affs/symlink.c
> index ea5b69a..e3f9dc3 100644
> --- a/fs/affs/symlink.c
> +++ b/fs/affs/symlink.c
> @@ -14,7 +14,7 @@ static int affs_symlink_readpage(struct file *file, struct page *page)
>  {
>         struct buffer_head *bh;
>         struct inode *inode = page->mapping->host;
> -       char *link = kmap(page);
> +       char *link = page_address(page);
>         struct slink_front *lf;
>         int                      i, j;
>         char                     c;
> @@ -57,12 +57,10 @@ static int affs_symlink_readpage(struct file *file, struct page *page)
>         link[i] = '\0';
>         affs_brelse(bh);
>         SetPageUptodate(page);
> -       kunmap(page);
>         unlock_page(page);
>         return 0;
>  fail:
>         SetPageError(page);
> -       kunmap(page);
>         unlock_page(page);
>         return -EIO;
>  }
> diff --git a/fs/afs/inode.c b/fs/afs/inode.c
> index e06f5a2..86cc726 100644
> --- a/fs/afs/inode.c
> +++ b/fs/afs/inode.c
> @@ -56,6 +56,7 @@ static int afs_inode_map_status(struct afs_vnode *vnode, struct key *key)
>         case AFS_FTYPE_SYMLINK:
>                 inode->i_mode   = S_IFLNK | vnode->status.mode;
>                 inode->i_op     = &page_symlink_inode_operations;
> +               inode_nohighmem(inode);
>                 break;
>         default:
>                 printk("kAFS: AFS vnode with undefined type\n");
> diff --git a/fs/befs/linuxvfs.c b/fs/befs/linuxvfs.c
> index 1c8b0dc..25250fa 100644
> --- a/fs/befs/linuxvfs.c
> +++ b/fs/befs/linuxvfs.c
> @@ -397,6 +397,7 @@ static struct inode *befs_iget(struct super_block *sb, unsigned long ino)
>         } else if (S_ISLNK(inode->i_mode)) {
>                 if (befs_ino->i_flags & BEFS_LONG_SYMLINK) {
>                         inode->i_op = &page_symlink_inode_operations;
> +                       inode_nohighmem(inode);
>                         inode->i_mapping->a_ops = &befs_symlink_aops;
>                 } else {
>                         inode->i_link = befs_ino->i_data.symlink;
> @@ -469,7 +470,7 @@ static int befs_symlink_readpage(struct file *unused, struct page *page)
>         struct befs_inode_info *befs_ino = BEFS_I(inode);
>         befs_data_stream *data = &befs_ino->i_data.ds;
>         befs_off_t len = data->size;
> -       char *link = kmap(page);
> +       char *link = page_address(page);
>
>         if (len == 0 || len > PAGE_SIZE) {
>                 befs_error(sb, "Long symlink with illegal length");
> @@ -483,12 +484,10 @@ static int befs_symlink_readpage(struct file *unused, struct page *page)
>         }
>         link[len - 1] = '\0';
>         SetPageUptodate(page);
> -       kunmap(page);
>         unlock_page(page);
>         return 0;
>  fail:
>         SetPageError(page);
> -       kunmap(page);
>         unlock_page(page);
>         return -EIO;
>  }
> diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c
> index a70c579..70f98bf 100644
> --- a/fs/btrfs/inode.c
> +++ b/fs/btrfs/inode.c
> @@ -3774,6 +3774,7 @@ cache_acl:
>                 break;
>         case S_IFLNK:
>                 inode->i_op = &btrfs_symlink_inode_operations;
> +               inode_nohighmem(inode);
>                 inode->i_mapping->a_ops = &btrfs_symlink_aops;
>                 break;
>         default:
> @@ -9705,6 +9706,7 @@ static int btrfs_symlink(struct inode *dir, struct dentry *dentry,
>         btrfs_free_path(path);
>
>         inode->i_op = &btrfs_symlink_inode_operations;
> +       inode_nohighmem(inode);
>         inode->i_mapping->a_ops = &btrfs_symlink_aops;
>         inode_set_bytes(inode, name_len);
>         btrfs_i_size_write(inode, name_len);
> diff --git a/fs/coda/cnode.c b/fs/coda/cnode.c
> index 7740b1c..dd6a79e 100644
> --- a/fs/coda/cnode.c
> +++ b/fs/coda/cnode.c
> @@ -8,6 +8,7 @@
>
>  #include <linux/coda.h>
>  #include <linux/coda_psdev.h>
> +#include <linux/pagemap.h>
>  #include "coda_linux.h"
>
>  static inline int coda_fideq(struct CodaFid *fid1, struct CodaFid *fid2)
> @@ -35,6 +36,7 @@ static void coda_fill_inode(struct inode *inode, struct coda_vattr *attr)
>                  inode->i_fop = &coda_dir_operations;
>          } else if (S_ISLNK(inode->i_mode)) {
>                 inode->i_op = &coda_symlink_inode_operations;
> +               inode_nohighmem(inode);
>                 inode->i_data.a_ops = &coda_symlink_aops;
>                 inode->i_mapping = &inode->i_data;
>         } else
> diff --git a/fs/coda/symlink.c b/fs/coda/symlink.c
> index ab94ef6..03736e2 100644
> --- a/fs/coda/symlink.c
> +++ b/fs/coda/symlink.c
> @@ -26,7 +26,7 @@ static int coda_symlink_filler(struct file *file, struct page *page)
>         int error;
>         struct coda_inode_info *cii;
>         unsigned int len = PAGE_SIZE;
> -       char *p = kmap(page);
> +       char *p = page_address(page);
>
>         cii = ITOC(inode);
>
> @@ -34,13 +34,11 @@ static int coda_symlink_filler(struct file *file, struct page *page)
>         if (error)
>                 goto fail;
>         SetPageUptodate(page);
> -       kunmap(page);
>         unlock_page(page);
>         return 0;
>
>  fail:
>         SetPageError(page);
> -       kunmap(page);
>         unlock_page(page);
>         return error;
>  }
> diff --git a/fs/cramfs/inode.c b/fs/cramfs/inode.c
> index 355c522..b862bc2 100644
> --- a/fs/cramfs/inode.c
> +++ b/fs/cramfs/inode.c
> @@ -100,6 +100,7 @@ static struct inode *get_cramfs_inode(struct super_block *sb,
>                 break;
>         case S_IFLNK:
>                 inode->i_op = &page_symlink_inode_operations;
> +               inode_nohighmem(inode);
>                 inode->i_data.a_ops = &cramfs_aops;
>                 break;
>         default:
> diff --git a/fs/efs/inode.c b/fs/efs/inode.c
> index 079d203..cdf0872 100644
> --- a/fs/efs/inode.c
> +++ b/fs/efs/inode.c
> @@ -151,6 +151,7 @@ struct inode *efs_iget(struct super_block *super, unsigned long ino)
>                         break;
>                 case S_IFLNK:
>                         inode->i_op = &page_symlink_inode_operations;
> +                       inode_nohighmem(inode);
>                         inode->i_data.a_ops = &efs_symlink_aops;
>                         break;
>                 case S_IFCHR:
> diff --git a/fs/efs/symlink.c b/fs/efs/symlink.c
> index 75117d0..4870cc8 100644
> --- a/fs/efs/symlink.c
> +++ b/fs/efs/symlink.c
> @@ -13,7 +13,7 @@
>
>  static int efs_symlink_readpage(struct file *file, struct page *page)
>  {
> -       char *link = kmap(page);
> +       char *link = page_address(page);
>         struct buffer_head * bh;
>         struct inode * inode = page->mapping->host;
>         efs_block_t size = inode->i_size;
> @@ -39,12 +39,10 @@ static int efs_symlink_readpage(struct file *file, struct page *page)
>         }
>         link[size] = '\0';
>         SetPageUptodate(page);
> -       kunmap(page);
>         unlock_page(page);
>         return 0;
>  fail:
>         SetPageError(page);
> -       kunmap(page);
>         unlock_page(page);
>         return err;
>  }
> diff --git a/fs/exofs/inode.c b/fs/exofs/inode.c
> index 73c64da..d8e9c181 100644
> --- a/fs/exofs/inode.c
> +++ b/fs/exofs/inode.c
> @@ -1227,6 +1227,7 @@ struct inode *exofs_iget(struct super_block *sb, unsigned long ino)
>                         inode->i_link = (char *)oi->i_data;
>                 } else {
>                         inode->i_op = &page_symlink_inode_operations;
> +                       inode_nohighmem(inode);
>                         inode->i_mapping->a_ops = &exofs_aops;
>                 }
>         } else {
> diff --git a/fs/exofs/namei.c b/fs/exofs/namei.c
> index 994e078..c20d77d 100644
> --- a/fs/exofs/namei.c
> +++ b/fs/exofs/namei.c
> @@ -111,6 +111,7 @@ static int exofs_symlink(struct inode *dir, struct dentry *dentry,
>         if (l > sizeof(oi->i_data)) {
>                 /* slow symlink */
>                 inode->i_op = &page_symlink_inode_operations;
> +               inode_nohighmem(inode);
>                 inode->i_mapping->a_ops = &exofs_aops;
>                 memset(oi->i_data, 0, sizeof(oi->i_data));
>
> diff --git a/fs/ext2/inode.c b/fs/ext2/inode.c
> index 0aa9bf6..338eefd 100644
> --- a/fs/ext2/inode.c
> +++ b/fs/ext2/inode.c
> @@ -1420,6 +1420,7 @@ struct inode *ext2_iget (struct super_block *sb, unsigned long ino)
>                                 sizeof(ei->i_data) - 1);
>                 } else {
>                         inode->i_op = &ext2_symlink_inode_operations;
> +                       inode_nohighmem(inode);
>                         if (test_opt(inode->i_sb, NOBH))
>                                 inode->i_mapping->a_ops = &ext2_nobh_aops;
>                         else
> diff --git a/fs/ext2/namei.c b/fs/ext2/namei.c
> index 3267a80d..7a2be8f 100644
> --- a/fs/ext2/namei.c
> +++ b/fs/ext2/namei.c
> @@ -183,6 +183,7 @@ static int ext2_symlink (struct inode * dir, struct dentry * dentry,
>         if (l > sizeof (EXT2_I(inode)->i_data)) {
>                 /* slow symlink */
>                 inode->i_op = &ext2_symlink_inode_operations;
> +               inode_nohighmem(inode);
>                 if (test_opt(inode->i_sb, NOBH))
>                         inode->i_mapping->a_ops = &ext2_nobh_aops;
>                 else
> diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c
> index ea433a7..b3bd912 100644
> --- a/fs/ext4/inode.c
> +++ b/fs/ext4/inode.c
> @@ -4283,6 +4283,7 @@ struct inode *ext4_iget(struct super_block *sb, unsigned long ino)
>                         inode->i_op = &ext4_symlink_inode_operations;
>                         ext4_set_aops(inode);
>                 }
> +               inode_nohighmem(inode);
>         } else if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode) ||
>               S_ISFIFO(inode->i_mode) || S_ISSOCK(inode->i_mode)) {
>                 inode->i_op = &ext4_special_inode_operations;
> diff --git a/fs/ext4/namei.c b/fs/ext4/namei.c
> index a969ab3..f27e0c2 100644
> --- a/fs/ext4/namei.c
> +++ b/fs/ext4/namei.c
> @@ -3132,6 +3132,7 @@ static int ext4_symlink(struct inode *dir,
>         if ((disk_link.len > EXT4_N_BLOCKS * 4)) {
>                 if (!encryption_required)
>                         inode->i_op = &ext4_symlink_inode_operations;
> +               inode_nohighmem(inode);
>                 ext4_set_aops(inode);
>                 /*
>                  * We cannot call page_symlink() with transaction started
> diff --git a/fs/ext4/symlink.c b/fs/ext4/symlink.c
> index abe2401..0e6dc44 100644
> --- a/fs/ext4/symlink.c
> +++ b/fs/ext4/symlink.c
> @@ -45,7 +45,7 @@ static const char *ext4_encrypted_follow_link(struct dentry *dentry, void **cook
>                 cpage = read_mapping_page(inode->i_mapping, 0, NULL);
>                 if (IS_ERR(cpage))
>                         return ERR_CAST(cpage);
> -               caddr = kmap(cpage);
> +               caddr = page_address(cpage);
>                 caddr[size] = 0;
>         }
>
> @@ -75,16 +75,12 @@ static const char *ext4_encrypted_follow_link(struct dentry *dentry, void **cook
>         /* Null-terminate the name */
>         if (res <= plen)
>                 paddr[res] = '\0';
> -       if (cpage) {
> -               kunmap(cpage);
> +       if (cpage)
>                 page_cache_release(cpage);
> -       }
>         return *cookie = paddr;
>  errout:
> -       if (cpage) {
> -               kunmap(cpage);
> +       if (cpage)
>                 page_cache_release(cpage);
> -       }
>         kfree(paddr);
>         return ERR_PTR(res);
>  }
> diff --git a/fs/f2fs/inode.c b/fs/f2fs/inode.c
> index 97e20de..5528801 100644
> --- a/fs/f2fs/inode.c
> +++ b/fs/f2fs/inode.c
> @@ -202,6 +202,7 @@ make_now:
>                         inode->i_op = &f2fs_encrypted_symlink_inode_operations;
>                 else
>                         inode->i_op = &f2fs_symlink_inode_operations;
> +               inode_nohighmem(inode);
>                 inode->i_mapping->a_ops = &f2fs_dblock_aops;
>         } else if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode) ||
>                         S_ISFIFO(inode->i_mode) || S_ISSOCK(inode->i_mode)) {
> diff --git a/fs/f2fs/namei.c b/fs/f2fs/namei.c
> index 2c32110..484df68 100644
> --- a/fs/f2fs/namei.c
> +++ b/fs/f2fs/namei.c
> @@ -351,6 +351,7 @@ static int f2fs_symlink(struct inode *dir, struct dentry *dentry,
>                 inode->i_op = &f2fs_encrypted_symlink_inode_operations;
>         else
>                 inode->i_op = &f2fs_symlink_inode_operations;
> +       inode_nohighmem(inode);
>         inode->i_mapping->a_ops = &f2fs_dblock_aops;
>
>         f2fs_lock_op(sbi);
> @@ -942,7 +943,7 @@ static const char *f2fs_encrypted_follow_link(struct dentry *dentry, void **cook
>         cpage = read_mapping_page(inode->i_mapping, 0, NULL);
>         if (IS_ERR(cpage))
>                 return ERR_CAST(cpage);
> -       caddr = kmap(cpage);
> +       caddr = page_address(cpage);
>         caddr[size] = 0;
>
>         /* Symlink is encrypted */
> @@ -982,13 +983,11 @@ static const char *f2fs_encrypted_follow_link(struct dentry *dentry, void **cook
>         /* Null-terminate the name */
>         paddr[res] = '\0';
>
> -       kunmap(cpage);
>         page_cache_release(cpage);
>         return *cookie = paddr;
>  errout:
>         kfree(cstr.name);
>         f2fs_fname_crypto_free_buffer(&pstr);
> -       kunmap(cpage);
>         page_cache_release(cpage);
>         return ERR_PTR(res);
>  }
> diff --git a/fs/freevxfs/vxfs_inode.c b/fs/freevxfs/vxfs_inode.c
> index ef73ed6..3e2ccad 100644
> --- a/fs/freevxfs/vxfs_inode.c
> +++ b/fs/freevxfs/vxfs_inode.c
> @@ -326,6 +326,7 @@ vxfs_iget(struct super_block *sbp, ino_t ino)
>         } else if (S_ISLNK(ip->i_mode)) {
>                 if (!VXFS_ISIMMED(vip)) {
>                         ip->i_op = &page_symlink_inode_operations;
> +                       inode_nohighmem(ip);
>                         ip->i_mapping->a_ops = &vxfs_aops;
>                 } else {
>                         ip->i_op = &simple_symlink_inode_operations;
> diff --git a/fs/hfsplus/inode.c b/fs/hfsplus/inode.c
> index 6dd107d..19b33f8 100644
> --- a/fs/hfsplus/inode.c
> +++ b/fs/hfsplus/inode.c
> @@ -403,6 +403,7 @@ struct inode *hfsplus_new_inode(struct super_block *sb, umode_t mode)
>         } else if (S_ISLNK(inode->i_mode)) {
>                 sbi->file_count++;
>                 inode->i_op = &page_symlink_inode_operations;
> +               inode_nohighmem(inode);
>                 inode->i_mapping->a_ops = &hfsplus_aops;
>                 hip->clump_blocks = 1;
>         } else
> @@ -526,6 +527,7 @@ int hfsplus_cat_read_inode(struct inode *inode, struct hfs_find_data *fd)
>                         inode->i_mapping->a_ops = &hfsplus_aops;
>                 } else if (S_ISLNK(inode->i_mode)) {
>                         inode->i_op = &page_symlink_inode_operations;
> +                       inode_nohighmem(inode);
>                         inode->i_mapping->a_ops = &hfsplus_aops;
>                 } else {
>                         init_special_inode(inode, inode->i_mode,
> diff --git a/fs/hpfs/inode.c b/fs/hpfs/inode.c
> index 933c737..1f3c6d7 100644
> --- a/fs/hpfs/inode.c
> +++ b/fs/hpfs/inode.c
> @@ -77,6 +77,7 @@ void hpfs_read_inode(struct inode *i)
>                         kfree(ea);
>                         i->i_mode = S_IFLNK | 0777;
>                         i->i_op = &page_symlink_inode_operations;
> +                       inode_nohighmem(i);
>                         i->i_data.a_ops = &hpfs_symlink_aops;
>                         set_nlink(i, 1);
>                         i->i_size = ea_size;
> diff --git a/fs/hpfs/namei.c b/fs/hpfs/namei.c
> index ae4d5a1..506765a 100644
> --- a/fs/hpfs/namei.c
> +++ b/fs/hpfs/namei.c
> @@ -332,6 +332,7 @@ static int hpfs_symlink(struct inode *dir, struct dentry *dentry, const char *sy
>         result->i_blocks = 1;
>         set_nlink(result, 1);
>         result->i_size = strlen(symlink);
> +       inode_nohighmem(result);
>         result->i_op = &page_symlink_inode_operations;
>         result->i_data.a_ops = &hpfs_symlink_aops;
>
> @@ -500,7 +501,7 @@ out:
>
>  static int hpfs_symlink_readpage(struct file *file, struct page *page)
>  {
> -       char *link = kmap(page);
> +       char *link = page_address(page);
>         struct inode *i = page->mapping->host;
>         struct fnode *fnode;
>         struct buffer_head *bh;
> @@ -516,14 +517,12 @@ static int hpfs_symlink_readpage(struct file *file, struct page *page)
>                 goto fail;
>         hpfs_unlock(i->i_sb);
>         SetPageUptodate(page);
> -       kunmap(page);
>         unlock_page(page);
>         return 0;
>
>  fail:
>         hpfs_unlock(i->i_sb);
>         SetPageError(page);
> -       kunmap(page);
>         unlock_page(page);
>         return err;
>  }
> diff --git a/fs/hugetlbfs/inode.c b/fs/hugetlbfs/inode.c
> index de4bdfa..d8f51ee 100644
> --- a/fs/hugetlbfs/inode.c
> +++ b/fs/hugetlbfs/inode.c
> @@ -760,6 +760,7 @@ static struct inode *hugetlbfs_get_inode(struct super_block *sb,
>                         break;
>                 case S_IFLNK:
>                         inode->i_op = &page_symlink_inode_operations;
> +                       inode_nohighmem(inode);
>                         break;
>                 }
>                 lockdep_annotate_inode_mutex_key(inode);
> diff --git a/fs/inode.c b/fs/inode.c
> index 1be5f90..5bb85a0 100644
> --- a/fs/inode.c
> +++ b/fs/inode.c
> @@ -2028,3 +2028,9 @@ void inode_set_flags(struct inode *inode, unsigned int flags,
>                                   new_flags) != old_flags));
>  }
>  EXPORT_SYMBOL(inode_set_flags);
> +
> +void inode_nohighmem(struct inode *inode)
> +{
> +       mapping_set_gfp_mask(inode->i_mapping, GFP_USER);
> +}
> +EXPORT_SYMBOL(inode_nohighmem);
> diff --git a/fs/isofs/inode.c b/fs/isofs/inode.c
> index d67a16f..61abdc4 100644
> --- a/fs/isofs/inode.c
> +++ b/fs/isofs/inode.c
> @@ -1417,6 +1417,7 @@ static int isofs_read_inode(struct inode *inode, int relocated)
>                 inode->i_fop = &isofs_dir_operations;
>         } else if (S_ISLNK(inode->i_mode)) {
>                 inode->i_op = &page_symlink_inode_operations;
> +               inode_nohighmem(inode);
>                 inode->i_data.a_ops = &isofs_symlink_aops;
>         } else
>                 /* XXX - parse_rock_ridge_inode() had already set i_rdev. */
> diff --git a/fs/isofs/rock.c b/fs/isofs/rock.c
> index 735d752..5384ceb 100644
> --- a/fs/isofs/rock.c
> +++ b/fs/isofs/rock.c
> @@ -687,7 +687,7 @@ static int rock_ridge_symlink_readpage(struct file *file, struct page *page)
>         struct inode *inode = page->mapping->host;
>         struct iso_inode_info *ei = ISOFS_I(inode);
>         struct isofs_sb_info *sbi = ISOFS_SB(inode->i_sb);
> -       char *link = kmap(page);
> +       char *link = page_address(page);
>         unsigned long bufsize = ISOFS_BUFFER_SIZE(inode);
>         struct buffer_head *bh;
>         char *rpnt = link;
> @@ -774,7 +774,6 @@ repeat:
>         brelse(bh);
>         *rpnt = '\0';
>         SetPageUptodate(page);
> -       kunmap(page);
>         unlock_page(page);
>         return 0;
>
> @@ -791,7 +790,6 @@ fail:
>         brelse(bh);
>  error:
>         SetPageError(page);
> -       kunmap(page);
>         unlock_page(page);
>         return -EIO;
>  }
> diff --git a/fs/jfs/inode.c b/fs/jfs/inode.c
> index 41aa3ca..9d9bae6 100644
> --- a/fs/jfs/inode.c
> +++ b/fs/jfs/inode.c
> @@ -60,6 +60,7 @@ struct inode *jfs_iget(struct super_block *sb, unsigned long ino)
>         } else if (S_ISLNK(inode->i_mode)) {
>                 if (inode->i_size >= IDATASIZE) {
>                         inode->i_op = &page_symlink_inode_operations;
> +                       inode_nohighmem(inode);
>                         inode->i_mapping->a_ops = &jfs_aops;
>                 } else {
>                         inode->i_op = &jfs_fast_symlink_inode_operations;
> diff --git a/fs/jfs/namei.c b/fs/jfs/namei.c
> index 9d7551f..701f893 100644
> --- a/fs/jfs/namei.c
> +++ b/fs/jfs/namei.c
> @@ -983,6 +983,7 @@ static int jfs_symlink(struct inode *dip, struct dentry *dentry,
>                 jfs_info("jfs_symlink: allocate extent ip:0x%p", ip);
>
>                 ip->i_op = &jfs_symlink_inode_operations;
> +               inode_nohighmem(ip);
>                 ip->i_mapping->a_ops = &jfs_aops;
>
>                 /*
> diff --git a/fs/logfs/dir.c b/fs/logfs/dir.c
> index 99944a4..542468e 100644
> --- a/fs/logfs/dir.c
> +++ b/fs/logfs/dir.c
> @@ -529,6 +529,7 @@ static int logfs_symlink(struct inode *dir, struct dentry *dentry,
>                 return PTR_ERR(inode);
>
>         inode->i_op = &page_symlink_inode_operations;
> +       inode_nohighmem(inode);
>         inode->i_mapping->a_ops = &logfs_reg_aops;
>
>         return __logfs_create(dir, dentry, inode, target, destlen);
> diff --git a/fs/logfs/inode.c b/fs/logfs/inode.c
> index 06baa92..0fce46d 100644
> --- a/fs/logfs/inode.c
> +++ b/fs/logfs/inode.c
> @@ -65,6 +65,7 @@ static void logfs_inode_setops(struct inode *inode)
>                 break;
>         case S_IFLNK:
>                 inode->i_op = &page_symlink_inode_operations;
> +               inode_nohighmem(inode);
>                 inode->i_mapping->a_ops = &logfs_reg_aops;
>                 break;
>         case S_IFSOCK:  /* fall through */
> diff --git a/fs/minix/inode.c b/fs/minix/inode.c
> index 086cd0a..67a23bf 100644
> --- a/fs/minix/inode.c
> +++ b/fs/minix/inode.c
> @@ -452,6 +452,7 @@ void minix_set_inode(struct inode *inode, dev_t rdev)
>                 inode->i_mapping->a_ops = &minix_aops;
>         } else if (S_ISLNK(inode->i_mode)) {
>                 inode->i_op = &minix_symlink_inode_operations;
> +               inode_nohighmem(inode);
>                 inode->i_mapping->a_ops = &minix_aops;
>         } else
>                 init_special_inode(inode, inode->i_mode, rdev);
> diff --git a/fs/namei.c b/fs/namei.c
> index 4bae5cb..2808958 100644
> --- a/fs/namei.c
> +++ b/fs/namei.c
> @@ -4527,7 +4527,8 @@ static const char *page_getlink(struct dentry * dentry, void **cookie)
>         if (IS_ERR(page))
>                 return (char*)page;
>         *cookie = page;
> -       kaddr = kmap(page);
> +       BUG_ON(mapping_gfp_mask(mapping) & __GFP_HIGHMEM);
> +       kaddr = page_address(page);
>         nd_terminate_link(kaddr, dentry->d_inode->i_size, PAGE_SIZE - 1);
>         return kaddr;
>  }
> @@ -4541,7 +4542,6 @@ EXPORT_SYMBOL(page_follow_link_light);
>  void page_put_link(struct inode *unused, void *cookie)
>  {
>         struct page *page = cookie;
> -       kunmap(page);
>         page_cache_release(page);
>  }
>  EXPORT_SYMBOL(page_put_link);
> @@ -4565,7 +4565,6 @@ int __page_symlink(struct inode *inode, const char *symname, int len, int nofs)
>         struct page *page;
>         void *fsdata;
>         int err;
> -       char *kaddr;
>         unsigned int flags = AOP_FLAG_UNINTERRUPTIBLE;
>         if (nofs)
>                 flags |= AOP_FLAG_NOFS;
> @@ -4576,9 +4575,7 @@ retry:
>         if (err)
>                 goto fail;
>
> -       kaddr = kmap_atomic(page);
> -       memcpy(kaddr, symname, len-1);
> -       kunmap_atomic(kaddr);
> +       memcpy(page_address(page), symname, len-1);
>
>         err = pagecache_write_end(NULL, mapping, 0, len-1, len-1,
>                                                         page, fsdata);
> diff --git a/fs/ncpfs/inode.c b/fs/ncpfs/inode.c
> index 9605a2f..bb856f7 100644
> --- a/fs/ncpfs/inode.c
> +++ b/fs/ncpfs/inode.c
> @@ -283,6 +283,7 @@ ncp_iget(struct super_block *sb, struct ncp_entry_info *info)
>  #if defined(CONFIG_NCPFS_EXTRAS) || defined(CONFIG_NCPFS_NFS_NS)
>                 } else if (S_ISLNK(inode->i_mode)) {
>                         inode->i_op = &ncp_symlink_inode_operations;
> +                       inode_nohighmem(inode);
>                         inode->i_data.a_ops = &ncp_symlink_aops;
>  #endif
>                 } else {
> diff --git a/fs/nfs/inode.c b/fs/nfs/inode.c
> index 31b0a52..ae9aa0b 100644
> --- a/fs/nfs/inode.c
> +++ b/fs/nfs/inode.c
> @@ -408,9 +408,10 @@ nfs_fhget(struct super_block *sb, struct nfs_fh *fh, struct nfs_fattr *fattr, st
>                                 inode->i_fop = NULL;
>                                 inode->i_flags |= S_AUTOMOUNT;
>                         }
> -               } else if (S_ISLNK(inode->i_mode))
> +               } else if (S_ISLNK(inode->i_mode)) {
>                         inode->i_op = &nfs_symlink_inode_operations;
> -               else
> +                       inode_nohighmem(inode);
> +               } else
>                         init_special_inode(inode, inode->i_mode, fattr->rdev);
>
>                 memset(&inode->i_atime, 0, sizeof(inode->i_atime));
> diff --git a/fs/nfs/symlink.c b/fs/nfs/symlink.c
> index b6de433..abd93bf 100644
> --- a/fs/nfs/symlink.c
> +++ b/fs/nfs/symlink.c
> @@ -56,7 +56,7 @@ static const char *nfs_follow_link(struct dentry *dentry, void **cookie)
>         if (IS_ERR(page))
>                 return ERR_CAST(page);
>         *cookie = page;
> -       return kmap(page);
> +       return page_address(page);
>  }
>
>  /*
> diff --git a/fs/nilfs2/inode.c b/fs/nilfs2/inode.c
> index ac2f649..10b2252 100644
> --- a/fs/nilfs2/inode.c
> +++ b/fs/nilfs2/inode.c
> @@ -510,6 +510,7 @@ static int __nilfs_read_inode(struct super_block *sb,
>                 inode->i_mapping->a_ops = &nilfs_aops;
>         } else if (S_ISLNK(inode->i_mode)) {
>                 inode->i_op = &nilfs_symlink_inode_operations;
> +               inode_nohighmem(inode);
>                 inode->i_mapping->a_ops = &nilfs_aops;
>         } else {
>                 inode->i_op = &nilfs_special_inode_operations;
> diff --git a/fs/nilfs2/namei.c b/fs/nilfs2/namei.c
> index c9a1a49..90b3ba9 100644
> --- a/fs/nilfs2/namei.c
> +++ b/fs/nilfs2/namei.c
> @@ -161,6 +161,7 @@ static int nilfs_symlink(struct inode *dir, struct dentry *dentry,
>
>         /* slow symlink */
>         inode->i_op = &nilfs_symlink_inode_operations;
> +       inode_nohighmem(inode);
>         inode->i_mapping->a_ops = &nilfs_aops;
>         err = page_symlink(inode, symname, l);
>         if (err)
> diff --git a/fs/ocfs2/inode.c b/fs/ocfs2/inode.c
> index 8f87e05..97a563b 100644
> --- a/fs/ocfs2/inode.c
> +++ b/fs/ocfs2/inode.c
> @@ -361,6 +361,7 @@ void ocfs2_populate_inode(struct inode *inode, struct ocfs2_dinode *fe,
>                     break;
>             case S_IFLNK:
>                     inode->i_op = &ocfs2_symlink_inode_operations;
> +                   inode_nohighmem(inode);
>                     i_size_write(inode, le64_to_cpu(fe->i_size));
>                     break;
>             default:
> diff --git a/fs/ocfs2/namei.c b/fs/ocfs2/namei.c
> index a03f6f4..2efe8af 100644
> --- a/fs/ocfs2/namei.c
> +++ b/fs/ocfs2/namei.c
> @@ -1960,6 +1960,7 @@ static int ocfs2_symlink(struct inode *dir,
>         inode->i_rdev = 0;
>         newsize = l - 1;
>         inode->i_op = &ocfs2_symlink_inode_operations;
> +       inode_nohighmem(inode);
>         if (l > ocfs2_fast_symlink_chars(sb)) {
>                 u32 offset = 0;
>
> diff --git a/fs/qnx4/inode.c b/fs/qnx4/inode.c
> index c4bcb77..f37b3de 100644
> --- a/fs/qnx4/inode.c
> +++ b/fs/qnx4/inode.c
> @@ -316,6 +316,7 @@ struct inode *qnx4_iget(struct super_block *sb, unsigned long ino)
>                 inode->i_fop = &qnx4_dir_operations;
>         } else if (S_ISLNK(inode->i_mode)) {
>                 inode->i_op = &page_symlink_inode_operations;
> +               inode_nohighmem(inode);
>                 inode->i_mapping->a_ops = &qnx4_aops;
>                 qnx4_i(inode)->mmu_private = inode->i_size;
>         } else {
> diff --git a/fs/qnx6/inode.c b/fs/qnx6/inode.c
> index 32d2e1a..9728b54 100644
> --- a/fs/qnx6/inode.c
> +++ b/fs/qnx6/inode.c
> @@ -582,6 +582,7 @@ struct inode *qnx6_iget(struct super_block *sb, unsigned ino)
>                 inode->i_mapping->a_ops = &qnx6_aops;
>         } else if (S_ISLNK(inode->i_mode)) {
>                 inode->i_op = &page_symlink_inode_operations;
> +               inode_nohighmem(inode);
>                 inode->i_mapping->a_ops = &qnx6_aops;
>         } else
>                 init_special_inode(inode, inode->i_mode, 0);
> diff --git a/fs/ramfs/inode.c b/fs/ramfs/inode.c
> index 889d558..38981b0 100644
> --- a/fs/ramfs/inode.c
> +++ b/fs/ramfs/inode.c
> @@ -79,6 +79,7 @@ struct inode *ramfs_get_inode(struct super_block *sb,
>                         break;
>                 case S_IFLNK:
>                         inode->i_op = &page_symlink_inode_operations;
> +                       inode_nohighmem(inode);
>                         break;
>                 }
>         }
> diff --git a/fs/reiserfs/inode.c b/fs/reiserfs/inode.c
> index 3d8e7e6..ae9e5b3 100644
> --- a/fs/reiserfs/inode.c
> +++ b/fs/reiserfs/inode.c
> @@ -1361,6 +1361,7 @@ static void init_inode(struct inode *inode, struct treepath *path)
>                 inode->i_fop = &reiserfs_dir_operations;
>         } else if (S_ISLNK(inode->i_mode)) {
>                 inode->i_op = &reiserfs_symlink_inode_operations;
> +               inode_nohighmem(inode);
>                 inode->i_mapping->a_ops = &reiserfs_address_space_operations;
>         } else {
>                 inode->i_blocks = 0;
> diff --git a/fs/reiserfs/namei.c b/fs/reiserfs/namei.c
> index 47f9698..4fc2326 100644
> --- a/fs/reiserfs/namei.c
> +++ b/fs/reiserfs/namei.c
> @@ -1170,6 +1170,7 @@ static int reiserfs_symlink(struct inode *parent_dir,
>         reiserfs_update_inode_transaction(parent_dir);
>
>         inode->i_op = &reiserfs_symlink_inode_operations;
> +       inode_nohighmem(inode);
>         inode->i_mapping->a_ops = &reiserfs_address_space_operations;
>
>         retval = reiserfs_add_entry(&th, parent_dir, dentry->d_name.name,
> diff --git a/fs/romfs/super.c b/fs/romfs/super.c
> index 268733c..bb894e7 100644
> --- a/fs/romfs/super.c
> +++ b/fs/romfs/super.c
> @@ -360,6 +360,7 @@ static struct inode *romfs_iget(struct super_block *sb, unsigned long pos)
>                 break;
>         case ROMFH_SYM:
>                 i->i_op = &page_symlink_inode_operations;
> +               inode_nohighmem(i);
>                 i->i_data.a_ops = &romfs_aops;
>                 mode |= S_IRWXUGO;
>                 break;
> diff --git a/fs/squashfs/inode.c b/fs/squashfs/inode.c
> index a1ce5ce..0927b1e 100644
> --- a/fs/squashfs/inode.c
> +++ b/fs/squashfs/inode.c
> @@ -41,6 +41,7 @@
>  #include <linux/fs.h>
>  #include <linux/vfs.h>
>  #include <linux/xattr.h>
> +#include <linux/pagemap.h>
>
>  #include "squashfs_fs.h"
>  #include "squashfs_fs_sb.h"
> @@ -291,6 +292,7 @@ int squashfs_read_inode(struct inode *inode, long long ino)
>                 set_nlink(inode, le32_to_cpu(sqsh_ino->nlink));
>                 inode->i_size = le32_to_cpu(sqsh_ino->symlink_size);
>                 inode->i_op = &squashfs_symlink_inode_ops;
> +               inode_nohighmem(inode);
>                 inode->i_data.a_ops = &squashfs_symlink_aops;
>                 inode->i_mode |= S_IFLNK;
>                 squashfs_i(inode)->start = block;
> diff --git a/fs/sysv/inode.c b/fs/sysv/inode.c
> index 02fa1dc..ef8bcdb 100644
> --- a/fs/sysv/inode.c
> +++ b/fs/sysv/inode.c
> @@ -163,6 +163,7 @@ void sysv_set_inode(struct inode *inode, dev_t rdev)
>                 inode->i_mapping->a_ops = &sysv_aops;
>         } else if (S_ISLNK(inode->i_mode)) {
>                 inode->i_op = &sysv_symlink_inode_operations;
> +               inode_nohighmem(inode);
>                 inode->i_mapping->a_ops = &sysv_aops;
>         } else
>                 init_special_inode(inode, inode->i_mode, rdev);
> diff --git a/fs/udf/inode.c b/fs/udf/inode.c
> index 8675c2b..0557463 100644
> --- a/fs/udf/inode.c
> +++ b/fs/udf/inode.c
> @@ -1541,6 +1541,7 @@ reread:
>         case ICBTAG_FILE_TYPE_SYMLINK:
>                 inode->i_data.a_ops = &udf_symlink_aops;
>                 inode->i_op = &page_symlink_inode_operations;
> +               inode_nohighmem(inode);
>                 inode->i_mode = S_IFLNK | S_IRWXUGO;
>                 break;
>         case ICBTAG_FILE_TYPE_MAIN:
> diff --git a/fs/udf/namei.c b/fs/udf/namei.c
> index d0e6de1..42eafb9 100644
> --- a/fs/udf/namei.c
> +++ b/fs/udf/namei.c
> @@ -922,6 +922,7 @@ static int udf_symlink(struct inode *dir, struct dentry *dentry,
>
>         inode->i_data.a_ops = &udf_symlink_aops;
>         inode->i_op = &page_symlink_inode_operations;
> +       inode_nohighmem(inode);
>
>         if (iinfo->i_alloc_type != ICBTAG_FLAG_AD_IN_ICB) {
>                 struct kernel_lb_addr eloc;
> diff --git a/fs/udf/symlink.c b/fs/udf/symlink.c
> index 862535b..8d61977 100644
> --- a/fs/udf/symlink.c
> +++ b/fs/udf/symlink.c
> @@ -107,7 +107,7 @@ static int udf_symlink_filler(struct file *file, struct page *page)
>         struct buffer_head *bh = NULL;
>         unsigned char *symlink;
>         int err;
> -       unsigned char *p = kmap(page);
> +       unsigned char *p = page_address(page);
>         struct udf_inode_info *iinfo;
>         uint32_t pos;
>
> @@ -141,7 +141,6 @@ static int udf_symlink_filler(struct file *file, struct page *page)
>
>         up_read(&iinfo->i_data_sem);
>         SetPageUptodate(page);
> -       kunmap(page);
>         unlock_page(page);
>         return 0;
>
> @@ -149,7 +148,6 @@ out_unlock_inode:
>         up_read(&iinfo->i_data_sem);
>         SetPageError(page);
>  out_unmap:
> -       kunmap(page);
>         unlock_page(page);
>         return err;
>  }
> diff --git a/fs/ufs/inode.c b/fs/ufs/inode.c
> index 737160a..d897e16 100644
> --- a/fs/ufs/inode.c
> +++ b/fs/ufs/inode.c
> @@ -533,6 +533,7 @@ static void ufs_set_inode_ops(struct inode *inode)
>                 } else {
>                         inode->i_mapping->a_ops = &ufs_aops;
>                         inode->i_op = &page_symlink_inode_operations;
> +                       inode_nohighmem(inode);
>                 }
>         } else
>                 init_special_inode(inode, inode->i_mode,
> diff --git a/fs/ufs/namei.c b/fs/ufs/namei.c
> index 24b0cbd..acf4a3b 100644
> --- a/fs/ufs/namei.c
> +++ b/fs/ufs/namei.c
> @@ -124,6 +124,7 @@ static int ufs_symlink (struct inode * dir, struct dentry * dentry,
>         if (l > UFS_SB(sb)->s_uspi->s_maxsymlinklen) {
>                 /* slow symlink */
>                 inode->i_op = &page_symlink_inode_operations;
> +               inode_nohighmem(inode);
>                 inode->i_mapping->a_ops = &ufs_aops;
>                 err = page_symlink(inode, symname, l);
>                 if (err)
> diff --git a/include/linux/fs.h b/include/linux/fs.h
> index 3aa5142..dfeda44 100644
> --- a/include/linux/fs.h
> +++ b/include/linux/fs.h
> @@ -3025,5 +3025,6 @@ static inline bool dir_relax(struct inode *inode)
>  }
>
>  extern bool path_noexec(const struct path *path);
> +extern void inode_nohighmem(struct inode *inode);
>
>  #endif /* _LINUX_FS_H */
> diff --git a/mm/shmem.c b/mm/shmem.c
> index 9187eee..64bf5ac 100644
> --- a/mm/shmem.c
> +++ b/mm/shmem.c
> @@ -2444,7 +2444,6 @@ static int shmem_symlink(struct inode *dir, struct dentry *dentry, const char *s
>         int len;
>         struct inode *inode;
>         struct page *page;
> -       char *kaddr;
>         struct shmem_inode_info *info;
>
>         len = strlen(symname) + 1;
> @@ -2483,9 +2482,8 @@ static int shmem_symlink(struct inode *dir, struct dentry *dentry, const char *s
>                 }
>                 inode->i_mapping->a_ops = &shmem_aops;
>                 inode->i_op = &shmem_symlink_inode_operations;
> -               kaddr = kmap_atomic(page);
> -               memcpy(kaddr, symname, len);
> -               kunmap_atomic(kaddr);
> +               inode_nohighmem(inode);
> +               memcpy(page_address(page), symname, len);
>                 SetPageUptodate(page);
>                 set_page_dirty(page);
>                 unlock_page(page);
> @@ -2506,13 +2504,12 @@ static const char *shmem_follow_link(struct dentry *dentry, void **cookie)
>                 return ERR_PTR(error);
>         unlock_page(page);
>         *cookie = page;
> -       return kmap(page);
> +       return page_address(page);
>  }
>
>  static void shmem_put_link(struct inode *unused, void *cookie)
>  {
>         struct page *page = cookie;
> -       kunmap(page);
>         mark_page_accessed(page);
>         page_cache_release(page);
>  }
> --
> 2.1.4
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
> Please read the FAQ at  http://www.tux.org/lkml/
--
To unsubscribe from this list: send the line "unsubscribe linux-fsdevel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Al Viro Jan. 14, 2016, 3:25 p.m. UTC | #2
On Thu, Jan 14, 2016 at 02:22:51PM +0100, Tomeu Vizoso wrote:
> On 9 December 2015 at 06:34, Al Viro <viro@zeniv.linux.org.uk> wrote:
> > From: Al Viro <viro@zeniv.linux.org.uk>
> >
> > kmap() in page_follow_link_light() needed to go - allowing to hold
> > an arbitrary number of kmaps for long is a great way to deadlocking
> > the system.
> >
> > new helper (inode_nohighmem(inode)) needs to be used for pagecache
> > symlinks inodes; done for all in-tree cases.  page_follow_link_light()
> > instrumented to yell about anything missed.
> 
> Hi, starting with with this change, I get this oops when installing
> packages into a rootfs in NFS:

Lovely...  So you somehow getting a highmem page out
                page = read_cache_page(&inode->i_data, 0,
                                        (filler_t *)nfs_symlink_filler, inode);
                if (IS_ERR(page))
                        return ERR_CAST(page);
and that - after
                        inode_nohighmem(inode);
(otherwise you wouldn't get nfs_symlink_inode_operations on that inode).

Could you add
	printk(KERN_ERR "i_data = %p, i_mapping = %p, flags: %lx\n",
		&inode->i_data,
		inode->i_mapping,
		(unsigned long)inode->i_data.flags);
right before the return from nfs_get_link() and see what it prints?
--
To unsubscribe from this list: send the line "unsubscribe linux-fsdevel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Tomeu Vizoso Jan. 14, 2016, 3:58 p.m. UTC | #3
On 14 January 2016 at 16:25, Al Viro <viro@zeniv.linux.org.uk> wrote:
> On Thu, Jan 14, 2016 at 02:22:51PM +0100, Tomeu Vizoso wrote:
>> On 9 December 2015 at 06:34, Al Viro <viro@zeniv.linux.org.uk> wrote:
>> > From: Al Viro <viro@zeniv.linux.org.uk>
>> >
>> > kmap() in page_follow_link_light() needed to go - allowing to hold
>> > an arbitrary number of kmaps for long is a great way to deadlocking
>> > the system.
>> >
>> > new helper (inode_nohighmem(inode)) needs to be used for pagecache
>> > symlinks inodes; done for all in-tree cases.  page_follow_link_light()
>> > instrumented to yell about anything missed.
>>
>> Hi, starting with with this change, I get this oops when installing
>> packages into a rootfs in NFS:
>
> Lovely...  So you somehow getting a highmem page out
>                 page = read_cache_page(&inode->i_data, 0,
>                                         (filler_t *)nfs_symlink_filler, inode);
>                 if (IS_ERR(page))
>                         return ERR_CAST(page);
> and that - after
>                         inode_nohighmem(inode);
> (otherwise you wouldn't get nfs_symlink_inode_operations on that inode).
>
> Could you add
>         printk(KERN_ERR "i_data = %p, i_mapping = %p, flags: %lx\n",
>                 &inode->i_data,
>                 inode->i_mapping,
>                 (unsigned long)inode->i_data.flags);
> right before the return from nfs_get_link() and see what it prints?

Here it is:

[  170.136956] i_data = ed9c1b04, i_mapping = ed9c1b04, flags: 24200c0
[  170.144567] i_data = ed9de784, i_mapping = ed9de784, flags: 24200c0
[  170.151457] i_data = ed9dec84, i_mapping = ed9dec84, flags: 24200c0
[  170.158358] i_data = ed9c3b84, i_mapping = ed9c3b84, flags: 24200c0
[  170.165253] i_data = ed9d4204, i_mapping = ed9d4204, flags: 24200c0
[  170.172131] i_data = ed9df184, i_mapping = ed9df184, flags: 24200c0
[  170.188804] i_data = eddbce84, i_mapping = eddbce84, flags: 24200c0
[  170.196158] i_data = ec904984, i_mapping = ec904984, flags: 24200c0
[  170.205133] i_data = ec906784, i_mapping = ec906784, flags: 24200c0
[  170.211406] Unable to handle kernel NULL pointer dereference at
virtual address 00000000
[  170.219490] pgd = ee7f0000
[  170.222197] [00000000] *pgd=7b85b835
[  170.225784] Internal error: Oops: 17 [#1] SMP ARM
[  170.230477] Modules linked in:
[  170.233537] CPU: 2 PID: 1 Comm: systemd Not tainted
4.4.0-next-20160114-00005-g6f86169c2250-dirty #3525
[  170.242910] Hardware name: Rockchip (Device Tree)
[  170.247604] task: ee078000 ti: ee062000 task.ti: ee062000
[  170.252996] PC is at strlen+0x0/0x2c
[  170.256565] LR is at readlink_copy+0x24/0x94
[  170.260826] pc : [<c049b138>]    lr : [<c0321160>]    psr: 00000013
[  170.260826] sp : ee063f38  ip : 00000000  fp : ec9066b0
[  170.272281] r10: 001a49f8  r9 : 00000063  r8 : ee063f74
[  170.277494] r7 : 001a4968  r6 : 001a49f8  r5 : 00000000  r4 : 00000063
[  170.284008] r3 : 0000012c  r2 : 00000000  r1 : 00000063  r0 : 00000000
[  170.290522] Flags: nzcv  IRQs on  FIQs on  Mode SVC_32  ISA ARM  Segment none
[  170.297642] Control: 10c5387d  Table: 2e7f006a  DAC: 00000051
[  170.303375] Process systemd (pid: 1, stack limit = 0xee062220)
[  170.309196] Stack: (0xee063f38 to 0xee064000)
[  170.313542] 3f20:
    001a49f8 00000063
[  170.321705] 3f40: ffffff9c 001a4968 ee063f74 c0321208 c0321250
eff6b920 ffffffea ffffffea
[  170.329868] 3f60: 00004000 c031c234 00000000 00000000 00000025
00000000 ee102b50 ec874990
[  170.338030] 3f80: 5697c41b 001a49f8 00000064 00000063 0000014c
c0210e84 ee062000 00000000
[  170.346193] 3fa0: 001a4968 c0210cc0 001a49f8 00000064 ffffff9c
001a4968 001a49f8 00000063
[  170.354355] 3fc0: 001a49f8 00000064 00000063 0000014c bec450b8
001c1094 001d0933 001a4968
[  170.362518] 3fe0: 0000014c bec45094 b6f2af7b b6eb88e6 20000030
ffffff9c ffffffff ffeffffe
[  170.370683] [<c049b138>] (strlen) from [<c0321160>] (readlink_copy+0x24/0x94)
[  170.377806] [<c0321160>] (readlink_copy) from [<c0321208>]
(generic_readlink+0x38/0x80)
[  170.385796] [<c0321208>] (generic_readlink) from [<c031c234>]
(SyS_readlinkat+0x98/0xe0)
[  170.393874] [<c031c234>] (SyS_readlinkat) from [<c0210cc0>]
(ret_fast_syscall+0x0/0x3c)
[  170.401863] Code: e7d23003 e3130020 1afffffb e12fff1e (e5d02000)
[  170.407965] ---[ end trace 87f95166dedbabb0 ]---

Full log at https://lava.collabora.co.uk/scheduler/job/127626/log_file

Regards,

Tomeu
--
To unsubscribe from this list: send the line "unsubscribe linux-fsdevel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Al Viro Jan. 14, 2016, 4:23 p.m. UTC | #4
On Thu, Jan 14, 2016 at 04:58:48PM +0100, Tomeu Vizoso wrote:
> > Could you add
> >         printk(KERN_ERR "i_data = %p, i_mapping = %p, flags: %lx\n",
> >                 &inode->i_data,
> >                 inode->i_mapping,
> >                 (unsigned long)inode->i_data.flags);
> > right before the return from nfs_get_link() and see what it prints?
> 
> Here it is:
> 
> [  170.136956] i_data = ed9c1b04, i_mapping = ed9c1b04, flags: 24200c0
> [  170.144567] i_data = ed9de784, i_mapping = ed9de784, flags: 24200c0
> [  170.151457] i_data = ed9dec84, i_mapping = ed9dec84, flags: 24200c0
> [  170.158358] i_data = ed9c3b84, i_mapping = ed9c3b84, flags: 24200c0
> [  170.165253] i_data = ed9d4204, i_mapping = ed9d4204, flags: 24200c0
> [  170.172131] i_data = ed9df184, i_mapping = ed9df184, flags: 24200c0
> [  170.188804] i_data = eddbce84, i_mapping = eddbce84, flags: 24200c0
> [  170.196158] i_data = ec904984, i_mapping = ec904984, flags: 24200c0
> [  170.205133] i_data = ec906784, i_mapping = ec906784, flags: 24200c0

Aha.  So ->i_data vs. ->i_mapping is irrelevant (as it ought to be here)
and inode_nohighmem() should've acted on the address_space we are hitting
here.  What do we have in flags...  ___GFP_IO | ___GFP_FS | ___GFP_HARDWALL |
___GFP_DIRECT_RECLAIM | ___GFP_KSWAPD_RECLAIM.  IOW, normal GFP_USER, no
__GFP_HIGHMEM in sight.

So either we have a highmem page somehow ending up in i_data before we
set the flags, or __page_cache_alloc() done by read_cache_page() returns
us a highmem page on GFP_USER | __GFP_COLD (or I'm misreading the things
completely)...

Could you slap
	printk(KERN_ERR "inode: %p, pages: %ld\n",
		inode, inode->i_data.nrpages);
before that read_cache_page() in nfs_get_link() and
	printk(KERN_ERR "page_address: %p\n", page_address(page));
right before the return?
--
To unsubscribe from this list: send the line "unsubscribe linux-fsdevel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Tomeu Vizoso Jan. 14, 2016, 4:57 p.m. UTC | #5
On 14 January 2016 at 17:23, Al Viro <viro@zeniv.linux.org.uk> wrote:
> On Thu, Jan 14, 2016 at 04:58:48PM +0100, Tomeu Vizoso wrote:
>> > Could you add
>> >         printk(KERN_ERR "i_data = %p, i_mapping = %p, flags: %lx\n",
>> >                 &inode->i_data,
>> >                 inode->i_mapping,
>> >                 (unsigned long)inode->i_data.flags);
>> > right before the return from nfs_get_link() and see what it prints?
>>
>> Here it is:
>>
>> [  170.136956] i_data = ed9c1b04, i_mapping = ed9c1b04, flags: 24200c0
>> [  170.144567] i_data = ed9de784, i_mapping = ed9de784, flags: 24200c0
>> [  170.151457] i_data = ed9dec84, i_mapping = ed9dec84, flags: 24200c0
>> [  170.158358] i_data = ed9c3b84, i_mapping = ed9c3b84, flags: 24200c0
>> [  170.165253] i_data = ed9d4204, i_mapping = ed9d4204, flags: 24200c0
>> [  170.172131] i_data = ed9df184, i_mapping = ed9df184, flags: 24200c0
>> [  170.188804] i_data = eddbce84, i_mapping = eddbce84, flags: 24200c0
>> [  170.196158] i_data = ec904984, i_mapping = ec904984, flags: 24200c0
>> [  170.205133] i_data = ec906784, i_mapping = ec906784, flags: 24200c0
>
> Aha.  So ->i_data vs. ->i_mapping is irrelevant (as it ought to be here)
> and inode_nohighmem() should've acted on the address_space we are hitting
> here.  What do we have in flags...  ___GFP_IO | ___GFP_FS | ___GFP_HARDWALL |
> ___GFP_DIRECT_RECLAIM | ___GFP_KSWAPD_RECLAIM.  IOW, normal GFP_USER, no
> __GFP_HIGHMEM in sight.
>
> So either we have a highmem page somehow ending up in i_data before we
> set the flags, or __page_cache_alloc() done by read_cache_page() returns
> us a highmem page on GFP_USER | __GFP_COLD (or I'm misreading the things
> completely)...
>
> Could you slap
>         printk(KERN_ERR "inode: %p, pages: %ld\n",
>                 inode, inode->i_data.nrpages);
> before that read_cache_page() in nfs_get_link() and
>         printk(KERN_ERR "page_address: %p\n", page_address(page));
> right before the return?

Here it is:

[  170.715356] inode: ec8c30b0, pages: 1
[  170.719014] page_address:   (null)

https://lava.collabora.co.uk/scheduler/job/127698/log_file

Regards,

Tomeu
--
To unsubscribe from this list: send the line "unsubscribe linux-fsdevel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Al Viro Jan. 14, 2016, 5:13 p.m. UTC | #6
On Thu, Jan 14, 2016 at 05:57:42PM +0100, Tomeu Vizoso wrote:
> Here it is:
> 
> [  170.715356] inode: ec8c30b0, pages: 1
> [  170.719014] page_address:   (null)
> 
> https://lava.collabora.co.uk/scheduler/job/127698/log_file

	Lovely...  And that looks like the first time that inode hits
nfs_get_link().  Ho-hum...

	Could you add WARN_ON(inode->i_mapping.nrpages) in inode_nohighmem()
and see if that triggers?  It really shouldn't (we hit it after iget5_locked()
in nfs_fhget() has returned us a new inode, and there shouldn't be a chance
for any pages to get in there between struct inode allocation and that
point), but then neither should highmem pages be added to address_space
without __GFP_HIGHMEM in ->flags...
--
To unsubscribe from this list: send the line "unsubscribe linux-fsdevel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
diff mbox

Patch

diff --git a/Documentation/filesystems/porting b/Documentation/filesystems/porting
index f24d1b8..3eb7c35 100644
--- a/Documentation/filesystems/porting
+++ b/Documentation/filesystems/porting
@@ -504,3 +504,8 @@  in your dentry operations instead.
 [mandatory]
 	__fd_install() & fd_install() can now sleep. Callers should not
 	hold a spinlock	or other resources that do not allow a schedule.
+--
+[mandatory]
+	any symlink that might use page_follow_link_light/page_put_link() must
+	have inode_nohighmem(inode) called before anything might start playing with
+	its pagecache.
diff --git a/fs/affs/inode.c b/fs/affs/inode.c
index 1734950..0fdb0f5 100644
--- a/fs/affs/inode.c
+++ b/fs/affs/inode.c
@@ -140,6 +140,7 @@  struct inode *affs_iget(struct super_block *sb, unsigned long ino)
 		break;
 	case ST_SOFTLINK:
 		inode->i_mode |= S_IFLNK;
+		inode_nohighmem(inode);
 		inode->i_op = &affs_symlink_inode_operations;
 		inode->i_data.a_ops = &affs_symlink_aops;
 		break;
diff --git a/fs/affs/namei.c b/fs/affs/namei.c
index 181e05b..00d3002 100644
--- a/fs/affs/namei.c
+++ b/fs/affs/namei.c
@@ -344,6 +344,7 @@  affs_symlink(struct inode *dir, struct dentry *dentry, const char *symname)
 		return -ENOSPC;
 
 	inode->i_op = &affs_symlink_inode_operations;
+	inode_nohighmem(inode);
 	inode->i_data.a_ops = &affs_symlink_aops;
 	inode->i_mode = S_IFLNK | 0777;
 	mode_to_prot(inode);
diff --git a/fs/affs/symlink.c b/fs/affs/symlink.c
index ea5b69a..e3f9dc3 100644
--- a/fs/affs/symlink.c
+++ b/fs/affs/symlink.c
@@ -14,7 +14,7 @@  static int affs_symlink_readpage(struct file *file, struct page *page)
 {
 	struct buffer_head *bh;
 	struct inode *inode = page->mapping->host;
-	char *link = kmap(page);
+	char *link = page_address(page);
 	struct slink_front *lf;
 	int			 i, j;
 	char			 c;
@@ -57,12 +57,10 @@  static int affs_symlink_readpage(struct file *file, struct page *page)
 	link[i] = '\0';
 	affs_brelse(bh);
 	SetPageUptodate(page);
-	kunmap(page);
 	unlock_page(page);
 	return 0;
 fail:
 	SetPageError(page);
-	kunmap(page);
 	unlock_page(page);
 	return -EIO;
 }
diff --git a/fs/afs/inode.c b/fs/afs/inode.c
index e06f5a2..86cc726 100644
--- a/fs/afs/inode.c
+++ b/fs/afs/inode.c
@@ -56,6 +56,7 @@  static int afs_inode_map_status(struct afs_vnode *vnode, struct key *key)
 	case AFS_FTYPE_SYMLINK:
 		inode->i_mode	= S_IFLNK | vnode->status.mode;
 		inode->i_op	= &page_symlink_inode_operations;
+		inode_nohighmem(inode);
 		break;
 	default:
 		printk("kAFS: AFS vnode with undefined type\n");
diff --git a/fs/befs/linuxvfs.c b/fs/befs/linuxvfs.c
index 1c8b0dc..25250fa 100644
--- a/fs/befs/linuxvfs.c
+++ b/fs/befs/linuxvfs.c
@@ -397,6 +397,7 @@  static struct inode *befs_iget(struct super_block *sb, unsigned long ino)
 	} else if (S_ISLNK(inode->i_mode)) {
 		if (befs_ino->i_flags & BEFS_LONG_SYMLINK) {
 			inode->i_op = &page_symlink_inode_operations;
+			inode_nohighmem(inode);
 			inode->i_mapping->a_ops = &befs_symlink_aops;
 		} else {
 			inode->i_link = befs_ino->i_data.symlink;
@@ -469,7 +470,7 @@  static int befs_symlink_readpage(struct file *unused, struct page *page)
 	struct befs_inode_info *befs_ino = BEFS_I(inode);
 	befs_data_stream *data = &befs_ino->i_data.ds;
 	befs_off_t len = data->size;
-	char *link = kmap(page);
+	char *link = page_address(page);
 
 	if (len == 0 || len > PAGE_SIZE) {
 		befs_error(sb, "Long symlink with illegal length");
@@ -483,12 +484,10 @@  static int befs_symlink_readpage(struct file *unused, struct page *page)
 	}
 	link[len - 1] = '\0';
 	SetPageUptodate(page);
-	kunmap(page);
 	unlock_page(page);
 	return 0;
 fail:
 	SetPageError(page);
-	kunmap(page);
 	unlock_page(page);
 	return -EIO;
 }
diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c
index a70c579..70f98bf 100644
--- a/fs/btrfs/inode.c
+++ b/fs/btrfs/inode.c
@@ -3774,6 +3774,7 @@  cache_acl:
 		break;
 	case S_IFLNK:
 		inode->i_op = &btrfs_symlink_inode_operations;
+		inode_nohighmem(inode);
 		inode->i_mapping->a_ops = &btrfs_symlink_aops;
 		break;
 	default:
@@ -9705,6 +9706,7 @@  static int btrfs_symlink(struct inode *dir, struct dentry *dentry,
 	btrfs_free_path(path);
 
 	inode->i_op = &btrfs_symlink_inode_operations;
+	inode_nohighmem(inode);
 	inode->i_mapping->a_ops = &btrfs_symlink_aops;
 	inode_set_bytes(inode, name_len);
 	btrfs_i_size_write(inode, name_len);
diff --git a/fs/coda/cnode.c b/fs/coda/cnode.c
index 7740b1c..dd6a79e 100644
--- a/fs/coda/cnode.c
+++ b/fs/coda/cnode.c
@@ -8,6 +8,7 @@ 
 
 #include <linux/coda.h>
 #include <linux/coda_psdev.h>
+#include <linux/pagemap.h>
 #include "coda_linux.h"
 
 static inline int coda_fideq(struct CodaFid *fid1, struct CodaFid *fid2)
@@ -35,6 +36,7 @@  static void coda_fill_inode(struct inode *inode, struct coda_vattr *attr)
                 inode->i_fop = &coda_dir_operations;
         } else if (S_ISLNK(inode->i_mode)) {
 		inode->i_op = &coda_symlink_inode_operations;
+		inode_nohighmem(inode);
 		inode->i_data.a_ops = &coda_symlink_aops;
 		inode->i_mapping = &inode->i_data;
 	} else
diff --git a/fs/coda/symlink.c b/fs/coda/symlink.c
index ab94ef6..03736e2 100644
--- a/fs/coda/symlink.c
+++ b/fs/coda/symlink.c
@@ -26,7 +26,7 @@  static int coda_symlink_filler(struct file *file, struct page *page)
 	int error;
 	struct coda_inode_info *cii;
 	unsigned int len = PAGE_SIZE;
-	char *p = kmap(page);
+	char *p = page_address(page);
 
 	cii = ITOC(inode);
 
@@ -34,13 +34,11 @@  static int coda_symlink_filler(struct file *file, struct page *page)
 	if (error)
 		goto fail;
 	SetPageUptodate(page);
-	kunmap(page);
 	unlock_page(page);
 	return 0;
 
 fail:
 	SetPageError(page);
-	kunmap(page);
 	unlock_page(page);
 	return error;
 }
diff --git a/fs/cramfs/inode.c b/fs/cramfs/inode.c
index 355c522..b862bc2 100644
--- a/fs/cramfs/inode.c
+++ b/fs/cramfs/inode.c
@@ -100,6 +100,7 @@  static struct inode *get_cramfs_inode(struct super_block *sb,
 		break;
 	case S_IFLNK:
 		inode->i_op = &page_symlink_inode_operations;
+		inode_nohighmem(inode);
 		inode->i_data.a_ops = &cramfs_aops;
 		break;
 	default:
diff --git a/fs/efs/inode.c b/fs/efs/inode.c
index 079d203..cdf0872 100644
--- a/fs/efs/inode.c
+++ b/fs/efs/inode.c
@@ -151,6 +151,7 @@  struct inode *efs_iget(struct super_block *super, unsigned long ino)
 			break;
 		case S_IFLNK:
 			inode->i_op = &page_symlink_inode_operations;
+			inode_nohighmem(inode);
 			inode->i_data.a_ops = &efs_symlink_aops;
 			break;
 		case S_IFCHR:
diff --git a/fs/efs/symlink.c b/fs/efs/symlink.c
index 75117d0..4870cc8 100644
--- a/fs/efs/symlink.c
+++ b/fs/efs/symlink.c
@@ -13,7 +13,7 @@ 
 
 static int efs_symlink_readpage(struct file *file, struct page *page)
 {
-	char *link = kmap(page);
+	char *link = page_address(page);
 	struct buffer_head * bh;
 	struct inode * inode = page->mapping->host;
 	efs_block_t size = inode->i_size;
@@ -39,12 +39,10 @@  static int efs_symlink_readpage(struct file *file, struct page *page)
 	}
 	link[size] = '\0';
 	SetPageUptodate(page);
-	kunmap(page);
 	unlock_page(page);
 	return 0;
 fail:
 	SetPageError(page);
-	kunmap(page);
 	unlock_page(page);
 	return err;
 }
diff --git a/fs/exofs/inode.c b/fs/exofs/inode.c
index 73c64da..d8e9c181 100644
--- a/fs/exofs/inode.c
+++ b/fs/exofs/inode.c
@@ -1227,6 +1227,7 @@  struct inode *exofs_iget(struct super_block *sb, unsigned long ino)
 			inode->i_link = (char *)oi->i_data;
 		} else {
 			inode->i_op = &page_symlink_inode_operations;
+			inode_nohighmem(inode);
 			inode->i_mapping->a_ops = &exofs_aops;
 		}
 	} else {
diff --git a/fs/exofs/namei.c b/fs/exofs/namei.c
index 994e078..c20d77d 100644
--- a/fs/exofs/namei.c
+++ b/fs/exofs/namei.c
@@ -111,6 +111,7 @@  static int exofs_symlink(struct inode *dir, struct dentry *dentry,
 	if (l > sizeof(oi->i_data)) {
 		/* slow symlink */
 		inode->i_op = &page_symlink_inode_operations;
+		inode_nohighmem(inode);
 		inode->i_mapping->a_ops = &exofs_aops;
 		memset(oi->i_data, 0, sizeof(oi->i_data));
 
diff --git a/fs/ext2/inode.c b/fs/ext2/inode.c
index 0aa9bf6..338eefd 100644
--- a/fs/ext2/inode.c
+++ b/fs/ext2/inode.c
@@ -1420,6 +1420,7 @@  struct inode *ext2_iget (struct super_block *sb, unsigned long ino)
 				sizeof(ei->i_data) - 1);
 		} else {
 			inode->i_op = &ext2_symlink_inode_operations;
+			inode_nohighmem(inode);
 			if (test_opt(inode->i_sb, NOBH))
 				inode->i_mapping->a_ops = &ext2_nobh_aops;
 			else
diff --git a/fs/ext2/namei.c b/fs/ext2/namei.c
index 3267a80d..7a2be8f 100644
--- a/fs/ext2/namei.c
+++ b/fs/ext2/namei.c
@@ -183,6 +183,7 @@  static int ext2_symlink (struct inode * dir, struct dentry * dentry,
 	if (l > sizeof (EXT2_I(inode)->i_data)) {
 		/* slow symlink */
 		inode->i_op = &ext2_symlink_inode_operations;
+		inode_nohighmem(inode);
 		if (test_opt(inode->i_sb, NOBH))
 			inode->i_mapping->a_ops = &ext2_nobh_aops;
 		else
diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c
index ea433a7..b3bd912 100644
--- a/fs/ext4/inode.c
+++ b/fs/ext4/inode.c
@@ -4283,6 +4283,7 @@  struct inode *ext4_iget(struct super_block *sb, unsigned long ino)
 			inode->i_op = &ext4_symlink_inode_operations;
 			ext4_set_aops(inode);
 		}
+		inode_nohighmem(inode);
 	} else if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode) ||
 	      S_ISFIFO(inode->i_mode) || S_ISSOCK(inode->i_mode)) {
 		inode->i_op = &ext4_special_inode_operations;
diff --git a/fs/ext4/namei.c b/fs/ext4/namei.c
index a969ab3..f27e0c2 100644
--- a/fs/ext4/namei.c
+++ b/fs/ext4/namei.c
@@ -3132,6 +3132,7 @@  static int ext4_symlink(struct inode *dir,
 	if ((disk_link.len > EXT4_N_BLOCKS * 4)) {
 		if (!encryption_required)
 			inode->i_op = &ext4_symlink_inode_operations;
+		inode_nohighmem(inode);
 		ext4_set_aops(inode);
 		/*
 		 * We cannot call page_symlink() with transaction started
diff --git a/fs/ext4/symlink.c b/fs/ext4/symlink.c
index abe2401..0e6dc44 100644
--- a/fs/ext4/symlink.c
+++ b/fs/ext4/symlink.c
@@ -45,7 +45,7 @@  static const char *ext4_encrypted_follow_link(struct dentry *dentry, void **cook
 		cpage = read_mapping_page(inode->i_mapping, 0, NULL);
 		if (IS_ERR(cpage))
 			return ERR_CAST(cpage);
-		caddr = kmap(cpage);
+		caddr = page_address(cpage);
 		caddr[size] = 0;
 	}
 
@@ -75,16 +75,12 @@  static const char *ext4_encrypted_follow_link(struct dentry *dentry, void **cook
 	/* Null-terminate the name */
 	if (res <= plen)
 		paddr[res] = '\0';
-	if (cpage) {
-		kunmap(cpage);
+	if (cpage)
 		page_cache_release(cpage);
-	}
 	return *cookie = paddr;
 errout:
-	if (cpage) {
-		kunmap(cpage);
+	if (cpage)
 		page_cache_release(cpage);
-	}
 	kfree(paddr);
 	return ERR_PTR(res);
 }
diff --git a/fs/f2fs/inode.c b/fs/f2fs/inode.c
index 97e20de..5528801 100644
--- a/fs/f2fs/inode.c
+++ b/fs/f2fs/inode.c
@@ -202,6 +202,7 @@  make_now:
 			inode->i_op = &f2fs_encrypted_symlink_inode_operations;
 		else
 			inode->i_op = &f2fs_symlink_inode_operations;
+		inode_nohighmem(inode);
 		inode->i_mapping->a_ops = &f2fs_dblock_aops;
 	} else if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode) ||
 			S_ISFIFO(inode->i_mode) || S_ISSOCK(inode->i_mode)) {
diff --git a/fs/f2fs/namei.c b/fs/f2fs/namei.c
index 2c32110..484df68 100644
--- a/fs/f2fs/namei.c
+++ b/fs/f2fs/namei.c
@@ -351,6 +351,7 @@  static int f2fs_symlink(struct inode *dir, struct dentry *dentry,
 		inode->i_op = &f2fs_encrypted_symlink_inode_operations;
 	else
 		inode->i_op = &f2fs_symlink_inode_operations;
+	inode_nohighmem(inode);
 	inode->i_mapping->a_ops = &f2fs_dblock_aops;
 
 	f2fs_lock_op(sbi);
@@ -942,7 +943,7 @@  static const char *f2fs_encrypted_follow_link(struct dentry *dentry, void **cook
 	cpage = read_mapping_page(inode->i_mapping, 0, NULL);
 	if (IS_ERR(cpage))
 		return ERR_CAST(cpage);
-	caddr = kmap(cpage);
+	caddr = page_address(cpage);
 	caddr[size] = 0;
 
 	/* Symlink is encrypted */
@@ -982,13 +983,11 @@  static const char *f2fs_encrypted_follow_link(struct dentry *dentry, void **cook
 	/* Null-terminate the name */
 	paddr[res] = '\0';
 
-	kunmap(cpage);
 	page_cache_release(cpage);
 	return *cookie = paddr;
 errout:
 	kfree(cstr.name);
 	f2fs_fname_crypto_free_buffer(&pstr);
-	kunmap(cpage);
 	page_cache_release(cpage);
 	return ERR_PTR(res);
 }
diff --git a/fs/freevxfs/vxfs_inode.c b/fs/freevxfs/vxfs_inode.c
index ef73ed6..3e2ccad 100644
--- a/fs/freevxfs/vxfs_inode.c
+++ b/fs/freevxfs/vxfs_inode.c
@@ -326,6 +326,7 @@  vxfs_iget(struct super_block *sbp, ino_t ino)
 	} else if (S_ISLNK(ip->i_mode)) {
 		if (!VXFS_ISIMMED(vip)) {
 			ip->i_op = &page_symlink_inode_operations;
+			inode_nohighmem(ip);
 			ip->i_mapping->a_ops = &vxfs_aops;
 		} else {
 			ip->i_op = &simple_symlink_inode_operations;
diff --git a/fs/hfsplus/inode.c b/fs/hfsplus/inode.c
index 6dd107d..19b33f8 100644
--- a/fs/hfsplus/inode.c
+++ b/fs/hfsplus/inode.c
@@ -403,6 +403,7 @@  struct inode *hfsplus_new_inode(struct super_block *sb, umode_t mode)
 	} else if (S_ISLNK(inode->i_mode)) {
 		sbi->file_count++;
 		inode->i_op = &page_symlink_inode_operations;
+		inode_nohighmem(inode);
 		inode->i_mapping->a_ops = &hfsplus_aops;
 		hip->clump_blocks = 1;
 	} else
@@ -526,6 +527,7 @@  int hfsplus_cat_read_inode(struct inode *inode, struct hfs_find_data *fd)
 			inode->i_mapping->a_ops = &hfsplus_aops;
 		} else if (S_ISLNK(inode->i_mode)) {
 			inode->i_op = &page_symlink_inode_operations;
+			inode_nohighmem(inode);
 			inode->i_mapping->a_ops = &hfsplus_aops;
 		} else {
 			init_special_inode(inode, inode->i_mode,
diff --git a/fs/hpfs/inode.c b/fs/hpfs/inode.c
index 933c737..1f3c6d7 100644
--- a/fs/hpfs/inode.c
+++ b/fs/hpfs/inode.c
@@ -77,6 +77,7 @@  void hpfs_read_inode(struct inode *i)
 			kfree(ea);
 			i->i_mode = S_IFLNK | 0777;
 			i->i_op = &page_symlink_inode_operations;
+			inode_nohighmem(i);
 			i->i_data.a_ops = &hpfs_symlink_aops;
 			set_nlink(i, 1);
 			i->i_size = ea_size;
diff --git a/fs/hpfs/namei.c b/fs/hpfs/namei.c
index ae4d5a1..506765a 100644
--- a/fs/hpfs/namei.c
+++ b/fs/hpfs/namei.c
@@ -332,6 +332,7 @@  static int hpfs_symlink(struct inode *dir, struct dentry *dentry, const char *sy
 	result->i_blocks = 1;
 	set_nlink(result, 1);
 	result->i_size = strlen(symlink);
+	inode_nohighmem(result);
 	result->i_op = &page_symlink_inode_operations;
 	result->i_data.a_ops = &hpfs_symlink_aops;
 
@@ -500,7 +501,7 @@  out:
 
 static int hpfs_symlink_readpage(struct file *file, struct page *page)
 {
-	char *link = kmap(page);
+	char *link = page_address(page);
 	struct inode *i = page->mapping->host;
 	struct fnode *fnode;
 	struct buffer_head *bh;
@@ -516,14 +517,12 @@  static int hpfs_symlink_readpage(struct file *file, struct page *page)
 		goto fail;
 	hpfs_unlock(i->i_sb);
 	SetPageUptodate(page);
-	kunmap(page);
 	unlock_page(page);
 	return 0;
 
 fail:
 	hpfs_unlock(i->i_sb);
 	SetPageError(page);
-	kunmap(page);
 	unlock_page(page);
 	return err;
 }
diff --git a/fs/hugetlbfs/inode.c b/fs/hugetlbfs/inode.c
index de4bdfa..d8f51ee 100644
--- a/fs/hugetlbfs/inode.c
+++ b/fs/hugetlbfs/inode.c
@@ -760,6 +760,7 @@  static struct inode *hugetlbfs_get_inode(struct super_block *sb,
 			break;
 		case S_IFLNK:
 			inode->i_op = &page_symlink_inode_operations;
+			inode_nohighmem(inode);
 			break;
 		}
 		lockdep_annotate_inode_mutex_key(inode);
diff --git a/fs/inode.c b/fs/inode.c
index 1be5f90..5bb85a0 100644
--- a/fs/inode.c
+++ b/fs/inode.c
@@ -2028,3 +2028,9 @@  void inode_set_flags(struct inode *inode, unsigned int flags,
 				  new_flags) != old_flags));
 }
 EXPORT_SYMBOL(inode_set_flags);
+
+void inode_nohighmem(struct inode *inode)
+{
+	mapping_set_gfp_mask(inode->i_mapping, GFP_USER);
+}
+EXPORT_SYMBOL(inode_nohighmem);
diff --git a/fs/isofs/inode.c b/fs/isofs/inode.c
index d67a16f..61abdc4 100644
--- a/fs/isofs/inode.c
+++ b/fs/isofs/inode.c
@@ -1417,6 +1417,7 @@  static int isofs_read_inode(struct inode *inode, int relocated)
 		inode->i_fop = &isofs_dir_operations;
 	} else if (S_ISLNK(inode->i_mode)) {
 		inode->i_op = &page_symlink_inode_operations;
+		inode_nohighmem(inode);
 		inode->i_data.a_ops = &isofs_symlink_aops;
 	} else
 		/* XXX - parse_rock_ridge_inode() had already set i_rdev. */
diff --git a/fs/isofs/rock.c b/fs/isofs/rock.c
index 735d752..5384ceb 100644
--- a/fs/isofs/rock.c
+++ b/fs/isofs/rock.c
@@ -687,7 +687,7 @@  static int rock_ridge_symlink_readpage(struct file *file, struct page *page)
 	struct inode *inode = page->mapping->host;
 	struct iso_inode_info *ei = ISOFS_I(inode);
 	struct isofs_sb_info *sbi = ISOFS_SB(inode->i_sb);
-	char *link = kmap(page);
+	char *link = page_address(page);
 	unsigned long bufsize = ISOFS_BUFFER_SIZE(inode);
 	struct buffer_head *bh;
 	char *rpnt = link;
@@ -774,7 +774,6 @@  repeat:
 	brelse(bh);
 	*rpnt = '\0';
 	SetPageUptodate(page);
-	kunmap(page);
 	unlock_page(page);
 	return 0;
 
@@ -791,7 +790,6 @@  fail:
 	brelse(bh);
 error:
 	SetPageError(page);
-	kunmap(page);
 	unlock_page(page);
 	return -EIO;
 }
diff --git a/fs/jfs/inode.c b/fs/jfs/inode.c
index 41aa3ca..9d9bae6 100644
--- a/fs/jfs/inode.c
+++ b/fs/jfs/inode.c
@@ -60,6 +60,7 @@  struct inode *jfs_iget(struct super_block *sb, unsigned long ino)
 	} else if (S_ISLNK(inode->i_mode)) {
 		if (inode->i_size >= IDATASIZE) {
 			inode->i_op = &page_symlink_inode_operations;
+			inode_nohighmem(inode);
 			inode->i_mapping->a_ops = &jfs_aops;
 		} else {
 			inode->i_op = &jfs_fast_symlink_inode_operations;
diff --git a/fs/jfs/namei.c b/fs/jfs/namei.c
index 9d7551f..701f893 100644
--- a/fs/jfs/namei.c
+++ b/fs/jfs/namei.c
@@ -983,6 +983,7 @@  static int jfs_symlink(struct inode *dip, struct dentry *dentry,
 		jfs_info("jfs_symlink: allocate extent ip:0x%p", ip);
 
 		ip->i_op = &jfs_symlink_inode_operations;
+		inode_nohighmem(ip);
 		ip->i_mapping->a_ops = &jfs_aops;
 
 		/*
diff --git a/fs/logfs/dir.c b/fs/logfs/dir.c
index 99944a4..542468e 100644
--- a/fs/logfs/dir.c
+++ b/fs/logfs/dir.c
@@ -529,6 +529,7 @@  static int logfs_symlink(struct inode *dir, struct dentry *dentry,
 		return PTR_ERR(inode);
 
 	inode->i_op = &page_symlink_inode_operations;
+	inode_nohighmem(inode);
 	inode->i_mapping->a_ops = &logfs_reg_aops;
 
 	return __logfs_create(dir, dentry, inode, target, destlen);
diff --git a/fs/logfs/inode.c b/fs/logfs/inode.c
index 06baa92..0fce46d 100644
--- a/fs/logfs/inode.c
+++ b/fs/logfs/inode.c
@@ -65,6 +65,7 @@  static void logfs_inode_setops(struct inode *inode)
 		break;
 	case S_IFLNK:
 		inode->i_op = &page_symlink_inode_operations;
+		inode_nohighmem(inode);
 		inode->i_mapping->a_ops = &logfs_reg_aops;
 		break;
 	case S_IFSOCK:	/* fall through */
diff --git a/fs/minix/inode.c b/fs/minix/inode.c
index 086cd0a..67a23bf 100644
--- a/fs/minix/inode.c
+++ b/fs/minix/inode.c
@@ -452,6 +452,7 @@  void minix_set_inode(struct inode *inode, dev_t rdev)
 		inode->i_mapping->a_ops = &minix_aops;
 	} else if (S_ISLNK(inode->i_mode)) {
 		inode->i_op = &minix_symlink_inode_operations;
+		inode_nohighmem(inode);
 		inode->i_mapping->a_ops = &minix_aops;
 	} else
 		init_special_inode(inode, inode->i_mode, rdev);
diff --git a/fs/namei.c b/fs/namei.c
index 4bae5cb..2808958 100644
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -4527,7 +4527,8 @@  static const char *page_getlink(struct dentry * dentry, void **cookie)
 	if (IS_ERR(page))
 		return (char*)page;
 	*cookie = page;
-	kaddr = kmap(page);
+	BUG_ON(mapping_gfp_mask(mapping) & __GFP_HIGHMEM);
+	kaddr = page_address(page);
 	nd_terminate_link(kaddr, dentry->d_inode->i_size, PAGE_SIZE - 1);
 	return kaddr;
 }
@@ -4541,7 +4542,6 @@  EXPORT_SYMBOL(page_follow_link_light);
 void page_put_link(struct inode *unused, void *cookie)
 {
 	struct page *page = cookie;
-	kunmap(page);
 	page_cache_release(page);
 }
 EXPORT_SYMBOL(page_put_link);
@@ -4565,7 +4565,6 @@  int __page_symlink(struct inode *inode, const char *symname, int len, int nofs)
 	struct page *page;
 	void *fsdata;
 	int err;
-	char *kaddr;
 	unsigned int flags = AOP_FLAG_UNINTERRUPTIBLE;
 	if (nofs)
 		flags |= AOP_FLAG_NOFS;
@@ -4576,9 +4575,7 @@  retry:
 	if (err)
 		goto fail;
 
-	kaddr = kmap_atomic(page);
-	memcpy(kaddr, symname, len-1);
-	kunmap_atomic(kaddr);
+	memcpy(page_address(page), symname, len-1);
 
 	err = pagecache_write_end(NULL, mapping, 0, len-1, len-1,
 							page, fsdata);
diff --git a/fs/ncpfs/inode.c b/fs/ncpfs/inode.c
index 9605a2f..bb856f7 100644
--- a/fs/ncpfs/inode.c
+++ b/fs/ncpfs/inode.c
@@ -283,6 +283,7 @@  ncp_iget(struct super_block *sb, struct ncp_entry_info *info)
 #if defined(CONFIG_NCPFS_EXTRAS) || defined(CONFIG_NCPFS_NFS_NS)
 		} else if (S_ISLNK(inode->i_mode)) {
 			inode->i_op = &ncp_symlink_inode_operations;
+			inode_nohighmem(inode);
 			inode->i_data.a_ops = &ncp_symlink_aops;
 #endif
 		} else {
diff --git a/fs/nfs/inode.c b/fs/nfs/inode.c
index 31b0a52..ae9aa0b 100644
--- a/fs/nfs/inode.c
+++ b/fs/nfs/inode.c
@@ -408,9 +408,10 @@  nfs_fhget(struct super_block *sb, struct nfs_fh *fh, struct nfs_fattr *fattr, st
 				inode->i_fop = NULL;
 				inode->i_flags |= S_AUTOMOUNT;
 			}
-		} else if (S_ISLNK(inode->i_mode))
+		} else if (S_ISLNK(inode->i_mode)) {
 			inode->i_op = &nfs_symlink_inode_operations;
-		else
+			inode_nohighmem(inode);
+		} else
 			init_special_inode(inode, inode->i_mode, fattr->rdev);
 
 		memset(&inode->i_atime, 0, sizeof(inode->i_atime));
diff --git a/fs/nfs/symlink.c b/fs/nfs/symlink.c
index b6de433..abd93bf 100644
--- a/fs/nfs/symlink.c
+++ b/fs/nfs/symlink.c
@@ -56,7 +56,7 @@  static const char *nfs_follow_link(struct dentry *dentry, void **cookie)
 	if (IS_ERR(page))
 		return ERR_CAST(page);
 	*cookie = page;
-	return kmap(page);
+	return page_address(page);
 }
 
 /*
diff --git a/fs/nilfs2/inode.c b/fs/nilfs2/inode.c
index ac2f649..10b2252 100644
--- a/fs/nilfs2/inode.c
+++ b/fs/nilfs2/inode.c
@@ -510,6 +510,7 @@  static int __nilfs_read_inode(struct super_block *sb,
 		inode->i_mapping->a_ops = &nilfs_aops;
 	} else if (S_ISLNK(inode->i_mode)) {
 		inode->i_op = &nilfs_symlink_inode_operations;
+		inode_nohighmem(inode);
 		inode->i_mapping->a_ops = &nilfs_aops;
 	} else {
 		inode->i_op = &nilfs_special_inode_operations;
diff --git a/fs/nilfs2/namei.c b/fs/nilfs2/namei.c
index c9a1a49..90b3ba9 100644
--- a/fs/nilfs2/namei.c
+++ b/fs/nilfs2/namei.c
@@ -161,6 +161,7 @@  static int nilfs_symlink(struct inode *dir, struct dentry *dentry,
 
 	/* slow symlink */
 	inode->i_op = &nilfs_symlink_inode_operations;
+	inode_nohighmem(inode);
 	inode->i_mapping->a_ops = &nilfs_aops;
 	err = page_symlink(inode, symname, l);
 	if (err)
diff --git a/fs/ocfs2/inode.c b/fs/ocfs2/inode.c
index 8f87e05..97a563b 100644
--- a/fs/ocfs2/inode.c
+++ b/fs/ocfs2/inode.c
@@ -361,6 +361,7 @@  void ocfs2_populate_inode(struct inode *inode, struct ocfs2_dinode *fe,
 		    break;
 	    case S_IFLNK:
 		    inode->i_op = &ocfs2_symlink_inode_operations;
+		    inode_nohighmem(inode);
 		    i_size_write(inode, le64_to_cpu(fe->i_size));
 		    break;
 	    default:
diff --git a/fs/ocfs2/namei.c b/fs/ocfs2/namei.c
index a03f6f4..2efe8af 100644
--- a/fs/ocfs2/namei.c
+++ b/fs/ocfs2/namei.c
@@ -1960,6 +1960,7 @@  static int ocfs2_symlink(struct inode *dir,
 	inode->i_rdev = 0;
 	newsize = l - 1;
 	inode->i_op = &ocfs2_symlink_inode_operations;
+	inode_nohighmem(inode);
 	if (l > ocfs2_fast_symlink_chars(sb)) {
 		u32 offset = 0;
 
diff --git a/fs/qnx4/inode.c b/fs/qnx4/inode.c
index c4bcb77..f37b3de 100644
--- a/fs/qnx4/inode.c
+++ b/fs/qnx4/inode.c
@@ -316,6 +316,7 @@  struct inode *qnx4_iget(struct super_block *sb, unsigned long ino)
 		inode->i_fop = &qnx4_dir_operations;
 	} else if (S_ISLNK(inode->i_mode)) {
 		inode->i_op = &page_symlink_inode_operations;
+		inode_nohighmem(inode);
 		inode->i_mapping->a_ops = &qnx4_aops;
 		qnx4_i(inode)->mmu_private = inode->i_size;
 	} else {
diff --git a/fs/qnx6/inode.c b/fs/qnx6/inode.c
index 32d2e1a..9728b54 100644
--- a/fs/qnx6/inode.c
+++ b/fs/qnx6/inode.c
@@ -582,6 +582,7 @@  struct inode *qnx6_iget(struct super_block *sb, unsigned ino)
 		inode->i_mapping->a_ops = &qnx6_aops;
 	} else if (S_ISLNK(inode->i_mode)) {
 		inode->i_op = &page_symlink_inode_operations;
+		inode_nohighmem(inode);
 		inode->i_mapping->a_ops = &qnx6_aops;
 	} else
 		init_special_inode(inode, inode->i_mode, 0);
diff --git a/fs/ramfs/inode.c b/fs/ramfs/inode.c
index 889d558..38981b0 100644
--- a/fs/ramfs/inode.c
+++ b/fs/ramfs/inode.c
@@ -79,6 +79,7 @@  struct inode *ramfs_get_inode(struct super_block *sb,
 			break;
 		case S_IFLNK:
 			inode->i_op = &page_symlink_inode_operations;
+			inode_nohighmem(inode);
 			break;
 		}
 	}
diff --git a/fs/reiserfs/inode.c b/fs/reiserfs/inode.c
index 3d8e7e6..ae9e5b3 100644
--- a/fs/reiserfs/inode.c
+++ b/fs/reiserfs/inode.c
@@ -1361,6 +1361,7 @@  static void init_inode(struct inode *inode, struct treepath *path)
 		inode->i_fop = &reiserfs_dir_operations;
 	} else if (S_ISLNK(inode->i_mode)) {
 		inode->i_op = &reiserfs_symlink_inode_operations;
+		inode_nohighmem(inode);
 		inode->i_mapping->a_ops = &reiserfs_address_space_operations;
 	} else {
 		inode->i_blocks = 0;
diff --git a/fs/reiserfs/namei.c b/fs/reiserfs/namei.c
index 47f9698..4fc2326 100644
--- a/fs/reiserfs/namei.c
+++ b/fs/reiserfs/namei.c
@@ -1170,6 +1170,7 @@  static int reiserfs_symlink(struct inode *parent_dir,
 	reiserfs_update_inode_transaction(parent_dir);
 
 	inode->i_op = &reiserfs_symlink_inode_operations;
+	inode_nohighmem(inode);
 	inode->i_mapping->a_ops = &reiserfs_address_space_operations;
 
 	retval = reiserfs_add_entry(&th, parent_dir, dentry->d_name.name,
diff --git a/fs/romfs/super.c b/fs/romfs/super.c
index 268733c..bb894e7 100644
--- a/fs/romfs/super.c
+++ b/fs/romfs/super.c
@@ -360,6 +360,7 @@  static struct inode *romfs_iget(struct super_block *sb, unsigned long pos)
 		break;
 	case ROMFH_SYM:
 		i->i_op = &page_symlink_inode_operations;
+		inode_nohighmem(i);
 		i->i_data.a_ops = &romfs_aops;
 		mode |= S_IRWXUGO;
 		break;
diff --git a/fs/squashfs/inode.c b/fs/squashfs/inode.c
index a1ce5ce..0927b1e 100644
--- a/fs/squashfs/inode.c
+++ b/fs/squashfs/inode.c
@@ -41,6 +41,7 @@ 
 #include <linux/fs.h>
 #include <linux/vfs.h>
 #include <linux/xattr.h>
+#include <linux/pagemap.h>
 
 #include "squashfs_fs.h"
 #include "squashfs_fs_sb.h"
@@ -291,6 +292,7 @@  int squashfs_read_inode(struct inode *inode, long long ino)
 		set_nlink(inode, le32_to_cpu(sqsh_ino->nlink));
 		inode->i_size = le32_to_cpu(sqsh_ino->symlink_size);
 		inode->i_op = &squashfs_symlink_inode_ops;
+		inode_nohighmem(inode);
 		inode->i_data.a_ops = &squashfs_symlink_aops;
 		inode->i_mode |= S_IFLNK;
 		squashfs_i(inode)->start = block;
diff --git a/fs/sysv/inode.c b/fs/sysv/inode.c
index 02fa1dc..ef8bcdb 100644
--- a/fs/sysv/inode.c
+++ b/fs/sysv/inode.c
@@ -163,6 +163,7 @@  void sysv_set_inode(struct inode *inode, dev_t rdev)
 		inode->i_mapping->a_ops = &sysv_aops;
 	} else if (S_ISLNK(inode->i_mode)) {
 		inode->i_op = &sysv_symlink_inode_operations;
+		inode_nohighmem(inode);
 		inode->i_mapping->a_ops = &sysv_aops;
 	} else
 		init_special_inode(inode, inode->i_mode, rdev);
diff --git a/fs/udf/inode.c b/fs/udf/inode.c
index 8675c2b..0557463 100644
--- a/fs/udf/inode.c
+++ b/fs/udf/inode.c
@@ -1541,6 +1541,7 @@  reread:
 	case ICBTAG_FILE_TYPE_SYMLINK:
 		inode->i_data.a_ops = &udf_symlink_aops;
 		inode->i_op = &page_symlink_inode_operations;
+		inode_nohighmem(inode);
 		inode->i_mode = S_IFLNK | S_IRWXUGO;
 		break;
 	case ICBTAG_FILE_TYPE_MAIN:
diff --git a/fs/udf/namei.c b/fs/udf/namei.c
index d0e6de1..42eafb9 100644
--- a/fs/udf/namei.c
+++ b/fs/udf/namei.c
@@ -922,6 +922,7 @@  static int udf_symlink(struct inode *dir, struct dentry *dentry,
 
 	inode->i_data.a_ops = &udf_symlink_aops;
 	inode->i_op = &page_symlink_inode_operations;
+	inode_nohighmem(inode);
 
 	if (iinfo->i_alloc_type != ICBTAG_FLAG_AD_IN_ICB) {
 		struct kernel_lb_addr eloc;
diff --git a/fs/udf/symlink.c b/fs/udf/symlink.c
index 862535b..8d61977 100644
--- a/fs/udf/symlink.c
+++ b/fs/udf/symlink.c
@@ -107,7 +107,7 @@  static int udf_symlink_filler(struct file *file, struct page *page)
 	struct buffer_head *bh = NULL;
 	unsigned char *symlink;
 	int err;
-	unsigned char *p = kmap(page);
+	unsigned char *p = page_address(page);
 	struct udf_inode_info *iinfo;
 	uint32_t pos;
 
@@ -141,7 +141,6 @@  static int udf_symlink_filler(struct file *file, struct page *page)
 
 	up_read(&iinfo->i_data_sem);
 	SetPageUptodate(page);
-	kunmap(page);
 	unlock_page(page);
 	return 0;
 
@@ -149,7 +148,6 @@  out_unlock_inode:
 	up_read(&iinfo->i_data_sem);
 	SetPageError(page);
 out_unmap:
-	kunmap(page);
 	unlock_page(page);
 	return err;
 }
diff --git a/fs/ufs/inode.c b/fs/ufs/inode.c
index 737160a..d897e16 100644
--- a/fs/ufs/inode.c
+++ b/fs/ufs/inode.c
@@ -533,6 +533,7 @@  static void ufs_set_inode_ops(struct inode *inode)
 		} else {
 			inode->i_mapping->a_ops = &ufs_aops;
 			inode->i_op = &page_symlink_inode_operations;
+			inode_nohighmem(inode);
 		}
 	} else
 		init_special_inode(inode, inode->i_mode,
diff --git a/fs/ufs/namei.c b/fs/ufs/namei.c
index 24b0cbd..acf4a3b 100644
--- a/fs/ufs/namei.c
+++ b/fs/ufs/namei.c
@@ -124,6 +124,7 @@  static int ufs_symlink (struct inode * dir, struct dentry * dentry,
 	if (l > UFS_SB(sb)->s_uspi->s_maxsymlinklen) {
 		/* slow symlink */
 		inode->i_op = &page_symlink_inode_operations;
+		inode_nohighmem(inode);
 		inode->i_mapping->a_ops = &ufs_aops;
 		err = page_symlink(inode, symname, l);
 		if (err)
diff --git a/include/linux/fs.h b/include/linux/fs.h
index 3aa5142..dfeda44 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -3025,5 +3025,6 @@  static inline bool dir_relax(struct inode *inode)
 }
 
 extern bool path_noexec(const struct path *path);
+extern void inode_nohighmem(struct inode *inode);
 
 #endif /* _LINUX_FS_H */
diff --git a/mm/shmem.c b/mm/shmem.c
index 9187eee..64bf5ac 100644
--- a/mm/shmem.c
+++ b/mm/shmem.c
@@ -2444,7 +2444,6 @@  static int shmem_symlink(struct inode *dir, struct dentry *dentry, const char *s
 	int len;
 	struct inode *inode;
 	struct page *page;
-	char *kaddr;
 	struct shmem_inode_info *info;
 
 	len = strlen(symname) + 1;
@@ -2483,9 +2482,8 @@  static int shmem_symlink(struct inode *dir, struct dentry *dentry, const char *s
 		}
 		inode->i_mapping->a_ops = &shmem_aops;
 		inode->i_op = &shmem_symlink_inode_operations;
-		kaddr = kmap_atomic(page);
-		memcpy(kaddr, symname, len);
-		kunmap_atomic(kaddr);
+		inode_nohighmem(inode);
+		memcpy(page_address(page), symname, len);
 		SetPageUptodate(page);
 		set_page_dirty(page);
 		unlock_page(page);
@@ -2506,13 +2504,12 @@  static const char *shmem_follow_link(struct dentry *dentry, void **cookie)
 		return ERR_PTR(error);
 	unlock_page(page);
 	*cookie = page;
-	return kmap(page);
+	return page_address(page);
 }
 
 static void shmem_put_link(struct inode *unused, void *cookie)
 {
 	struct page *page = cookie;
-	kunmap(page);
 	mark_page_accessed(page);
 	page_cache_release(page);
 }