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@xxxxxxxxx> >>>> --- >>>> 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