diff mbox series

[5/5] platform/x86: think-lmi: mutex protection around multiple WMI calls

Message ID 20230525193132.3727-5-mpearson-lenovo@squebb.ca (mailing list archive)
State Superseded, archived
Headers show
Series [1/5] platform/x86: think-lmi: Enable opcode support on BIOS settings | expand

Commit Message

Mark Pearson May 25, 2023, 7:31 p.m. UTC
Add mutex protection around cases where an operation needs multiple
WMI calls - e.g. setting password.

Signed-off-by: Mark Pearson <mpearson-lenovo@squebb.ca>
---
Changes in V2: New commit added after review of other patches in series.

 drivers/platform/x86/think-lmi.c | 46 ++++++++++++++++++++++++--------
 1 file changed, 35 insertions(+), 11 deletions(-)

Comments

Hans de Goede May 25, 2023, 7:41 p.m. UTC | #1
Hi Mark,

On 5/25/23 21:31, Mark Pearson wrote:
> Add mutex protection around cases where an operation needs multiple
> WMI calls - e.g. setting password.
> 
> Signed-off-by: Mark Pearson <mpearson-lenovo@squebb.ca>
> ---
> Changes in V2: New commit added after review of other patches in series.
> 
>  drivers/platform/x86/think-lmi.c | 46 ++++++++++++++++++++++++--------
>  1 file changed, 35 insertions(+), 11 deletions(-)
> 
> diff --git a/drivers/platform/x86/think-lmi.c b/drivers/platform/x86/think-lmi.c
> index 64cd453d6e7d..f3e1e4dacba2 100644
> --- a/drivers/platform/x86/think-lmi.c
> +++ b/drivers/platform/x86/think-lmi.c
> @@ -14,6 +14,7 @@
>  #include <linux/acpi.h>
>  #include <linux/errno.h>
>  #include <linux/fs.h>
> +#include <linux/mutex.h>
>  #include <linux/string.h>
>  #include <linux/types.h>
>  #include <linux/dmi.h>
> @@ -195,6 +196,7 @@ static const char * const level_options[] = {
>  };
>  static struct think_lmi tlmi_priv;
>  static struct class *fw_attr_class;
> +static DEFINE_MUTEX(tlmi_mutex);
>  
>  /* ------ Utility functions ------------*/
>  /* Strip out CR if one is present */
> @@ -463,23 +465,32 @@ static ssize_t new_password_store(struct kobject *kobj,
>  			sprintf(pwd_type, "%s", setting->pwd_type);
>  		}
>  
> +		mutex_lock(&tlmi_mutex);
>  		ret = tlmi_opcode_setting("WmiOpcodePasswordType", pwd_type);
> -		if (ret)
> +		if (ret) {
> +			mutex_unlock(&tlmi_mutex);
>  			goto out;
> -
> +		}
>  		if (tlmi_priv.pwd_admin->valid) {
>  			ret = tlmi_opcode_setting("WmiOpcodePasswordAdmin",
>  					tlmi_priv.pwd_admin->password);
> -			if (ret)
> +			if (ret) {
> +				mutex_unlock(&tlmi_mutex);
>  				goto out;
> +			}
>  		}
>  		ret = tlmi_opcode_setting("WmiOpcodePasswordCurrent01", setting->password);
> -		if (ret)
> +		if (ret) {
> +			mutex_unlock(&tlmi_mutex);
>  			goto out;
> +		}
>  		ret = tlmi_opcode_setting("WmiOpcodePasswordNew01", new_pwd);
> -		if (ret)
> +		if (ret) {
> +			mutex_unlock(&tlmi_mutex);
>  			goto out;
> +		}
>  		ret = tlmi_simple_call(LENOVO_OPCODE_IF_GUID, "WmiOpcodePasswordSetUpdate;");
> +		mutex_unlock(&tlmi_mutex);
>  	} else {
>  		/* Format: 'PasswordType,CurrentPw,NewPw,Encoding,KbdLang;' */
>  		auth_str = kasprintf(GFP_KERNEL, "%s,%s,%s,%s,%s;",


I haven't take a really close / good look yet. But at a first glance
I think it would be cleaner to just take the mutex at the top
and unlock it after the out label to which all the existing goto-s
already go ?

> @@ -1000,11 +1011,16 @@ static ssize_t current_value_store(struct kobject *kobj,
>  			goto out;
>  		}
>  
> +		mutex_lock(&tlmi_mutex);
>  		ret = tlmi_simple_call(LENOVO_SET_BIOS_SETTING_CERT_GUID, set_str);
> -		if (ret)
> +		if (ret) {
> +			mutex_unlock(&tlmi_mutex);
>  			goto out;
> +		}
>  		ret = tlmi_simple_call(LENOVO_SAVE_BIOS_SETTING_CERT_GUID,
>  				tlmi_priv.pwd_admin->save_signature);
> +
> +		mutex_unlock(&tlmi_mutex);
>  		if (ret)
>  			goto out;
>  	} else if (tlmi_priv.opcode_support) {
> @@ -1021,18 +1037,23 @@ static ssize_t current_value_store(struct kobject *kobj,
>  			goto out;
>  		}
>  
> +		mutex_lock(&tlmi_mutex);
>  		ret = tlmi_simple_call(LENOVO_SET_BIOS_SETTINGS_GUID, set_str);
> -		if (ret)
> +		if (ret) {
> +			mutex_unlock(&tlmi_mutex);
>  			goto out;
> +		}
>  
>  		if (tlmi_priv.pwd_admin->valid && tlmi_priv.pwd_admin->password[0]) {
>  			ret = tlmi_opcode_setting("WmiOpcodePasswordAdmin",
>  					tlmi_priv.pwd_admin->password);
> -			if (ret)
> +			if (ret) {
> +				mutex_unlock(&tlmi_mutex);
>  				goto out;
> +			}
>  		}
> -
>  		ret = tlmi_save_bios_settings("");
> +		mutex_unlock(&tlmi_mutex);
>  	} else { /* old non opcode based authentication method (deprecated)*/
>  		if (tlmi_priv.pwd_admin->valid && tlmi_priv.pwd_admin->password[0]) {
>  			auth_str = kasprintf(GFP_KERNEL, "%s,%s,%s;",
> @@ -1056,14 +1077,17 @@ static ssize_t current_value_store(struct kobject *kobj,
>  			goto out;
>  		}
>  
> +		mutex_lock(&tlmi_mutex);
>  		ret = tlmi_simple_call(LENOVO_SET_BIOS_SETTINGS_GUID, set_str);
> -		if (ret)
> +		if (ret) {
> +			mutex_unlock(&tlmi_mutex);
>  			goto out;
> -
> +		}
>  		if (auth_str)
>  			ret = tlmi_save_bios_settings(auth_str);
>  		else
>  			ret = tlmi_save_bios_settings("");
> +		mutex_unlock(&tlmi_mutex);
>  	}
>  	if (!ret && !tlmi_priv.pending_changes) {
>  		tlmi_priv.pending_changes = true;

And the same here.

Regards,

Hans
Mark Pearson May 25, 2023, 7:50 p.m. UTC | #2
On Thu, May 25, 2023, at 3:41 PM, Hans de Goede wrote:
> Hi Mark,
>
> On 5/25/23 21:31, Mark Pearson wrote:
>> Add mutex protection around cases where an operation needs multiple
>> WMI calls - e.g. setting password.
>> 
>> Signed-off-by: Mark Pearson <mpearson-lenovo@squebb.ca>
>> ---
>> Changes in V2: New commit added after review of other patches in series.
>> 
>>  drivers/platform/x86/think-lmi.c | 46 ++++++++++++++++++++++++--------
>>  1 file changed, 35 insertions(+), 11 deletions(-)
>> 
>> diff --git a/drivers/platform/x86/think-lmi.c b/drivers/platform/x86/think-lmi.c
>> index 64cd453d6e7d..f3e1e4dacba2 100644
>> --- a/drivers/platform/x86/think-lmi.c
>> +++ b/drivers/platform/x86/think-lmi.c
>> @@ -14,6 +14,7 @@
>>  #include <linux/acpi.h>
>>  #include <linux/errno.h>
>>  #include <linux/fs.h>
>> +#include <linux/mutex.h>
>>  #include <linux/string.h>
>>  #include <linux/types.h>
>>  #include <linux/dmi.h>
>> @@ -195,6 +196,7 @@ static const char * const level_options[] = {
>>  };
>>  static struct think_lmi tlmi_priv;
>>  static struct class *fw_attr_class;
>> +static DEFINE_MUTEX(tlmi_mutex);
>>  
>>  /* ------ Utility functions ------------*/
>>  /* Strip out CR if one is present */
>> @@ -463,23 +465,32 @@ static ssize_t new_password_store(struct kobject *kobj,
>>  			sprintf(pwd_type, "%s", setting->pwd_type);
>>  		}
>>  
>> +		mutex_lock(&tlmi_mutex);
>>  		ret = tlmi_opcode_setting("WmiOpcodePasswordType", pwd_type);
>> -		if (ret)
>> +		if (ret) {
>> +			mutex_unlock(&tlmi_mutex);
>>  			goto out;
>> -
>> +		}
>>  		if (tlmi_priv.pwd_admin->valid) {
>>  			ret = tlmi_opcode_setting("WmiOpcodePasswordAdmin",
>>  					tlmi_priv.pwd_admin->password);
>> -			if (ret)
>> +			if (ret) {
>> +				mutex_unlock(&tlmi_mutex);
>>  				goto out;
>> +			}
>>  		}
>>  		ret = tlmi_opcode_setting("WmiOpcodePasswordCurrent01", setting->password);
>> -		if (ret)
>> +		if (ret) {
>> +			mutex_unlock(&tlmi_mutex);
>>  			goto out;
>> +		}
>>  		ret = tlmi_opcode_setting("WmiOpcodePasswordNew01", new_pwd);
>> -		if (ret)
>> +		if (ret) {
>> +			mutex_unlock(&tlmi_mutex);
>>  			goto out;
>> +		}
>>  		ret = tlmi_simple_call(LENOVO_OPCODE_IF_GUID, "WmiOpcodePasswordSetUpdate;");
>> +		mutex_unlock(&tlmi_mutex);
>>  	} else {
>>  		/* Format: 'PasswordType,CurrentPw,NewPw,Encoding,KbdLang;' */
>>  		auth_str = kasprintf(GFP_KERNEL, "%s,%s,%s,%s,%s;",
>
>
> I haven't take a really close / good look yet. But at a first glance
> I think it would be cleaner to just take the mutex at the top
> and unlock it after the out label to which all the existing goto-s
> already go ?
>
I did consider that - and it was in my first implementation; but then I got concerned
about if the mutex_unlock could potentially get called without mutex_lock having been 
called beforehand. I couldn't find any good reference as to whether that was safe or not.

I ended up deciding that a few extra brackets and unlock calls wasn't that ugly and was 'safer'...and 
so went that route.

Happy to change it - but do you happen to know if it's safe to call unlock without a lock? If it is then
that implementation is cleaner.

Mark
Hans de Goede May 26, 2023, 8:12 a.m. UTC | #3
Hi,

On 5/25/23 21:50, Mark Pearson wrote:
> 
> 
> On Thu, May 25, 2023, at 3:41 PM, Hans de Goede wrote:
>> Hi Mark,
>>
>> On 5/25/23 21:31, Mark Pearson wrote:
>>> Add mutex protection around cases where an operation needs multiple
>>> WMI calls - e.g. setting password.
>>>
>>> Signed-off-by: Mark Pearson <mpearson-lenovo@squebb.ca>
>>> ---
>>> Changes in V2: New commit added after review of other patches in series.
>>>
>>>  drivers/platform/x86/think-lmi.c | 46 ++++++++++++++++++++++++--------
>>>  1 file changed, 35 insertions(+), 11 deletions(-)
>>>
>>> diff --git a/drivers/platform/x86/think-lmi.c b/drivers/platform/x86/think-lmi.c
>>> index 64cd453d6e7d..f3e1e4dacba2 100644
>>> --- a/drivers/platform/x86/think-lmi.c
>>> +++ b/drivers/platform/x86/think-lmi.c
>>> @@ -14,6 +14,7 @@
>>>  #include <linux/acpi.h>
>>>  #include <linux/errno.h>
>>>  #include <linux/fs.h>
>>> +#include <linux/mutex.h>
>>>  #include <linux/string.h>
>>>  #include <linux/types.h>
>>>  #include <linux/dmi.h>
>>> @@ -195,6 +196,7 @@ static const char * const level_options[] = {
>>>  };
>>>  static struct think_lmi tlmi_priv;
>>>  static struct class *fw_attr_class;
>>> +static DEFINE_MUTEX(tlmi_mutex);
>>>  
>>>  /* ------ Utility functions ------------*/
>>>  /* Strip out CR if one is present */
>>> @@ -463,23 +465,32 @@ static ssize_t new_password_store(struct kobject *kobj,
>>>  			sprintf(pwd_type, "%s", setting->pwd_type);
>>>  		}
>>>  
>>> +		mutex_lock(&tlmi_mutex);
>>>  		ret = tlmi_opcode_setting("WmiOpcodePasswordType", pwd_type);
>>> -		if (ret)
>>> +		if (ret) {
>>> +			mutex_unlock(&tlmi_mutex);
>>>  			goto out;
>>> -
>>> +		}
>>>  		if (tlmi_priv.pwd_admin->valid) {
>>>  			ret = tlmi_opcode_setting("WmiOpcodePasswordAdmin",
>>>  					tlmi_priv.pwd_admin->password);
>>> -			if (ret)
>>> +			if (ret) {
>>> +				mutex_unlock(&tlmi_mutex);
>>>  				goto out;
>>> +			}
>>>  		}
>>>  		ret = tlmi_opcode_setting("WmiOpcodePasswordCurrent01", setting->password);
>>> -		if (ret)
>>> +		if (ret) {
>>> +			mutex_unlock(&tlmi_mutex);
>>>  			goto out;
>>> +		}
>>>  		ret = tlmi_opcode_setting("WmiOpcodePasswordNew01", new_pwd);
>>> -		if (ret)
>>> +		if (ret) {
>>> +			mutex_unlock(&tlmi_mutex);
>>>  			goto out;
>>> +		}
>>>  		ret = tlmi_simple_call(LENOVO_OPCODE_IF_GUID, "WmiOpcodePasswordSetUpdate;");
>>> +		mutex_unlock(&tlmi_mutex);
>>>  	} else {
>>>  		/* Format: 'PasswordType,CurrentPw,NewPw,Encoding,KbdLang;' */
>>>  		auth_str = kasprintf(GFP_KERNEL, "%s,%s,%s,%s,%s;",
>>
>>
>> I haven't take a really close / good look yet. But at a first glance
>> I think it would be cleaner to just take the mutex at the top
>> and unlock it after the out label to which all the existing goto-s
>> already go ?
>>
> I did consider that - and it was in my first implementation; but then I got concerned
> about if the mutex_unlock could potentially get called without mutex_lock having been 
> called beforehand. I couldn't find any good reference as to whether that was safe or not.
> 
> I ended up deciding that a few extra brackets and unlock calls wasn't that ugly and was 'safer'...and 
> so went that route.
> 
> Happy to change it - but do you happen to know if it's safe to call unlock without a lock? If it is then
> that implementation is cleaner.

It is not allowed to unlock without a lock. But if you put the lock directly after the malloc for which the out: does the free then there should be no goto out paths which don't have the lock.

E.g. for new_password_store() put it here:

        new_pwd = kstrdup(buf, GFP_KERNEL);
        if (!new_pwd)
                return -ENOMEM;

	mutex_lock(&tlmi_mutex);

	/* Strip out CR if one is present, setting password won't work if it is present */
	...

This does mean also taking the lock in the case where the new password store is done with a single WMI call, but that is not an issue. It makes things a tiny bit slower but WMI calls already are not fast and it is not like we are going to change the password / settings 100-times per second.

And the same thing can be done in current_value_store():

        new_setting = kstrdup(buf, GFP_KERNEL);
        if (!new_setting)
                return -ENOMEM;

	mutex_lock(&tlmi_mutex);

        /* Strip out CR if one is present */
        ...

Regards,

Hans
Mark Pearson May 26, 2023, 2 p.m. UTC | #4
On Fri, May 26, 2023, at 4:12 AM, Hans de Goede wrote:
> Hi,
>
> On 5/25/23 21:50, Mark Pearson wrote:
>> 
>> 
>> On Thu, May 25, 2023, at 3:41 PM, Hans de Goede wrote:
>>> Hi Mark,
>>>
>>> On 5/25/23 21:31, Mark Pearson wrote:
>>>> Add mutex protection around cases where an operation needs multiple
>>>> WMI calls - e.g. setting password.
>>>>
>>>> Signed-off-by: Mark Pearson <mpearson-lenovo@squebb.ca>
>>>> ---
>>>> Changes in V2: New commit added after review of other patches in series.
>>>>
>>>>  drivers/platform/x86/think-lmi.c | 46 ++++++++++++++++++++++++--------
>>>>  1 file changed, 35 insertions(+), 11 deletions(-)
>>>>
>>>> diff --git a/drivers/platform/x86/think-lmi.c b/drivers/platform/x86/think-lmi.c
>>>> index 64cd453d6e7d..f3e1e4dacba2 100644
>>>> --- a/drivers/platform/x86/think-lmi.c
>>>> +++ b/drivers/platform/x86/think-lmi.c
>>>> @@ -14,6 +14,7 @@
>>>>  #include <linux/acpi.h>
>>>>  #include <linux/errno.h>
>>>>  #include <linux/fs.h>
>>>> +#include <linux/mutex.h>
>>>>  #include <linux/string.h>
>>>>  #include <linux/types.h>
>>>>  #include <linux/dmi.h>
>>>> @@ -195,6 +196,7 @@ static const char * const level_options[] = {
>>>>  };
>>>>  static struct think_lmi tlmi_priv;
>>>>  static struct class *fw_attr_class;
>>>> +static DEFINE_MUTEX(tlmi_mutex);
>>>>  
>>>>  /* ------ Utility functions ------------*/
>>>>  /* Strip out CR if one is present */
>>>> @@ -463,23 +465,32 @@ static ssize_t new_password_store(struct kobject *kobj,
>>>>  			sprintf(pwd_type, "%s", setting->pwd_type);
>>>>  		}
>>>>  
>>>> +		mutex_lock(&tlmi_mutex);
>>>>  		ret = tlmi_opcode_setting("WmiOpcodePasswordType", pwd_type);
>>>> -		if (ret)
>>>> +		if (ret) {
>>>> +			mutex_unlock(&tlmi_mutex);
>>>>  			goto out;
>>>> -
>>>> +		}
>>>>  		if (tlmi_priv.pwd_admin->valid) {
>>>>  			ret = tlmi_opcode_setting("WmiOpcodePasswordAdmin",
>>>>  					tlmi_priv.pwd_admin->password);
>>>> -			if (ret)
>>>> +			if (ret) {
>>>> +				mutex_unlock(&tlmi_mutex);
>>>>  				goto out;
>>>> +			}
>>>>  		}
>>>>  		ret = tlmi_opcode_setting("WmiOpcodePasswordCurrent01", setting->password);
>>>> -		if (ret)
>>>> +		if (ret) {
>>>> +			mutex_unlock(&tlmi_mutex);
>>>>  			goto out;
>>>> +		}
>>>>  		ret = tlmi_opcode_setting("WmiOpcodePasswordNew01", new_pwd);
>>>> -		if (ret)
>>>> +		if (ret) {
>>>> +			mutex_unlock(&tlmi_mutex);
>>>>  			goto out;
>>>> +		}
>>>>  		ret = tlmi_simple_call(LENOVO_OPCODE_IF_GUID, "WmiOpcodePasswordSetUpdate;");
>>>> +		mutex_unlock(&tlmi_mutex);
>>>>  	} else {
>>>>  		/* Format: 'PasswordType,CurrentPw,NewPw,Encoding,KbdLang;' */
>>>>  		auth_str = kasprintf(GFP_KERNEL, "%s,%s,%s,%s,%s;",
>>>
>>>
>>> I haven't take a really close / good look yet. But at a first glance
>>> I think it would be cleaner to just take the mutex at the top
>>> and unlock it after the out label to which all the existing goto-s
>>> already go ?
>>>
>> I did consider that - and it was in my first implementation; but then I got concerned
>> about if the mutex_unlock could potentially get called without mutex_lock having been 
>> called beforehand. I couldn't find any good reference as to whether that was safe or not.
>> 
>> I ended up deciding that a few extra brackets and unlock calls wasn't that ugly and was 'safer'...and 
>> so went that route.
>> 
>> Happy to change it - but do you happen to know if it's safe to call unlock without a lock? If it is then
>> that implementation is cleaner.
>
> It is not allowed to unlock without a lock. But if you put the lock 
> directly after the malloc for which the out: does the free then there 
> should be no goto out paths which don't have the lock.
>
> E.g. for new_password_store() put it here:
>
>         new_pwd = kstrdup(buf, GFP_KERNEL);
>         if (!new_pwd)
>                 return -ENOMEM;
>
> 	mutex_lock(&tlmi_mutex);
>
> 	/* Strip out CR if one is present, setting password won't work if it 
> is present */
> 	...
>
> This does mean also taking the lock in the case where the new password 
> store is done with a single WMI call, but that is not an issue. It 
> makes things a tiny bit slower but WMI calls already are not fast and 
> it is not like we are going to change the password / settings 100-times 
> per second.
>
> And the same thing can be done in current_value_store():
>
>         new_setting = kstrdup(buf, GFP_KERNEL);
>         if (!new_setting)
>                 return -ENOMEM;
>
> 	mutex_lock(&tlmi_mutex);
>
>         /* Strip out CR if one is present */
>         ...
>

Yeah - you're right.
For some reason I was trying to do the lock only in the block of code that needed locking...but it makes more sense to do it earlier. I'll update.
Thanks!
Mark
diff mbox series

Patch

diff --git a/drivers/platform/x86/think-lmi.c b/drivers/platform/x86/think-lmi.c
index 64cd453d6e7d..f3e1e4dacba2 100644
--- a/drivers/platform/x86/think-lmi.c
+++ b/drivers/platform/x86/think-lmi.c
@@ -14,6 +14,7 @@ 
 #include <linux/acpi.h>
 #include <linux/errno.h>
 #include <linux/fs.h>
+#include <linux/mutex.h>
 #include <linux/string.h>
 #include <linux/types.h>
 #include <linux/dmi.h>
@@ -195,6 +196,7 @@  static const char * const level_options[] = {
 };
 static struct think_lmi tlmi_priv;
 static struct class *fw_attr_class;
+static DEFINE_MUTEX(tlmi_mutex);
 
 /* ------ Utility functions ------------*/
 /* Strip out CR if one is present */
@@ -463,23 +465,32 @@  static ssize_t new_password_store(struct kobject *kobj,
 			sprintf(pwd_type, "%s", setting->pwd_type);
 		}
 
+		mutex_lock(&tlmi_mutex);
 		ret = tlmi_opcode_setting("WmiOpcodePasswordType", pwd_type);
-		if (ret)
+		if (ret) {
+			mutex_unlock(&tlmi_mutex);
 			goto out;
-
+		}
 		if (tlmi_priv.pwd_admin->valid) {
 			ret = tlmi_opcode_setting("WmiOpcodePasswordAdmin",
 					tlmi_priv.pwd_admin->password);
-			if (ret)
+			if (ret) {
+				mutex_unlock(&tlmi_mutex);
 				goto out;
+			}
 		}
 		ret = tlmi_opcode_setting("WmiOpcodePasswordCurrent01", setting->password);
-		if (ret)
+		if (ret) {
+			mutex_unlock(&tlmi_mutex);
 			goto out;
+		}
 		ret = tlmi_opcode_setting("WmiOpcodePasswordNew01", new_pwd);
-		if (ret)
+		if (ret) {
+			mutex_unlock(&tlmi_mutex);
 			goto out;
+		}
 		ret = tlmi_simple_call(LENOVO_OPCODE_IF_GUID, "WmiOpcodePasswordSetUpdate;");
+		mutex_unlock(&tlmi_mutex);
 	} else {
 		/* Format: 'PasswordType,CurrentPw,NewPw,Encoding,KbdLang;' */
 		auth_str = kasprintf(GFP_KERNEL, "%s,%s,%s,%s,%s;",
@@ -1000,11 +1011,16 @@  static ssize_t current_value_store(struct kobject *kobj,
 			goto out;
 		}
 
+		mutex_lock(&tlmi_mutex);
 		ret = tlmi_simple_call(LENOVO_SET_BIOS_SETTING_CERT_GUID, set_str);
-		if (ret)
+		if (ret) {
+			mutex_unlock(&tlmi_mutex);
 			goto out;
+		}
 		ret = tlmi_simple_call(LENOVO_SAVE_BIOS_SETTING_CERT_GUID,
 				tlmi_priv.pwd_admin->save_signature);
+
+		mutex_unlock(&tlmi_mutex);
 		if (ret)
 			goto out;
 	} else if (tlmi_priv.opcode_support) {
@@ -1021,18 +1037,23 @@  static ssize_t current_value_store(struct kobject *kobj,
 			goto out;
 		}
 
+		mutex_lock(&tlmi_mutex);
 		ret = tlmi_simple_call(LENOVO_SET_BIOS_SETTINGS_GUID, set_str);
-		if (ret)
+		if (ret) {
+			mutex_unlock(&tlmi_mutex);
 			goto out;
+		}
 
 		if (tlmi_priv.pwd_admin->valid && tlmi_priv.pwd_admin->password[0]) {
 			ret = tlmi_opcode_setting("WmiOpcodePasswordAdmin",
 					tlmi_priv.pwd_admin->password);
-			if (ret)
+			if (ret) {
+				mutex_unlock(&tlmi_mutex);
 				goto out;
+			}
 		}
-
 		ret = tlmi_save_bios_settings("");
+		mutex_unlock(&tlmi_mutex);
 	} else { /* old non opcode based authentication method (deprecated)*/
 		if (tlmi_priv.pwd_admin->valid && tlmi_priv.pwd_admin->password[0]) {
 			auth_str = kasprintf(GFP_KERNEL, "%s,%s,%s;",
@@ -1056,14 +1077,17 @@  static ssize_t current_value_store(struct kobject *kobj,
 			goto out;
 		}
 
+		mutex_lock(&tlmi_mutex);
 		ret = tlmi_simple_call(LENOVO_SET_BIOS_SETTINGS_GUID, set_str);
-		if (ret)
+		if (ret) {
+			mutex_unlock(&tlmi_mutex);
 			goto out;
-
+		}
 		if (auth_str)
 			ret = tlmi_save_bios_settings(auth_str);
 		else
 			ret = tlmi_save_bios_settings("");
+		mutex_unlock(&tlmi_mutex);
 	}
 	if (!ret && !tlmi_priv.pending_changes) {
 		tlmi_priv.pending_changes = true;