]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
dmaengine: idxd: Add idxd_device_config_save() and idxd_device_config_restore() helpers
authorFenghua Yu <fenghua.yu@intel.com>
Fri, 22 Nov 2024 23:30:26 +0000 (15:30 -0800)
committerVinod Koul <vkoul@kernel.org>
Tue, 24 Dec 2024 10:38:04 +0000 (16:08 +0530)
Add the helpers to save and restore IDXD device configurations.

These helpers will be called during Function Level Reset (FLR) processing.

Signed-off-by: Fenghua Yu <fenghua.yu@intel.com>
Reviewed-by: Dave Jiang <dave.jiang@intel.com>
Link: https://lore.kernel.org/r/20241122233028.2762809-4-fenghua.yu@intel.com
Signed-off-by: Vinod Koul <vkoul@kernel.org>
drivers/dma/idxd/idxd.h
drivers/dma/idxd/init.c

index 0da282b84b59efa35516167eef4fc0f2699e0224..214b8039439fef791f14d521a2390566f7f37d5e 100644 (file)
@@ -374,6 +374,17 @@ struct idxd_device {
        struct dentry *dbgfs_evl_file;
 
        bool user_submission_safe;
+
+       struct idxd_saved_states *idxd_saved;
+};
+
+struct idxd_saved_states {
+       struct idxd_device saved_idxd;
+       struct idxd_evl saved_evl;
+       struct idxd_engine **saved_engines;
+       struct idxd_wq **saved_wqs;
+       struct idxd_group **saved_groups;
+       unsigned long *saved_wq_enable_map;
 };
 
 static inline unsigned int evl_ent_size(struct idxd_device *idxd)
index eb64a38444c116481ece2a4c66eb47981f0b4d5c..ade76a5f2760cf714f137700a3bc1b50fed1f795 100644 (file)
@@ -758,6 +758,231 @@ static void idxd_unbind(struct device_driver *drv, const char *buf)
        put_device(dev);
 }
 
+#define idxd_free_saved_configs(saved_configs, count)  \
+       do {                                            \
+               int i;                                  \
+                                                       \
+               for (i = 0; i < (count); i++)           \
+                       kfree(saved_configs[i]);        \
+       } while (0)
+
+static void idxd_free_saved(struct idxd_group **saved_groups,
+                           struct idxd_engine **saved_engines,
+                           struct idxd_wq **saved_wqs,
+                           struct idxd_device *idxd)
+{
+       if (saved_groups)
+               idxd_free_saved_configs(saved_groups, idxd->max_groups);
+       if (saved_engines)
+               idxd_free_saved_configs(saved_engines, idxd->max_engines);
+       if (saved_wqs)
+               idxd_free_saved_configs(saved_wqs, idxd->max_wqs);
+}
+
+/*
+ * Save IDXD device configurations including engines, groups, wqs etc.
+ * The saved configurations can be restored when needed.
+ */
+static int idxd_device_config_save(struct idxd_device *idxd,
+                                  struct idxd_saved_states *idxd_saved)
+{
+       struct device *dev = &idxd->pdev->dev;
+       int i;
+
+       memcpy(&idxd_saved->saved_idxd, idxd, sizeof(*idxd));
+
+       if (idxd->evl) {
+               memcpy(&idxd_saved->saved_evl, idxd->evl,
+                      sizeof(struct idxd_evl));
+       }
+
+       struct idxd_group **saved_groups __free(kfree) =
+                       kcalloc_node(idxd->max_groups,
+                                    sizeof(struct idxd_group *),
+                                    GFP_KERNEL, dev_to_node(dev));
+       if (!saved_groups)
+               return -ENOMEM;
+
+       for (i = 0; i < idxd->max_groups; i++) {
+               struct idxd_group *saved_group __free(kfree) =
+                       kzalloc_node(sizeof(*saved_group), GFP_KERNEL,
+                                    dev_to_node(dev));
+
+               if (!saved_group) {
+                       /* Free saved groups */
+                       idxd_free_saved(saved_groups, NULL, NULL, idxd);
+
+                       return -ENOMEM;
+               }
+
+               memcpy(saved_group, idxd->groups[i], sizeof(*saved_group));
+               saved_groups[i] = no_free_ptr(saved_group);
+       }
+
+       struct idxd_engine **saved_engines =
+                       kcalloc_node(idxd->max_engines,
+                                    sizeof(struct idxd_engine *),
+                                    GFP_KERNEL, dev_to_node(dev));
+       if (!saved_engines) {
+               /* Free saved groups */
+               idxd_free_saved(saved_groups, NULL, NULL, idxd);
+
+               return -ENOMEM;
+       }
+       for (i = 0; i < idxd->max_engines; i++) {
+               struct idxd_engine *saved_engine __free(kfree) =
+                               kzalloc_node(sizeof(*saved_engine), GFP_KERNEL,
+                                            dev_to_node(dev));
+               if (!saved_engine) {
+                       /* Free saved groups and engines */
+                       idxd_free_saved(saved_groups, saved_engines, NULL,
+                                       idxd);
+
+                       return -ENOMEM;
+               }
+
+               memcpy(saved_engine, idxd->engines[i], sizeof(*saved_engine));
+               saved_engines[i] = no_free_ptr(saved_engine);
+       }
+
+       unsigned long *saved_wq_enable_map __free(bitmap) =
+                       bitmap_zalloc_node(idxd->max_wqs, GFP_KERNEL,
+                                          dev_to_node(dev));
+       if (!saved_wq_enable_map) {
+               /* Free saved groups and engines */
+               idxd_free_saved(saved_groups, saved_engines, NULL, idxd);
+
+               return -ENOMEM;
+       }
+
+       bitmap_copy(saved_wq_enable_map, idxd->wq_enable_map, idxd->max_wqs);
+
+       struct idxd_wq **saved_wqs __free(kfree) =
+                       kcalloc_node(idxd->max_wqs, sizeof(struct idxd_wq *),
+                                    GFP_KERNEL, dev_to_node(dev));
+       if (!saved_wqs) {
+               /* Free saved groups and engines */
+               idxd_free_saved(saved_groups, saved_engines, NULL, idxd);
+
+               return -ENOMEM;
+       }
+
+       for (i = 0; i < idxd->max_wqs; i++) {
+               struct idxd_wq *saved_wq __free(kfree) =
+                       kzalloc_node(sizeof(*saved_wq), GFP_KERNEL,
+                                    dev_to_node(dev));
+               struct idxd_wq *wq;
+
+               if (!saved_wq) {
+                       /* Free saved groups, engines, and wqs */
+                       idxd_free_saved(saved_groups, saved_engines, saved_wqs,
+                                       idxd);
+
+                       return -ENOMEM;
+               }
+
+               if (!test_bit(i, saved_wq_enable_map))
+                       continue;
+
+               wq = idxd->wqs[i];
+               mutex_lock(&wq->wq_lock);
+               memcpy(saved_wq, wq, sizeof(*saved_wq));
+               saved_wqs[i] = no_free_ptr(saved_wq);
+               mutex_unlock(&wq->wq_lock);
+       }
+
+       /* Save configurations */
+       idxd_saved->saved_groups = no_free_ptr(saved_groups);
+       idxd_saved->saved_engines = no_free_ptr(saved_engines);
+       idxd_saved->saved_wq_enable_map = no_free_ptr(saved_wq_enable_map);
+       idxd_saved->saved_wqs = no_free_ptr(saved_wqs);
+
+       return 0;
+}
+
+/*
+ * Restore IDXD device configurations including engines, groups, wqs etc
+ * that were saved before.
+ */
+static void idxd_device_config_restore(struct idxd_device *idxd,
+                                      struct idxd_saved_states *idxd_saved)
+{
+       struct idxd_evl *saved_evl = &idxd_saved->saved_evl;
+       int i;
+
+       idxd->rdbuf_limit = idxd_saved->saved_idxd.rdbuf_limit;
+
+       if (saved_evl)
+               idxd->evl->size = saved_evl->size;
+
+       for (i = 0; i < idxd->max_groups; i++) {
+               struct idxd_group *saved_group, *group;
+
+               saved_group = idxd_saved->saved_groups[i];
+               group = idxd->groups[i];
+
+               group->rdbufs_allowed = saved_group->rdbufs_allowed;
+               group->rdbufs_reserved = saved_group->rdbufs_reserved;
+               group->tc_a = saved_group->tc_a;
+               group->tc_b = saved_group->tc_b;
+               group->use_rdbuf_limit = saved_group->use_rdbuf_limit;
+
+               kfree(saved_group);
+       }
+       kfree(idxd_saved->saved_groups);
+
+       for (i = 0; i < idxd->max_engines; i++) {
+               struct idxd_engine *saved_engine, *engine;
+
+               saved_engine = idxd_saved->saved_engines[i];
+               engine = idxd->engines[i];
+
+               engine->group = saved_engine->group;
+
+               kfree(saved_engine);
+       }
+       kfree(idxd_saved->saved_engines);
+
+       bitmap_copy(idxd->wq_enable_map, idxd_saved->saved_wq_enable_map,
+                   idxd->max_wqs);
+       bitmap_free(idxd_saved->saved_wq_enable_map);
+
+       for (i = 0; i < idxd->max_wqs; i++) {
+               struct idxd_wq *saved_wq, *wq;
+               size_t len;
+
+               if (!test_bit(i, idxd->wq_enable_map))
+                       continue;
+
+               saved_wq = idxd_saved->saved_wqs[i];
+               wq = idxd->wqs[i];
+
+               mutex_lock(&wq->wq_lock);
+
+               wq->group = saved_wq->group;
+               wq->flags = saved_wq->flags;
+               wq->threshold = saved_wq->threshold;
+               wq->size = saved_wq->size;
+               wq->priority = saved_wq->priority;
+               wq->type = saved_wq->type;
+               len = strlen(saved_wq->name) + 1;
+               strscpy(wq->name, saved_wq->name, len);
+               wq->max_xfer_bytes = saved_wq->max_xfer_bytes;
+               wq->max_batch_size = saved_wq->max_batch_size;
+               wq->enqcmds_retries = saved_wq->enqcmds_retries;
+               wq->descs = saved_wq->descs;
+               wq->idxd_chan = saved_wq->idxd_chan;
+               len = strlen(saved_wq->driver_name) + 1;
+               strscpy(wq->driver_name, saved_wq->driver_name, len);
+
+               mutex_unlock(&wq->wq_lock);
+
+               kfree(saved_wq);
+       }
+
+       kfree(idxd_saved->saved_wqs);
+}
+
 /*
  * Probe idxd PCI device.
  * If idxd is not given, need to allocate idxd and set up its data.