int num;
        u8 type;
 
-       if (found_key->type == BTRFS_XATTR_ITEM_KEY)
-               buf_len = BTRFS_MAX_XATTR_SIZE(root);
-       else
-               buf_len = PATH_MAX;
-
+       /*
+        * Start with a small buffer (1 page). If later we end up needing more
+        * space, which can happen for xattrs on a fs with a leaf size greater
+        * then the page size, attempt to increase the buffer. Typically xattr
+        * values are small.
+        */
+       buf_len = PATH_MAX;
        buf = kmalloc(buf_len, GFP_NOFS);
        if (!buf) {
                ret = -ENOMEM;
                                ret = -ENAMETOOLONG;
                                goto out;
                        }
-                       if (name_len + data_len > buf_len) {
+                       if (name_len + data_len > BTRFS_MAX_XATTR_SIZE(root)) {
                                ret = -E2BIG;
                                goto out;
                        }
                        /*
                         * Path too long
                         */
-                       if (name_len + data_len > buf_len) {
+                       if (name_len + data_len > PATH_MAX) {
                                ret = -ENAMETOOLONG;
                                goto out;
                        }
                }
 
+               if (name_len + data_len > buf_len) {
+                       buf_len = name_len + data_len;
+                       if (is_vmalloc_addr(buf)) {
+                               vfree(buf);
+                               buf = NULL;
+                       } else {
+                               char *tmp = krealloc(buf, buf_len,
+                                                    GFP_NOFS | __GFP_NOWARN);
+
+                               if (!tmp)
+                                       kfree(buf);
+                               buf = tmp;
+                       }
+                       if (!buf) {
+                               buf = vmalloc(buf_len);
+                               if (!buf) {
+                                       ret = -ENOMEM;
+                                       goto out;
+                               }
+                       }
+               }
+
                read_extent_buffer(eb, buf, (unsigned long)(di + 1),
                                name_len + data_len);
 
        }
 
 out:
-       kfree(buf);
+       kvfree(buf);
        return ret;
 }