*/
                if (inode->i_ino != EXT4_JOURNAL_INO &&
                    ext4_should_journal_data(inode) &&
-                   (S_ISLNK(inode->i_mode) || S_ISREG(inode->i_mode)) &&
-                   inode->i_data.nrpages) {
+                   S_ISREG(inode->i_mode) && inode->i_data.nrpages) {
                        journal_t *journal = EXT4_SB(inode->i_sb)->s_journal;
                        tid_t commit_tid = EXT4_I(inode)->i_datasync_tid;
 
 
        index = pos >> PAGE_SHIFT;
 
-       if (ext4_nonda_switch(inode->i_sb) || S_ISLNK(inode->i_mode) ||
-           ext4_verity_in_progress(inode)) {
+       if (ext4_nonda_switch(inode->i_sb) || ext4_verity_in_progress(inode)) {
                *fsdata = (void *)FALL_BACK_TO_NONDELALLOC;
                return ext4_write_begin(file, mapping, pos,
                                        len, flags, pagep, fsdata);
                }
                if (IS_ENCRYPTED(inode)) {
                        inode->i_op = &ext4_encrypted_symlink_inode_operations;
-                       ext4_set_aops(inode);
                } else if (ext4_inode_is_fast_symlink(inode)) {
                        inode->i_link = (char *)ei->i_data;
                        inode->i_op = &ext4_fast_symlink_inode_operations;
                                sizeof(ei->i_data) - 1);
                } else {
                        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;
 
        return retval;
 }
 
+static int ext4_init_symlink_block(handle_t *handle, struct inode *inode,
+                                  struct fscrypt_str *disk_link)
+{
+       struct buffer_head *bh;
+       char *kaddr;
+       int err = 0;
+
+       bh = ext4_bread(handle, inode, 0, EXT4_GET_BLOCKS_CREATE);
+       if (IS_ERR(bh))
+               return PTR_ERR(bh);
+
+       BUFFER_TRACE(bh, "get_write_access");
+       err = ext4_journal_get_write_access(handle, inode->i_sb, bh, EXT4_JTR_NONE);
+       if (err)
+               goto out;
+
+       kaddr = (char *)bh->b_data;
+       memcpy(kaddr, disk_link->name, disk_link->len);
+       inode->i_size = disk_link->len - 1;
+       EXT4_I(inode)->i_disksize = inode->i_size;
+       err = ext4_handle_dirty_metadata(handle, inode, bh);
+out:
+       brelse(bh);
+       return err;
+}
+
 static int ext4_symlink(struct user_namespace *mnt_userns, struct inode *dir,
                        struct dentry *dentry, const char *symname)
 {
        int err, len = strlen(symname);
        int credits;
        struct fscrypt_str disk_link;
+       int retries = 0;
 
        if (unlikely(ext4_forced_shutdown(EXT4_SB(dir->i_sb))))
                return -EIO;
        if (err)
                return err;
 
-       if ((disk_link.len > EXT4_N_BLOCKS * 4)) {
-               /*
-                * For non-fast symlinks, we just allocate inode and put it on
-                * orphan list in the first transaction => we need bitmap,
-                * group descriptor, sb, inode block, quota blocks, and
-                * possibly selinux xattr blocks.
-                */
-               credits = 4 + EXT4_MAXQUOTAS_INIT_BLOCKS(dir->i_sb) +
-                         EXT4_XATTR_TRANS_BLOCKS;
-       } else {
-               /*
-                * Fast symlink. We have to add entry to directory
-                * (EXT4_DATA_TRANS_BLOCKS + EXT4_INDEX_EXTRA_TRANS_BLOCKS),
-                * allocate new inode (bitmap, group descriptor, inode block,
-                * quota blocks, sb is already counted in previous macros).
-                */
-               credits = EXT4_DATA_TRANS_BLOCKS(dir->i_sb) +
-                         EXT4_INDEX_EXTRA_TRANS_BLOCKS + 3;
-       }
-
+       /*
+        * EXT4_INDEX_EXTRA_TRANS_BLOCKS for addition of entry into the
+        * directory. +3 for inode, inode bitmap, group descriptor allocation.
+        * EXT4_DATA_TRANS_BLOCKS for the data block allocation and
+        * modification.
+        */
+       credits = EXT4_DATA_TRANS_BLOCKS(dir->i_sb) +
+                 EXT4_INDEX_EXTRA_TRANS_BLOCKS + 3;
+retry:
        inode = ext4_new_inode_start_handle(mnt_userns, dir, S_IFLNK|S_IRWXUGO,
                                            &dentry->d_name, 0, NULL,
                                            EXT4_HT_DIR, credits);
        if (IS_ERR(inode)) {
                if (handle)
                        ext4_journal_stop(handle);
-               return PTR_ERR(inode);
+               err = PTR_ERR(inode);
+               goto out_retry;
        }
 
        if (IS_ENCRYPTED(inode)) {
                if (err)
                        goto err_drop_inode;
                inode->i_op = &ext4_encrypted_symlink_inode_operations;
+       } else {
+               if ((disk_link.len > EXT4_N_BLOCKS * 4)) {
+                       inode->i_op = &ext4_symlink_inode_operations;
+               } else {
+                       inode->i_op = &ext4_fast_symlink_inode_operations;
+                       inode->i_link = (char *)&EXT4_I(inode)->i_data;
+               }
        }
 
        if ((disk_link.len > EXT4_N_BLOCKS * 4)) {
-               if (!IS_ENCRYPTED(inode))
-                       inode->i_op = &ext4_symlink_inode_operations;
-               inode_nohighmem(inode);
-               ext4_set_aops(inode);
-               /*
-                * We cannot call page_symlink() with transaction started
-                * because it calls into ext4_write_begin() which can wait
-                * for transaction commit if we are running out of space
-                * and thus we deadlock. So we have to stop transaction now
-                * and restart it when symlink contents is written.
-                *
-                * To keep fs consistent in case of crash, we have to put inode
-                * to orphan list in the mean time.
-                */
-               drop_nlink(inode);
-               err = ext4_orphan_add(handle, inode);
-               if (handle)
-                       ext4_journal_stop(handle);
-               handle = NULL;
-               if (err)
-                       goto err_drop_inode;
-               err = __page_symlink(inode, disk_link.name, disk_link.len, 1);
-               if (err)
-                       goto err_drop_inode;
-               /*
-                * Now inode is being linked into dir (EXT4_DATA_TRANS_BLOCKS
-                * + EXT4_INDEX_EXTRA_TRANS_BLOCKS), inode is also modified
-                */
-               handle = ext4_journal_start(dir, EXT4_HT_DIR,
-                               EXT4_DATA_TRANS_BLOCKS(dir->i_sb) +
-                               EXT4_INDEX_EXTRA_TRANS_BLOCKS + 1);
-               if (IS_ERR(handle)) {
-                       err = PTR_ERR(handle);
-                       handle = NULL;
-                       goto err_drop_inode;
-               }
-               set_nlink(inode, 1);
-               err = ext4_orphan_del(handle, inode);
+               /* alloc symlink block and fill it */
+               err = ext4_init_symlink_block(handle, inode, &disk_link);
                if (err)
                        goto err_drop_inode;
        } else {
                /* clear the extent format for fast symlink */
                ext4_clear_inode_flag(inode, EXT4_INODE_EXTENTS);
-               if (!IS_ENCRYPTED(inode)) {
-                       inode->i_op = &ext4_fast_symlink_inode_operations;
-                       inode->i_link = (char *)&EXT4_I(inode)->i_data;
-               }
                memcpy((char *)&EXT4_I(inode)->i_data, disk_link.name,
                       disk_link.len);
                inode->i_size = disk_link.len - 1;
+               EXT4_I(inode)->i_disksize = inode->i_size;
        }
-       EXT4_I(inode)->i_disksize = inode->i_size;
        err = ext4_add_nondir(handle, dentry, &inode);
        if (handle)
                ext4_journal_stop(handle);
        iput(inode);
-       goto out_free_encrypted_link;
+       goto out_retry;
 
 err_drop_inode:
-       if (handle)
-               ext4_journal_stop(handle);
        clear_nlink(inode);
+       ext4_orphan_add(handle, inode);
        unlock_new_inode(inode);
+       if (handle)
+               ext4_journal_stop(handle);
        iput(inode);
-out_free_encrypted_link:
+out_retry:
+       if (err == -ENOSPC && ext4_should_retry_alloc(dir->i_sb, &retries))
+               goto retry;
        if (disk_link.name != (unsigned char *)symname)
                kfree(disk_link.name);
        return err;
 
                                           struct inode *inode,
                                           struct delayed_call *done)
 {
-       struct page *cpage = NULL;
+       struct buffer_head *bh = NULL;
        const void *caddr;
        unsigned int max_size;
        const char *paddr;
                caddr = EXT4_I(inode)->i_data;
                max_size = sizeof(EXT4_I(inode)->i_data);
        } else {
-               cpage = read_mapping_page(inode->i_mapping, 0, NULL);
-               if (IS_ERR(cpage))
-                       return ERR_CAST(cpage);
-               caddr = page_address(cpage);
+               bh = ext4_bread(NULL, inode, 0, 0);
+               if (IS_ERR(bh))
+                       return ERR_CAST(bh);
+               if (!bh) {
+                       EXT4_ERROR_INODE(inode, "bad symlink.");
+                       return ERR_PTR(-EFSCORRUPTED);
+               }
+               caddr = bh->b_data;
                max_size = inode->i_sb->s_blocksize;
        }
 
        paddr = fscrypt_get_symlink(inode, caddr, max_size, done);
-       if (cpage)
-               put_page(cpage);
+       brelse(bh);
        return paddr;
 }
 
        return fscrypt_symlink_getattr(path, stat);
 }
 
+static void ext4_free_link(void *bh)
+{
+       brelse(bh);
+}
+
+static const char *ext4_get_link(struct dentry *dentry, struct inode *inode,
+                                struct delayed_call *callback)
+{
+       struct buffer_head *bh;
+
+       if (!dentry) {
+               bh = ext4_getblk(NULL, inode, 0, EXT4_GET_BLOCKS_CACHED_NOWAIT);
+               if (IS_ERR(bh))
+                       return ERR_CAST(bh);
+               if (!bh || !ext4_buffer_uptodate(bh))
+                       return ERR_PTR(-ECHILD);
+       } else {
+               bh = ext4_bread(NULL, inode, 0, 0);
+               if (IS_ERR(bh))
+                       return ERR_CAST(bh);
+               if (!bh) {
+                       EXT4_ERROR_INODE(inode, "bad symlink.");
+                       return ERR_PTR(-EFSCORRUPTED);
+               }
+       }
+
+       set_delayed_call(callback, ext4_free_link, bh);
+       nd_terminate_link(bh->b_data, inode->i_size,
+                         inode->i_sb->s_blocksize - 1);
+       return bh->b_data;
+}
+
 const struct inode_operations ext4_encrypted_symlink_inode_operations = {
        .get_link       = ext4_encrypted_get_link,
        .setattr        = ext4_setattr,
 };
 
 const struct inode_operations ext4_symlink_inode_operations = {
-       .get_link       = page_get_link,
+       .get_link       = ext4_get_link,
        .setattr        = ext4_setattr,
        .getattr        = ext4_getattr,
        .listxattr      = ext4_listxattr,