mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-12-12 05:24:12 +08:00
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: 5ee52118ac
("crypto: qat - expose device state through sysfs for 4xxx")
Signed-off-by: Shashank Gupta <shashank.gupta@intel.com>
Reviewed-by: Giovanni Cabiddu <giovanni.cabiddu@intel.com>
Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
This commit is contained in:
parent
59a0ab4953
commit
1bdc85550a
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user