Merge drm/drm-fixes into drm-misc-fixes
[platform/kernel/linux-starfive.git] / fs / xattr.c
index e7bbb7f..efd4736 100644 (file)
@@ -1040,12 +1040,32 @@ const char *xattr_full_name(const struct xattr_handler *handler,
 EXPORT_SYMBOL(xattr_full_name);
 
 /**
- * free_simple_xattr - free an xattr object
+ * simple_xattr_space - estimate the memory used by a simple xattr
+ * @name: the full name of the xattr
+ * @size: the size of its value
+ *
+ * This takes no account of how much larger the two slab objects actually are:
+ * that would depend on the slab implementation, when what is required is a
+ * deterministic number, which grows with name length and size and quantity.
+ *
+ * Return: The approximate number of bytes of memory used by such an xattr.
+ */
+size_t simple_xattr_space(const char *name, size_t size)
+{
+       /*
+        * Use "40" instead of sizeof(struct simple_xattr), to return the
+        * same result on 32-bit and 64-bit, and even if simple_xattr grows.
+        */
+       return 40 + size + strlen(name);
+}
+
+/**
+ * simple_xattr_free - free an xattr object
  * @xattr: the xattr object
  *
  * Free the xattr object. Can handle @xattr being NULL.
  */
-static inline void free_simple_xattr(struct simple_xattr *xattr)
+void simple_xattr_free(struct simple_xattr *xattr)
 {
        if (xattr)
                kfree(xattr->name);
@@ -1073,7 +1093,7 @@ struct simple_xattr *simple_xattr_alloc(const void *value, size_t size)
        if (len < sizeof(*new_xattr))
                return NULL;
 
-       new_xattr = kvmalloc(len, GFP_KERNEL);
+       new_xattr = kvmalloc(len, GFP_KERNEL_ACCOUNT);
        if (!new_xattr)
                return NULL;
 
@@ -1164,7 +1184,6 @@ int simple_xattr_get(struct simple_xattrs *xattrs, const char *name,
  * @value: the value to store along the xattr
  * @size: the size of @value
  * @flags: the flags determining how to set the xattr
- * @removed_size: the size of the removed xattr
  *
  * Set a new xattr object.
  * If @value is passed a new xattr object will be allocated. If XATTR_REPLACE
@@ -1181,29 +1200,27 @@ int simple_xattr_get(struct simple_xattrs *xattrs, const char *name,
  * nothing if XATTR_CREATE is specified in @flags or @flags is zero. For
  * XATTR_REPLACE we fail as mentioned above.
  *
- * Return: On success zero and on error a negative error code is returned.
+ * Return: On success, the removed or replaced xattr is returned, to be freed
+ * by the caller; or NULL if none. On failure a negative error code is returned.
  */
-int simple_xattr_set(struct simple_xattrs *xattrs, const char *name,
-                    const void *value, size_t size, int flags,
-                    ssize_t *removed_size)
+struct simple_xattr *simple_xattr_set(struct simple_xattrs *xattrs,
+                                     const char *name, const void *value,
+                                     size_t size, int flags)
 {
-       struct simple_xattr *xattr = NULL, *new_xattr = NULL;
+       struct simple_xattr *old_xattr = NULL, *new_xattr = NULL;
        struct rb_node *parent = NULL, **rbp;
        int err = 0, ret;
 
-       if (removed_size)
-               *removed_size = -1;
-
        /* value == NULL means remove */
        if (value) {
                new_xattr = simple_xattr_alloc(value, size);
                if (!new_xattr)
-                       return -ENOMEM;
+                       return ERR_PTR(-ENOMEM);
 
-               new_xattr->name = kstrdup(name, GFP_KERNEL);
+               new_xattr->name = kstrdup(name, GFP_KERNEL_ACCOUNT);
                if (!new_xattr->name) {
-                       free_simple_xattr(new_xattr);
-                       return -ENOMEM;
+                       simple_xattr_free(new_xattr);
+                       return ERR_PTR(-ENOMEM);
                }
        }
 
@@ -1217,12 +1234,12 @@ int simple_xattr_set(struct simple_xattrs *xattrs, const char *name,
                else if (ret > 0)
                        rbp = &(*rbp)->rb_right;
                else
-                       xattr = rb_entry(*rbp, struct simple_xattr, rb_node);
-               if (xattr)
+                       old_xattr = rb_entry(*rbp, struct simple_xattr, rb_node);
+               if (old_xattr)
                        break;
        }
 
-       if (xattr) {
+       if (old_xattr) {
                /* Fail if XATTR_CREATE is requested and the xattr exists. */
                if (flags & XATTR_CREATE) {
                        err = -EEXIST;
@@ -1230,12 +1247,10 @@ int simple_xattr_set(struct simple_xattrs *xattrs, const char *name,
                }
 
                if (new_xattr)
-                       rb_replace_node(&xattr->rb_node, &new_xattr->rb_node,
-                                       &xattrs->rb_root);
+                       rb_replace_node(&old_xattr->rb_node,
+                                       &new_xattr->rb_node, &xattrs->rb_root);
                else
-                       rb_erase(&xattr->rb_node, &xattrs->rb_root);
-               if (!err && removed_size)
-                       *removed_size = xattr->size;
+                       rb_erase(&old_xattr->rb_node, &xattrs->rb_root);
        } else {
                /* Fail if XATTR_REPLACE is requested but no xattr is found. */
                if (flags & XATTR_REPLACE) {
@@ -1260,12 +1275,10 @@ int simple_xattr_set(struct simple_xattrs *xattrs, const char *name,
 
 out_unlock:
        write_unlock(&xattrs->lock);
-       if (err)
-               free_simple_xattr(new_xattr);
-       else
-               free_simple_xattr(xattr);
-       return err;
-
+       if (!err)
+               return old_xattr;
+       simple_xattr_free(new_xattr);
+       return ERR_PTR(err);
 }
 
 static bool xattr_is_trusted(const char *name)
@@ -1370,14 +1383,17 @@ void simple_xattrs_init(struct simple_xattrs *xattrs)
 /**
  * simple_xattrs_free - free xattrs
  * @xattrs: xattr header whose xattrs to destroy
+ * @freed_space: approximate number of bytes of memory freed from @xattrs
  *
  * Destroy all xattrs in @xattr. When this is called no one can hold a
  * reference to any of the xattrs anymore.
  */
-void simple_xattrs_free(struct simple_xattrs *xattrs)
+void simple_xattrs_free(struct simple_xattrs *xattrs, size_t *freed_space)
 {
        struct rb_node *rbp;
 
+       if (freed_space)
+               *freed_space = 0;
        rbp = rb_first(&xattrs->rb_root);
        while (rbp) {
                struct simple_xattr *xattr;
@@ -1386,7 +1402,10 @@ void simple_xattrs_free(struct simple_xattrs *xattrs)
                rbp_next = rb_next(rbp);
                xattr = rb_entry(rbp, struct simple_xattr, rb_node);
                rb_erase(&xattr->rb_node, &xattrs->rb_root);
-               free_simple_xattr(xattr);
+               if (freed_space)
+                       *freed_space += simple_xattr_space(xattr->name,
+                                                          xattr->size);
+               simple_xattr_free(xattr);
                rbp = rbp_next;
        }
 }