[RFC PATCH v12 06/17] dlb: add domain software reset

Mike Ximing Chen
2021-12-21T06:50:37Z

Add operation to reset a domain's resource's software state when its
reference count reaches zero, and re-inserts those resources in their
respective available-resources linked lists, for use by future scheduling
domains.

Signed-off-by: Mike Ximing Chen <mike.ximing.chen@intel.com>
---
drivers/misc/dlb/dlb_configfs.c | 10 +-
drivers/misc/dlb/dlb_main.c | 10 +-
drivers/misc/dlb/dlb_main.h | 36 ++++++
drivers/misc/dlb/dlb_resource.c | 204 ++++++++++++++++++++++++++++++++
include/uapi/linux/dlb.h | 1 +
5 files changed, 259 insertions(+), 2 deletions(-)

diff --git a/drivers/misc/dlb/dlb_configfs.c b/drivers/misc/dlb/dlb_configfs.c
index 7e5db0390a6a..dc4d1a3bae0f 100644
--- a/drivers/misc/dlb/dlb_configfs.c
+++ b/drivers/misc/dlb/dlb_configfs.c
@@ -20,13 +20,21 @@ static int dlb_configfs_create_sched_domain(struct dlb *dlb,

mutex_lock(&dlb->resource_mutex);

+ if (dlb->domain_reset_failed) {
+ response.status = DLB_ST_DOMAIN_RESET_FAILED;
+ ret = -EINVAL;
+ goto unlock;
+ }
+
ret = dlb_hw_create_sched_domain(&dlb->hw, arg, &response);
if (ret)
goto unlock;

ret = dlb_init_domain(dlb, response.id);
- if (ret)
+ if (ret) {
+ dlb_reset_domain(&dlb->hw, response.id);
goto unlock;
+ }

domain = dlb->sched_domains[response.id];

diff --git a/drivers/misc/dlb/dlb_main.c b/drivers/misc/dlb/dlb_main.c
index 3c949d9b4cf0..343bf72dc9c7 100644
--- a/drivers/misc/dlb/dlb_main.c
+++ b/drivers/misc/dlb/dlb_main.c
@@ -101,12 +101,20 @@ int dlb_init_domain(struct dlb *dlb, u32 domain_id)
static int __dlb_free_domain(struct dlb_domain *domain)
{
struct dlb *dlb = domain->dlb;
+ int ret;
+
+ ret = dlb_reset_domain(&dlb->hw, domain->id);
+ if (ret) {
+ dlb->domain_reset_failed = true;
+ dev_err(dlb->dev,
+ "Internal error: Domain reset failed. To recover, reset the device.\n");
+ }

dlb->sched_domains[domain->id] = NULL;

kfree(domain);

- return 0;
+ return ret;
}

void dlb_free_domain(struct kref *kref)
diff --git a/drivers/misc/dlb/dlb_main.h b/drivers/misc/dlb/dlb_main.h
index 6c9b9cce148e..b6e79397f312 100644
--- a/drivers/misc/dlb/dlb_main.h
+++ b/drivers/misc/dlb/dlb_main.h
@@ -139,6 +139,12 @@ struct dlb_sn_group {
u32 id;
};

+static inline void
+dlb_sn_group_free_slot(struct dlb_sn_group *group, int slot)
+{
+ group->slot_use_bitmap &= ~(BIT(slot));
+}
+
/*
* Scheduling domain level resource data structure.
*
@@ -271,6 +277,7 @@ struct dlb {
enum dlb_device_type type;
int id;
dev_t dev_number;
+ u8 domain_reset_failed;
};

/*************************/
@@ -337,6 +344,34 @@ static inline void dlb_bitmap_free(struct dlb_bitmap *bitmap)
kfree(bitmap);
}

+/**
+ * dlb_bitmap_set_range() - set a range of bitmap entries
+ * @bitmap: pointer to dlb_bitmap structure.
+ * @bit: starting bit index.
+ * @len: length of the range.
+ *
+ * Return:
+ * Returns 0 upon success, < 0 otherwise.
+ *
+ * Errors:
+ * EINVAL - bitmap is NULL or is uninitialized, or the range exceeds the bitmap
+ * length.
+ */
+static inline int dlb_bitmap_set_range(struct dlb_bitmap *bitmap,
+ unsigned int bit,
+ unsigned int len)
+{
+ if (!bitmap || !bitmap->map)
+ return -EINVAL;
+
+ if (bitmap->len <= bit)
+ return -EINVAL;
+
+ bitmap_set(bitmap->map, bit, len);
+
+ return 0;
+}
+
/**
* dlb_bitmap_clear_range() - clear a range of bitmap entries
* @bitmap: pointer to dlb_bitmap structure.
@@ -460,6 +495,7 @@ void dlb_resource_free(struct dlb_hw *hw);
int dlb_hw_create_sched_domain(struct dlb_hw *hw,
struct dlb_create_sched_domain_args *args,
struct dlb_cmd_response *resp);
+int dlb_reset_domain(struct dlb_hw *hw, u32 domain_id);
void dlb_clr_pmcsr_disable(struct dlb_hw *hw);

/* Prototypes for dlb_configfs.c */
diff --git a/drivers/misc/dlb/dlb_resource.c b/drivers/misc/dlb/dlb_resource.c
index 2d74d158219c..c5b3f1ff3d7e 100644
--- a/drivers/misc/dlb/dlb_resource.c
+++ b/drivers/misc/dlb/dlb_resource.c
@@ -190,6 +190,14 @@ int dlb_resource_init(struct dlb_hw *hw)
return ret;
}

+static struct dlb_hw_domain *dlb_get_domain_from_id(struct dlb_hw *hw, u32 id)
+{
+ if (id >= DLB_MAX_NUM_DOMAINS)
+ return NULL;
+
+ return &hw->domains[id];
+}
+
static int dlb_attach_ldb_queues(struct dlb_hw *hw,
struct dlb_function_resources *rsrcs,
struct dlb_hw_domain *domain, u32 num_queues,
@@ -726,6 +734,202 @@ int dlb_hw_create_sched_domain(struct dlb_hw *hw,
return 0;
}

+/*
+ * dlb_domain_reset_software_state() - returns domain's resources
+ * @hw: dlb_hw handle for a particular device.
+ * @domain: pointer to scheduling domain.
+ *
+ * This function returns the resources allocated/assigned to a domain back to
+ * the device/function level resource pool. These resources include ldb/dir
+ * queues, ports, history lists, etc. It is called by the dlb_reset_domain().
+ * When a domain is created/initialized, resources are moved to a domain from
+ * the resource pool.
+ *
+ */
+static int dlb_domain_reset_software_state(struct dlb_hw *hw,
+ struct dlb_hw_domain *domain)
+{
+ struct dlb *dlb = container_of(hw, struct dlb, hw);
+ struct dlb_dir_pq_pair *tmp_dir_port;
+ struct dlb_function_resources *rsrcs;
+ struct dlb_ldb_queue *tmp_ldb_queue;
+ struct dlb_ldb_port *tmp_ldb_port;
+ struct dlb_dir_pq_pair *dir_port;
+ struct dlb_ldb_queue *ldb_queue;
+ struct dlb_ldb_port *ldb_port;
+ int ret, i;
+
+ lockdep_assert_held(&dlb->resource_mutex);
+
+ rsrcs = domain->parent_func;
+
+ /* Move the domain's ldb queues to the function's avail list */
+ list_for_each_entry_safe(ldb_queue, tmp_ldb_queue,
+ &domain->used_ldb_queues, domain_list) {
+ if (ldb_queue->sn_cfg_valid) {
+ struct dlb_sn_group *grp;
+
+ grp = &hw->rsrcs.sn_groups[ldb_queue->sn_group];
+
+ dlb_sn_group_free_slot(grp, ldb_queue->sn_slot);
+ ldb_queue->sn_cfg_valid = false;
+ }
+
+ ldb_queue->owned = false;
+ ldb_queue->num_mappings = 0;
+ ldb_queue->num_pending_additions = 0;
+
+ list_del(&ldb_queue->domain_list);
+ list_add(&ldb_queue->func_list, &rsrcs->avail_ldb_queues);
+ rsrcs->num_avail_ldb_queues++;
+ }
+
+ list_for_each_entry_safe(ldb_queue, tmp_ldb_queue,
+ &domain->avail_ldb_queues, domain_list) {
+ ldb_queue->owned = false;
+
+ list_del(&ldb_queue->domain_list);
+ list_add(&ldb_queue->func_list, &rsrcs->avail_ldb_queues);
+ rsrcs->num_avail_ldb_queues++;
+ }
+
+ /* Move the domain's ldb ports to the function's avail list */
+ for (i = 0; i < DLB_NUM_COS_DOMAINS; i++) {
+ list_for_each_entry_safe(ldb_port, tmp_ldb_port,
+ &domain->used_ldb_ports[i], domain_list) {
+ int j;
+
+ ldb_port->owned = false;
+ ldb_port->configured = false;
+ ldb_port->num_pending_removals = 0;
+ ldb_port->num_mappings = 0;
+ ldb_port->init_tkn_cnt = 0;
+ for (j = 0; j < DLB_MAX_NUM_QIDS_PER_LDB_CQ; j++)
+ ldb_port->qid_map[j].state =
+ DLB_QUEUE_UNMAPPED;
+
+ list_del(&ldb_port->domain_list);
+ list_add(&ldb_port->func_list,
+ &rsrcs->avail_ldb_ports[i]);
+ rsrcs->num_avail_ldb_ports[i]++;
+ }
+
+ list_for_each_entry_safe(ldb_port, tmp_ldb_port,
+ &domain->avail_ldb_ports[i], domain_list) {
+ ldb_port->owned = false;
+
+ list_del(&ldb_port->domain_list);
+ list_add(&ldb_port->func_list,
+ &rsrcs->avail_ldb_ports[i]);
+ rsrcs->num_avail_ldb_ports[i]++;
+ }
+ }
+
+ /* Move the domain's dir ports to the function's avail list */
+ list_for_each_entry_safe(dir_port, tmp_dir_port,
+ &domain->used_dir_pq_pairs, domain_list) {
+ dir_port->owned = false;
+ dir_port->port_configured = false;
+ dir_port->init_tkn_cnt = 0;
+
+ list_del(&dir_port->domain_list);
+
+ list_add(&dir_port->func_list, &rsrcs->avail_dir_pq_pairs);
+ rsrcs->num_avail_dir_pq_pairs++;
+ }
+
+ list_for_each_entry_safe(dir_port, tmp_dir_port,
+ &domain->avail_dir_pq_pairs, domain_list) {
+ dir_port->owned = false;
+
+ list_del(&dir_port->domain_list);
+
+ list_add(&dir_port->func_list, &rsrcs->avail_dir_pq_pairs);
+ rsrcs->num_avail_dir_pq_pairs++;
+ }
+
+ /* Return hist list entries to the function */
+ ret = dlb_bitmap_set_range(rsrcs->avail_hist_list_entries,
+ domain->hist_list_entry_base,
+ domain->total_hist_list_entries);
+ if (ret) {
+ dev_err(hw_to_dev(hw),
+ "[%s()] Internal error: domain hist list base doesn't match the function's bitmap.\n",
+ __func__);
+ return ret;
+ }
+
+ domain->total_hist_list_entries = 0;
+ domain->avail_hist_list_entries = 0;
+ domain->hist_list_entry_base = 0;
+ domain->hist_list_entry_offset = 0;
+
+ rsrcs->num_avail_qed_entries += domain->num_ldb_credits;
+ domain->num_ldb_credits = 0;
+
+ rsrcs->num_avail_dqed_entries += domain->num_dir_credits;
+ domain->num_dir_credits = 0;
+
+ rsrcs->num_avail_aqed_entries += domain->num_avail_aqed_entries;
+ rsrcs->num_avail_aqed_entries += domain->num_used_aqed_entries;
+ domain->num_avail_aqed_entries = 0;
+ domain->num_used_aqed_entries = 0;
+
+ domain->num_pending_removals = 0;
+ domain->num_pending_additions = 0;
+ domain->configured = false;
+ domain->started = false;
+
+ /*
+ * Move the domain out of the used_domains list and back to the
+ * function's avail_domains list.
+ */
+ list_move(&domain->func_list, &rsrcs->avail_domains);
+ rsrcs->num_avail_domains++;
+
+ return 0;
+}
+
+static void dlb_log_reset_domain(struct dlb_hw *hw, u32 domain_id)
+{
+ dev_dbg(hw_to_dev(hw), "DLB reset domain:\n");
+ dev_dbg(hw_to_dev(hw), "\tDomain ID: %d\n", domain_id);
+}
+
+/**
+ * dlb_reset_domain() - reset a scheduling domain
+ * @hw: dlb_hw handle for a particular device.
+ * @domain_id: domain ID.
+ *
+ * This function resets and frees a DLB 2.0 scheduling domain and its associated
+ * resources.
+ *
+ * Pre-condition: the driver must ensure software has stopped sending QEs
+ * through this domain's producer ports before invoking this function, or
+ * undefined behavior will result.
+ *
+ * Return:
+ * Returns 0 upon success, -1 otherwise.
+ *
+ * EINVAL - Invalid domain ID, or the domain is not configured.
+ * EFAULT - Internal error. (Possibly caused if software is the pre-condition
+ * is not met.)
+ * ETIMEDOUT - Hardware component didn't reset in the expected time.
+ */
+int dlb_reset_domain(struct dlb_hw *hw, u32 domain_id)
+{
+ struct dlb_hw_domain *domain;
+
+ dlb_log_reset_domain(hw, domain_id);
+
+ domain = dlb_get_domain_from_id(hw, domain_id);
+
+ if (!domain || !domain->configured)
+ return -EINVAL;
+
+ return dlb_domain_reset_software_state(hw, domain);
+}
+
/**
* dlb_clr_pmcsr_disable() - power on bulk of DLB 2.0 logic
* @hw: dlb_hw handle for a particular device.
diff --git a/include/uapi/linux/dlb.h b/include/uapi/linux/dlb.h
index 91551d8a0175..580a5cafbe61 100644
--- a/include/uapi/linux/dlb.h
+++ b/include/uapi/linux/dlb.h
@@ -18,5 +18,6 @@ enum dlb_error {
DLB_ST_ATOMIC_INFLIGHTS_UNAVAILABLE,
DLB_ST_HIST_LIST_ENTRIES_UNAVAILABLE,
DLB_ST_LDB_PORT_REQUIRED_FOR_LDB_QUEUES,
+ DLB_ST_DOMAIN_RESET_FAILED,
};
#endif /* __DLB_H */
--
2.27.0