Commit 5ab6c06d authored by Małgorzata Mielnik's avatar Małgorzata Mielnik Committed by Herbert Xu
Browse files

crypto: qat - refactor service parsing logic



The service parsing logic is used to parse the configuration string
provided by the user using the attribute qat/cfg_services in sysfs.
The logic relies on hard-coded strings. For example, the service
"sym;asym" is also replicated as "asym;sym".
This makes the addition of new services or service combinations
complex as it requires the addition of new hard-coded strings for all
possible combinations.

This commit addresses this issue by:
 * reducing the number of internal service strings to only the basic
   service representations.
 * modifying the service parsing logic to analyze the service string
   token by token instead of comparing a whole string with patterns.
 * introducing the concept of a service mask where each service is
   represented by a single bit.
 * dividing the parsing logic into several functions to allow for code
   reuse (e.g. by sysfs-related functions).
 * introducing a new, device generation-specific function to verify
   whether the requested service combination is supported by the
   currently used device.

Signed-off-by: default avatarMałgorzata Mielnik <malgorzata.mielnik@intel.com>
Co-developed-by: default avatarGiovanni Cabiddu <giovanni.cabiddu@intel.com>
Signed-off-by: default avatarGiovanni Cabiddu <giovanni.cabiddu@intel.com>
Reviewed-by: default avatarAndy Shevchenko <andriy.shevchenko@linux.intel.com>
Signed-off-by: default avatarHerbert Xu <herbert@gondor.apana.org.au>
parent 9057f824
Loading
Loading
Loading
Loading
+4 −12
Original line number Diff line number Diff line
@@ -106,8 +106,7 @@ static u32 get_ae_mask(struct adf_hw_device_data *self)
static u32 uof_get_num_objs(struct adf_accel_dev *accel_dev)
{
	switch (adf_get_service_enabled(accel_dev)) {
	case SVC_CY:
	case SVC_CY2:
	case SVC_SYM_ASYM:
		return ARRAY_SIZE(adf_fw_cy_config);
	case SVC_DC:
		return ARRAY_SIZE(adf_fw_dc_config);
@@ -118,10 +117,8 @@ static u32 uof_get_num_objs(struct adf_accel_dev *accel_dev)
	case SVC_ASYM:
		return ARRAY_SIZE(adf_fw_asym_config);
	case SVC_ASYM_DC:
	case SVC_DC_ASYM:
		return ARRAY_SIZE(adf_fw_asym_dc_config);
	case SVC_SYM_DC:
	case SVC_DC_SYM:
		return ARRAY_SIZE(adf_fw_sym_dc_config);
	default:
		return 0;
@@ -131,8 +128,7 @@ static u32 uof_get_num_objs(struct adf_accel_dev *accel_dev)
static const struct adf_fw_config *get_fw_config(struct adf_accel_dev *accel_dev)
{
	switch (adf_get_service_enabled(accel_dev)) {
	case SVC_CY:
	case SVC_CY2:
	case SVC_SYM_ASYM:
		return adf_fw_cy_config;
	case SVC_DC:
		return adf_fw_dc_config;
@@ -143,10 +139,8 @@ static const struct adf_fw_config *get_fw_config(struct adf_accel_dev *accel_dev
	case SVC_ASYM:
		return adf_fw_asym_config;
	case SVC_ASYM_DC:
	case SVC_DC_ASYM:
		return adf_fw_asym_dc_config;
	case SVC_SYM_DC:
	case SVC_DC_SYM:
		return adf_fw_sym_dc_config;
	default:
		return NULL;
@@ -266,8 +260,7 @@ static u32 get_accel_cap(struct adf_accel_dev *accel_dev)
	}

	switch (adf_get_service_enabled(accel_dev)) {
	case SVC_CY:
	case SVC_CY2:
	case SVC_SYM_ASYM:
		return capabilities_sym | capabilities_asym;
	case SVC_DC:
		return capabilities_dc;
@@ -284,10 +277,8 @@ static u32 get_accel_cap(struct adf_accel_dev *accel_dev)
	case SVC_ASYM:
		return capabilities_asym;
	case SVC_ASYM_DC:
	case SVC_DC_ASYM:
		return capabilities_asym | capabilities_dc;
	case SVC_SYM_DC:
	case SVC_DC_SYM:
		return capabilities_sym | capabilities_dc;
	default:
		return 0;
@@ -482,6 +473,7 @@ void adf_init_hw_data_420xx(struct adf_hw_device_data *hw_data, u32 dev_id)
	hw_data->get_hb_clock = adf_gen4_get_heartbeat_clock;
	hw_data->num_hb_ctrs = ADF_NUM_HB_CNT_PER_AE;
	hw_data->clock_frequency = ADF_420XX_AE_FREQ;
	hw_data->services_supported = adf_gen4_services_supported;

	adf_gen4_set_err_mask(&hw_data->dev_err_mask);
	adf_gen4_init_hw_csr_ops(&hw_data->csr_ops);
+3 −8
Original line number Diff line number Diff line
@@ -178,8 +178,7 @@ static u32 get_accel_cap(struct adf_accel_dev *accel_dev)
	}

	switch (adf_get_service_enabled(accel_dev)) {
	case SVC_CY:
	case SVC_CY2:
	case SVC_SYM_ASYM:
		return capabilities_sym | capabilities_asym;
	case SVC_DC:
		return capabilities_dc;
@@ -196,10 +195,8 @@ static u32 get_accel_cap(struct adf_accel_dev *accel_dev)
	case SVC_ASYM:
		return capabilities_asym;
	case SVC_ASYM_DC:
	case SVC_DC_ASYM:
		return capabilities_asym | capabilities_dc;
	case SVC_SYM_DC:
	case SVC_DC_SYM:
		return capabilities_sym | capabilities_dc;
	default:
		return 0;
@@ -241,8 +238,7 @@ static u32 uof_get_num_objs(struct adf_accel_dev *accel_dev)
static const struct adf_fw_config *get_fw_config(struct adf_accel_dev *accel_dev)
{
	switch (adf_get_service_enabled(accel_dev)) {
	case SVC_CY:
	case SVC_CY2:
	case SVC_SYM_ASYM:
		return adf_fw_cy_config;
	case SVC_DC:
		return adf_fw_dc_config;
@@ -253,10 +249,8 @@ static const struct adf_fw_config *get_fw_config(struct adf_accel_dev *accel_dev
	case SVC_ASYM:
		return adf_fw_asym_config;
	case SVC_ASYM_DC:
	case SVC_DC_ASYM:
		return adf_fw_asym_dc_config;
	case SVC_SYM_DC:
	case SVC_DC_SYM:
		return adf_fw_sym_dc_config;
	default:
		return NULL;
@@ -466,6 +460,7 @@ void adf_init_hw_data_4xxx(struct adf_hw_device_data *hw_data, u32 dev_id)
	hw_data->get_hb_clock = adf_gen4_get_heartbeat_clock;
	hw_data->num_hb_ctrs = ADF_NUM_HB_CNT_PER_AE;
	hw_data->clock_frequency = ADF_4XXX_AE_FREQ;
	hw_data->services_supported = adf_gen4_services_supported;

	adf_gen4_set_err_mask(&hw_data->dev_err_mask);
	adf_gen4_init_hw_csr_ops(&hw_data->csr_ops);
+1 −0
Original line number Diff line number Diff line
@@ -333,6 +333,7 @@ struct adf_hw_device_data {
	int (*get_rp_group)(struct adf_accel_dev *accel_dev, u32 ae_mask);
	u32 (*get_ena_thd_mask)(struct adf_accel_dev *accel_dev, u32 obj_num);
	int (*dev_config)(struct adf_accel_dev *accel_dev);
	bool (*services_supported)(unsigned long mask);
	struct adf_pfvf_ops pfvf_ops;
	struct adf_hw_csr_ops csr_ops;
	struct adf_dc_ops dc_ops;
+147 −19
Original line number Diff line number Diff line
// SPDX-License-Identifier: GPL-2.0-only
/* Copyright(c) 2023 Intel Corporation */

#include <linux/array_size.h>
#include <linux/bitops.h>
#include <linux/export.h>
#include <linux/pci.h>
#include <linux/string.h>
@@ -8,39 +10,165 @@
#include "adf_cfg_services.h"
#include "adf_cfg_strings.h"

const char *const adf_cfg_services[] = {
	[SVC_CY] = ADF_CFG_CY,
	[SVC_CY2] = ADF_CFG_ASYM_SYM,
static const char *const adf_cfg_services[] = {
	[SVC_ASYM] = ADF_CFG_ASYM,
	[SVC_SYM] = ADF_CFG_SYM,
	[SVC_DC] = ADF_CFG_DC,
	[SVC_DCC] = ADF_CFG_DCC,
	[SVC_SYM] = ADF_CFG_SYM,
	[SVC_ASYM] = ADF_CFG_ASYM,
	[SVC_DC_ASYM] = ADF_CFG_DC_ASYM,
	[SVC_ASYM_DC] = ADF_CFG_ASYM_DC,
	[SVC_DC_SYM] = ADF_CFG_DC_SYM,
	[SVC_SYM_DC] = ADF_CFG_SYM_DC,
};

int adf_get_service_enabled(struct adf_accel_dev *accel_dev)
/*
 * Ensure that the size of the array matches the number of services,
 * SVC_BASE_COUNT, that is used to size the bitmap.
 */
static_assert(ARRAY_SIZE(adf_cfg_services) == SVC_BASE_COUNT);

/*
 * Ensure that the maximum number of concurrent services that can be
 * enabled on a device is less than or equal to the number of total
 * supported services.
 */
static_assert(ARRAY_SIZE(adf_cfg_services) >= MAX_NUM_CONCURR_SVC);

/*
 * Ensure that the number of services fit a single unsigned long, as each
 * service is represented by a bit in the mask.
 */
static_assert(BITS_PER_LONG >= SVC_BASE_COUNT);

/*
 * Ensure that size of the concatenation of all service strings is smaller
 * than the size of the buffer that will contain them.
 */
static_assert(sizeof(ADF_CFG_SYM ADF_SERVICES_DELIMITER
		     ADF_CFG_ASYM ADF_SERVICES_DELIMITER
		     ADF_CFG_DC ADF_SERVICES_DELIMITER
		     ADF_CFG_DCC) < ADF_CFG_MAX_VAL_LEN_IN_BYTES);

static int adf_service_string_to_mask(struct adf_accel_dev *accel_dev, const char *buf,
				      size_t len, unsigned long *out_mask)
{
	char services[ADF_CFG_MAX_VAL_LEN_IN_BYTES] = {0};
	struct adf_hw_device_data *hw_data = GET_HW_DATA(accel_dev);
	char services[ADF_CFG_MAX_VAL_LEN_IN_BYTES] = { };
	unsigned long mask = 0;
	char *substr, *token;
	int id, num_svc = 0;

	if (len > ADF_CFG_MAX_VAL_LEN_IN_BYTES - 1)
		return -EINVAL;

	strscpy(services, buf, ADF_CFG_MAX_VAL_LEN_IN_BYTES);
	substr = services;

	while ((token = strsep(&substr, ADF_SERVICES_DELIMITER))) {
		id = sysfs_match_string(adf_cfg_services, token);
		if (id < 0)
			return id;

		if (test_and_set_bit(id, &mask))
			return -EINVAL;

		if (num_svc++ == MAX_NUM_CONCURR_SVC)
			return -EINVAL;
	}

	if (hw_data->services_supported && !hw_data->services_supported(mask))
		return -EINVAL;

	*out_mask = mask;

	return 0;
}

static int adf_service_mask_to_string(unsigned long mask, char *buf, size_t len)
{
	int offset = 0;
	int bit;

	if (len < ADF_CFG_MAX_VAL_LEN_IN_BYTES)
		return -ENOSPC;

	for_each_set_bit(bit, &mask, SVC_BASE_COUNT) {
		if (offset)
			offset += scnprintf(buf + offset, len - offset,
					    ADF_SERVICES_DELIMITER);

		offset += scnprintf(buf + offset, len - offset, "%s",
				    adf_cfg_services[bit]);
	}

	return 0;
}

int adf_parse_service_string(struct adf_accel_dev *accel_dev, const char *in,
			     size_t in_len, char *out, size_t out_len)
{
	unsigned long mask;
	int ret;

	ret = adf_service_string_to_mask(accel_dev, in, in_len, &mask);
	if (ret)
		return ret;

	if (!mask)
		return -EINVAL;

	return adf_service_mask_to_string(mask, out, out_len);
}

static int adf_get_service_mask(struct adf_accel_dev *accel_dev, unsigned long *mask)
{
	char services[ADF_CFG_MAX_VAL_LEN_IN_BYTES] = { };
	size_t len;
	int ret;

	ret = adf_cfg_get_param_value(accel_dev, ADF_GENERAL_SEC,
				      ADF_SERVICES_ENABLED, services);
	if (ret) {
		dev_err(&GET_DEV(accel_dev),
			ADF_SERVICES_ENABLED " param not found\n");
		dev_err(&GET_DEV(accel_dev), "%s param not found\n",
			ADF_SERVICES_ENABLED);
		return ret;
	}

	len = strnlen(services, ADF_CFG_MAX_VAL_LEN_IN_BYTES);
	ret = adf_service_string_to_mask(accel_dev, services, len, mask);
	if (ret)
		dev_err(&GET_DEV(accel_dev), "Invalid value of %s param: %s\n",
			ADF_SERVICES_ENABLED, services);

	return ret;
}

	ret = match_string(adf_cfg_services, ARRAY_SIZE(adf_cfg_services),
			   services);
	if (ret < 0)
		dev_err(&GET_DEV(accel_dev),
			"Invalid value of " ADF_SERVICES_ENABLED " param: %s\n",
			services);
int adf_get_service_enabled(struct adf_accel_dev *accel_dev)
{
	unsigned long mask;
	int ret;

	ret = adf_get_service_mask(accel_dev, &mask);
	if (ret)
		return ret;

	if (test_bit(SVC_SYM, &mask) && test_bit(SVC_ASYM, &mask))
		return SVC_SYM_ASYM;

	if (test_bit(SVC_SYM, &mask) && test_bit(SVC_DC, &mask))
		return SVC_SYM_DC;

	if (test_bit(SVC_ASYM, &mask) && test_bit(SVC_DC, &mask))
		return SVC_ASYM_DC;

	if (test_bit(SVC_SYM, &mask))
		return SVC_SYM;

	if (test_bit(SVC_ASYM, &mask))
		return SVC_ASYM;

	if (test_bit(SVC_DC, &mask))
		return SVC_DC;

	if (test_bit(SVC_DCC, &mask))
		return SVC_DCC;

	return -EINVAL;
}
EXPORT_SYMBOL_GPL(adf_get_service_enabled);
+17 −9
Original line number Diff line number Diff line
@@ -8,21 +8,29 @@
struct adf_accel_dev;

enum adf_services {
	SVC_CY = 0,
	SVC_CY2,
	SVC_ASYM = 0,
	SVC_SYM,
	SVC_DC,
	SVC_DCC,
	SVC_SYM,
	SVC_ASYM,
	SVC_DC_ASYM,
	SVC_ASYM_DC,
	SVC_DC_SYM,
	SVC_BASE_COUNT
};

enum adf_composed_services {
	SVC_SYM_ASYM = SVC_BASE_COUNT,
	SVC_SYM_DC,
	SVC_COUNT
	SVC_ASYM_DC,
};

enum {
	ADF_ONE_SERVICE = 1,
	ADF_TWO_SERVICES,
	ADF_THREE_SERVICES,
};

extern const char *const adf_cfg_services[SVC_COUNT];
#define MAX_NUM_CONCURR_SVC	ADF_THREE_SERVICES

int adf_parse_service_string(struct adf_accel_dev *accel_dev, const char *in,
			     size_t in_len, char *out, size_t out_len);
int adf_get_service_enabled(struct adf_accel_dev *accel_dev);

#endif
Loading