Whamcloud - gitweb
LU-8130 osc: convert osc_quota hash to xarray 38/32038/76
authorJames Simmons <jsimmons@infradead.org>
Mon, 28 Aug 2023 14:03:03 +0000 (10:03 -0400)
committerOleg Drokin <green@whamcloud.com>
Wed, 6 Sep 2023 06:15:49 +0000 (06:15 +0000)
The cl_quota_hash originally had 3 hashes, one for each type of quota
(USR, GRP, PRJ) that just stored on the client whether a particular
quota ID was over its limit. This was overkill since cl_quota_hash
only needs one bit to check if a particular ID has exceeded quota
with IO from this client, and there will usually be only a few IDs
that are actually exceeding their limit where a client is involved.
Instead, use the quota ID as the index into an Xarray, and store
a value with the quota TYPE(s) that are over the limit for that ID.
We only need to test the presence/absence of an ID and a quota type
without the need to store any additional values (the clients do not
track the actual quota usage or limits).
To test if a quota is exceeded for particular ID is a two-step
process. First check if there is any entry for the particular ID,
and if it exists then check which quota type (USR, GRP, PRJ) is
over the limit for that ID value.  The same is done when setting
a particular quota ID/TYPE is over its limit - first lookup the
ID and then add the TYPE flag to the value if not already set.
The Xarray implementation does offer using "marks" (up to 3 bits
per index) but in this case there is no other value that needs to
be stored into the Xarray other than one bit for any exceeded type,
so they are not used here.

Change-Id: I9355ed2a7158f0d5cc0d600ad51ea1a1434f3e98
Signed-off-by: James Simmons <jsimmons@infradead.org>
Reviewed-on: https://review.whamcloud.com/c/fs/lustre-release/+/32038
Reviewed-by: Timothy Day <timday@amazon.com>
Reviewed-by: Andreas Dilger <adilger@whamcloud.com>
Reviewed-by: Oleg Drokin <green@whamcloud.com>
Tested-by: jenkins <devops@whamcloud.com>
Tested-by: Maloo <maloo@whamcloud.com>
libcfs/include/libcfs/linux/xarray.h
lustre/include/lustre_osc.h
lustre/include/obd.h
lustre/osc/osc_dev.c
lustre/osc/osc_internal.h
lustre/osc/osc_quota.c

index b252aaf..44d672f 100644 (file)
@@ -1761,6 +1761,8 @@ static inline void *xas_next(struct xa_state *xas)
        xas->xa_offset++;
        return xa_entry(xas->xa, node, xas->xa_offset);
 }
+#else
+#include <linux/xarray.h>
 #endif /* !HAVE_XARRAY_SUPPORT */
 
 #endif /* _LINUX_XARRAY_LUSTRE_H */
index 4a4880d..235f040 100644 (file)
  *  @{
  */
 
-struct osc_quota_info {
-       /** linkage for quota hash table */
-       struct hlist_node oqi_hash;
-       __u32             oqi_id;
-};
-
 enum oap_async_flags {
        ASYNC_READY        = 0x1, /* ap_make_ready will not be called before
                                   * this page is added to an rpc */
@@ -556,7 +550,6 @@ extern struct kmem_cache *osc_object_kmem;
 extern struct kmem_cache *osc_thread_kmem;
 extern struct kmem_cache *osc_session_kmem;
 extern struct kmem_cache *osc_extent_kmem;
-extern struct kmem_cache *osc_quota_kmem;
 extern struct kmem_cache *osc_obdo_kmem;
 
 extern struct lu_context_key osc_key;
index effdfd3..ed3e375 100644 (file)
@@ -37,7 +37,7 @@
 #include <linux/kobject.h>
 #include <linux/spinlock.h>
 #include <linux/sysfs.h>
-#include <linux/xarray.h>
+#include <libcfs/linux/xarray.h>
 
 #include <uapi/linux/lustre/lustre_idl.h>
 #include <lustre_lib.h>
@@ -363,8 +363,8 @@ struct client_obd {
        void                    *cl_writeback_work;
        void                    *cl_lru_work;
        struct mutex              cl_quota_mutex;
-       /* hash tables for osc_quota_info */
-       struct cfs_hash         *cl_quota_hash[LL_MAXQUOTAS];
+       /* quota IDs/types that have exceeded quota */
+       struct xarray            cl_quota_exceeded_ids;
        /* the xid of the request updating the hash tables */
        __u64                    cl_quota_last_xid;
        /* Links to the global list of registered changelog devices */
index a2d3bca..7cd8bd1 100644 (file)
@@ -54,7 +54,6 @@ EXPORT_SYMBOL(osc_object_kmem);
 struct kmem_cache *osc_thread_kmem;
 struct kmem_cache *osc_session_kmem;
 struct kmem_cache *osc_extent_kmem;
-struct kmem_cache *osc_quota_kmem;
 struct kmem_cache *osc_obdo_kmem;
 
 struct lu_kmem_descr osc_caches[] = {
@@ -84,11 +83,6 @@ struct lu_kmem_descr osc_caches[] = {
                .ckd_size  = sizeof (struct osc_extent)
        },
        {
-               .ckd_cache = &osc_quota_kmem,
-               .ckd_name  = "osc_quota_kmem",
-               .ckd_size  = sizeof(struct osc_quota_info)
-       },
-       {
                .ckd_cache = &osc_obdo_kmem,
                .ckd_name  = "osc_obdo_kmem",
                .ckd_size  = sizeof(struct obdo)
index 52a7ab5..361f4f7 100644 (file)
@@ -151,7 +151,7 @@ struct osc_async_args {
 };
 
 int osc_quota_setup(struct obd_device *obd);
-int osc_quota_cleanup(struct obd_device *obd);
+void osc_quota_cleanup(struct obd_device *obd);
 int osc_quota_setdq(struct client_obd *cli, __u64 xid, const unsigned int qid[],
                    u64 valid, u32 flags);
 int osc_quota_chkdq(struct client_obd *cli, const unsigned int qid[]);
index 36af9d7..b127361 100644 (file)
  * Code originally extracted from quota directory
  */
 
-#include <obd.h>
+#include <obd_class.h>
 #include <lustre_osc.h>
 
 #include "osc_internal.h"
 
-static inline struct osc_quota_info *osc_oqi_alloc(u32 id)
-{
-       struct osc_quota_info *oqi;
-
-       OBD_SLAB_ALLOC_PTR(oqi, osc_quota_kmem);
-       if (oqi != NULL)
-               oqi->oqi_id = id;
-
-       return oqi;
-}
-
 int osc_quota_chkdq(struct client_obd *cli, const unsigned int qid[])
 {
        int type;
-       ENTRY;
 
+       ENTRY;
        for (type = 0; type < LL_MAXQUOTAS; type++) {
-               struct osc_quota_info *oqi;
-
-               oqi = cfs_hash_lookup(cli->cl_quota_hash[type], &qid[type]);
-               if (oqi) {
-                       /* do not try to access oqi here, it could have been
-                        * freed by osc_quota_setdq() */
+               u8 *qtype;
 
+               qtype = xa_load(&cli->cl_quota_exceeded_ids, qid[type]);
+               if (qtype && (xa_to_value(qtype) & BIT(type))) {
                        /* the slot is busy, the user is about to run out of
-                        * quota space on this OST */
+                        * quota space on this OST
+                        */
                        CDEBUG(D_QUOTA, "chkdq found noquota for %s %d\n",
-                              type == USRQUOTA ? "user" : "grout", qid[type]);
+                              qtype_name(type), qid[type]);
                        RETURN(-EDQUOT);
                }
        }
@@ -96,14 +83,13 @@ static inline u32 fl_quota_flag(int qtype)
        }
 }
 
-int osc_quota_setdq(struct client_obd *cli, __u64 xid, const unsigned int qid[],
+int osc_quota_setdq(struct client_obd *cli, u64 xid, const unsigned int qid[],
                    u64 valid, u32 flags)
 {
        int type;
        int rc = 0;
 
         ENTRY;
-
        if ((valid & (OBD_MD_FLALLQUOTA)) == 0)
                RETURN(0);
 
@@ -121,48 +107,57 @@ int osc_quota_setdq(struct client_obd *cli, __u64 xid, const unsigned int qid[],
                cli->cl_quota_last_xid = xid;
 
        for (type = 0; type < LL_MAXQUOTAS; type++) {
-               struct osc_quota_info *oqi;
+               unsigned long bits = 0;
+               u8 *qtypes;
 
                if ((valid & md_quota_flag(type)) == 0)
                        continue;
 
-               /* lookup the ID in the per-type hash table */
-               oqi = cfs_hash_lookup(cli->cl_quota_hash[type], &qid[type]);
+               /* lookup the quota IDs in the ID xarray */
+               qtypes = xa_load(&cli->cl_quota_exceeded_ids, qid[type]);
                if ((flags & fl_quota_flag(type)) != 0) {
                        /* This ID is getting close to its quota limit, let's
-                        * switch to sync I/O */
-                       if (oqi != NULL)
-                               continue;
-
-                       oqi = osc_oqi_alloc(qid[type]);
-                       if (oqi == NULL) {
-                               rc = -ENOMEM;
-                               break;
+                        * switch to sync I/O
+                        */
+                       if (qtypes) {
+                               bits = xa_to_value(qtypes);
+                               /* test if already set */
+                               if (bits & BIT(type))
+                                       continue;
                        }
 
-                       rc = cfs_hash_add_unique(cli->cl_quota_hash[type],
-                                                &qid[type], &oqi->oqi_hash);
-                       /* race with others? */
-                       if (rc == -EALREADY) {
-                               rc = 0;
-                               OBD_SLAB_FREE_PTR(oqi, osc_quota_kmem);
-                       }
+                       bits |= BIT(type);
+                       rc = xa_insert(&cli->cl_quota_exceeded_ids, qid[type],
+                                      xa_mk_value(bits), GFP_KERNEL);
+                       if (rc)
+                               break;
 
-                       CDEBUG(D_QUOTA, "%s: setdq to insert for %s %d (%d)\n",
+                       CDEBUG(D_QUOTA, "%s: setdq to insert for %s %d: rc = %d\n",
                               cli_name(cli), qtype_name(type), qid[type], rc);
                } else {
                        /* This ID is now off the hook, let's remove it from
-                        * the hash table */
-                       if (oqi == NULL)
+                        * the xarray
+                        */
+                       if (!qtypes)
                                continue;
 
-                       oqi = cfs_hash_del_key(cli->cl_quota_hash[type],
-                                              &qid[type]);
-                       if (oqi)
-                               OBD_SLAB_FREE_PTR(oqi, osc_quota_kmem);
+                       bits = xa_to_value(qtypes);
+                       if (!(bits & BIT(type)))
+                               continue;
 
-                       CDEBUG(D_QUOTA, "%s: setdq to remove for %s %d (%p)\n",
-                              cli_name(cli), qtype_name(type), qid[type], oqi);
+                       bits &= ~BIT(type);
+                       if (bits) {
+                               if (xa_cmpxchg(&cli->cl_quota_exceeded_ids,
+                                              qid[type], qtypes,
+                                              xa_mk_value(bits),
+                                              GFP_KERNEL) != qtypes)
+                                       GOTO(out_unlock, rc = -ENOENT);
+                       } else {
+                               xa_erase(&cli->cl_quota_exceeded_ids, qid[type]);
+                       }
+
+                       CDEBUG(D_QUOTA, "%s: setdq to remove for %s %d\n",
+                              cli_name(cli), qtype_name(type), qid[type]);
                }
        }
 
@@ -171,117 +166,22 @@ out_unlock:
        RETURN(rc);
 }
 
-/*
- * Hash operations for uid/gid <-> osc_quota_info
- */
-static unsigned
-oqi_hashfn(struct cfs_hash *hs, const void *key, unsigned mask)
-{
-       return cfs_hash_32(*((__u32 *)key), 0) & mask;
-}
-
-static int
-oqi_keycmp(const void *key, struct hlist_node *hnode)
-{
-       struct osc_quota_info *oqi;
-       u32 uid;
-
-       LASSERT(key != NULL);
-       uid = *((u32 *)key);
-       oqi = hlist_entry(hnode, struct osc_quota_info, oqi_hash);
-
-       return uid == oqi->oqi_id;
-}
-
-static void *
-oqi_key(struct hlist_node *hnode)
-{
-       struct osc_quota_info *oqi;
-       oqi = hlist_entry(hnode, struct osc_quota_info, oqi_hash);
-       return &oqi->oqi_id;
-}
-
-static void *
-oqi_object(struct hlist_node *hnode)
-{
-       return hlist_entry(hnode, struct osc_quota_info, oqi_hash);
-}
-
-static void
-oqi_get(struct cfs_hash *hs, struct hlist_node *hnode)
-{
-}
-
-static void
-oqi_put_locked(struct cfs_hash *hs, struct hlist_node *hnode)
-{
-}
-
-static void
-oqi_exit(struct cfs_hash *hs, struct hlist_node *hnode)
-{
-       struct osc_quota_info *oqi;
-
-       oqi = hlist_entry(hnode, struct osc_quota_info, oqi_hash);
-
-        OBD_SLAB_FREE_PTR(oqi, osc_quota_kmem);
-}
-
-#define HASH_QUOTA_BKT_BITS 5
-#define HASH_QUOTA_CUR_BITS 5
-#define HASH_QUOTA_MAX_BITS 15
-
-static struct cfs_hash_ops quota_hash_ops = {
-       .hs_hash        = oqi_hashfn,
-       .hs_keycmp      = oqi_keycmp,
-       .hs_key         = oqi_key,
-       .hs_object      = oqi_object,
-       .hs_get         = oqi_get,
-       .hs_put_locked  = oqi_put_locked,
-       .hs_exit        = oqi_exit,
-};
-
 int osc_quota_setup(struct obd_device *obd)
 {
        struct client_obd *cli = &obd->u.cli;
-       int i, type;
-       ENTRY;
 
        mutex_init(&cli->cl_quota_mutex);
 
-       for (type = 0; type < LL_MAXQUOTAS; type++) {
-               cli->cl_quota_hash[type] = cfs_hash_create("QUOTA_HASH",
-                                                          HASH_QUOTA_CUR_BITS,
-                                                          HASH_QUOTA_MAX_BITS,
-                                                          HASH_QUOTA_BKT_BITS,
-                                                          0,
-                                                          CFS_HASH_MIN_THETA,
-                                                          CFS_HASH_MAX_THETA,
-                                                          &quota_hash_ops,
-                                                          CFS_HASH_DEFAULT);
-               if (cli->cl_quota_hash[type] == NULL)
-                       break;
-       }
-
-       if (type == LL_MAXQUOTAS)
-               RETURN(0);
-
-       for (i = 0; i < type; i++)
-               cfs_hash_putref(cli->cl_quota_hash[i]);
+       xa_init(&cli->cl_quota_exceeded_ids);
 
-       RETURN(-ENOMEM);
+       return 0;
 }
 
-int osc_quota_cleanup(struct obd_device *obd)
+void osc_quota_cleanup(struct obd_device *obd)
 {
-       struct client_obd     *cli = &obd->u.cli;
-       int type;
-       ENTRY;
-
-       for (type = 0; type < LL_MAXQUOTAS; type++)
-               cfs_hash_putref(cli->cl_quota_hash[type]);
+       struct client_obd *cli = &obd->u.cli;
 
-       RETURN(0);
+       xa_destroy(&cli->cl_quota_exceeded_ids);
 }
 
 int osc_quotactl(struct obd_device *unused, struct obd_export *exp,