Commit 1bdc8555 authored by Shashank Gupta's avatar Shashank Gupta Committed by Herbert Xu

crypto: qat - fix concurrency issue when device state changes

The sysfs `state` attribute is not protected against race conditions.
If multiple processes perform a device state transition on the same
device in parallel, unexpected behaviors might occur.

For transitioning the device state, adf_sysfs.c calls the functions
adf_dev_init(), adf_dev_start(), adf_dev_stop() and adf_dev_shutdown()
which are unprotected and interdependent on each other. To perform a
state transition, these functions needs to be called in a specific
order:
  * device up:   adf_dev_init() -> adf_dev_start()
  * device down: adf_dev_stop() -> adf_dev_shutdown()

This change introduces the functions adf_dev_up() and adf_dev_down()
which wrap the state machine functions and protect them with a
per-device lock. These are then used in adf_sysfs.c instead of the
individual state transition functions.

Fixes: 5ee52118 ("crypto: qat - expose device state through sysfs for 4xxx")
Signed-off-by: default avatarShashank Gupta <shashank.gupta@intel.com>
Reviewed-by: default avatarGiovanni Cabiddu <giovanni.cabiddu@intel.com>
Signed-off-by: default avatarHerbert Xu <herbert@gondor.apana.org.au>
parent 59a0ab49
......@@ -310,6 +310,7 @@ struct adf_accel_dev {
u8 pf_compat_ver;
} vf;
};
struct mutex state_lock; /* protect state of the device */
bool is_vf;
u32 accel_id;
};
......
......@@ -58,6 +58,9 @@ void adf_dev_stop(struct adf_accel_dev *accel_dev);
void adf_dev_shutdown(struct adf_accel_dev *accel_dev);
int adf_dev_shutdown_cache_cfg(struct adf_accel_dev *accel_dev);
int adf_dev_up(struct adf_accel_dev *accel_dev, bool init_config);
int adf_dev_down(struct adf_accel_dev *accel_dev, bool cache_config);
void adf_devmgr_update_class_index(struct adf_hw_device_data *hw_data);
void adf_clean_vf_map(bool);
......
......@@ -223,6 +223,7 @@ int adf_devmgr_add_dev(struct adf_accel_dev *accel_dev,
map->attached = true;
list_add_tail(&map->list, &vfs_table);
}
mutex_init(&accel_dev->state_lock);
unlock:
mutex_unlock(&table_lock);
return ret;
......@@ -269,6 +270,7 @@ void adf_devmgr_rm_dev(struct adf_accel_dev *accel_dev,
}
}
unlock:
mutex_destroy(&accel_dev->state_lock);
list_del(&accel_dev->list);
mutex_unlock(&table_lock);
}
......
......@@ -400,3 +400,67 @@ int adf_dev_shutdown_cache_cfg(struct adf_accel_dev *accel_dev)
return 0;
}
int adf_dev_down(struct adf_accel_dev *accel_dev, bool reconfig)
{
int ret = 0;
if (!accel_dev)
return -EINVAL;
mutex_lock(&accel_dev->state_lock);
if (!adf_dev_started(accel_dev)) {
dev_info(&GET_DEV(accel_dev), "Device qat_dev%d already down\n",
accel_dev->accel_id);
ret = -EINVAL;
goto out;
}
if (reconfig) {
ret = adf_dev_shutdown_cache_cfg(accel_dev);
goto out;
}
adf_dev_stop(accel_dev);
adf_dev_shutdown(accel_dev);
out:
mutex_unlock(&accel_dev->state_lock);
return ret;
}
EXPORT_SYMBOL_GPL(adf_dev_down);
int adf_dev_up(struct adf_accel_dev *accel_dev, bool config)
{
int ret = 0;
if (!accel_dev)
return -EINVAL;
mutex_lock(&accel_dev->state_lock);
if (adf_dev_started(accel_dev)) {
dev_info(&GET_DEV(accel_dev), "Device qat_dev%d already up\n",
accel_dev->accel_id);
ret = -EALREADY;
goto out;
}
if (config && GET_HW_DATA(accel_dev)->dev_config) {
ret = GET_HW_DATA(accel_dev)->dev_config(accel_dev);
if (unlikely(ret))
goto out;
}
ret = adf_dev_init(accel_dev);
if (unlikely(ret))
goto out;
ret = adf_dev_start(accel_dev);
out:
mutex_unlock(&accel_dev->state_lock);
return ret;
}
EXPORT_SYMBOL_GPL(adf_dev_up);
......@@ -50,38 +50,21 @@ static ssize_t state_store(struct device *dev, struct device_attribute *attr,
switch (ret) {
case DEV_DOWN:
if (!adf_dev_started(accel_dev)) {
dev_info(dev, "Device qat_dev%d already down\n",
accel_id);
return -EINVAL;
}
dev_info(dev, "Stopping device qat_dev%d\n", accel_id);
ret = adf_dev_shutdown_cache_cfg(accel_dev);
ret = adf_dev_down(accel_dev, true);
if (ret < 0)
return -EINVAL;
break;
case DEV_UP:
if (adf_dev_started(accel_dev)) {
dev_info(dev, "Device qat_dev%d already up\n",
accel_id);
return -EINVAL;
}
dev_info(dev, "Starting device qat_dev%d\n", accel_id);
ret = GET_HW_DATA(accel_dev)->dev_config(accel_dev);
if (!ret)
ret = adf_dev_init(accel_dev);
if (!ret)
ret = adf_dev_start(accel_dev);
ret = adf_dev_up(accel_dev, true);
if (ret < 0) {
dev_err(dev, "Failed to start device qat_dev%d\n",
accel_id);
adf_dev_shutdown_cache_cfg(accel_dev);
adf_dev_down(accel_dev, true);
return ret;
}
break;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment