Return-Path: Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1753247AbcKILr2 (ORCPT ); Wed, 9 Nov 2016 06:47:28 -0500 Received: from mailout4.w1.samsung.com ([210.118.77.14]:61951 "EHLO mailout4.w1.samsung.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1750792AbcKILrZ (ORCPT ); Wed, 9 Nov 2016 06:47:25 -0500 X-AuditID: cbfec7f1-f79f46d0000008eb-b0-58230cc7b1be Subject: Re: [PATCH v2] led: core: Use atomic bit-field for the blink-flags To: linux-leds@vger.kernel.org Cc: linux-kernel@vger.kernel.org, Hans de Goede From: Jacek Anaszewski Message-id: Date: Wed, 09 Nov 2016 12:47:18 +0100 User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:45.0) Gecko/20100101 Thunderbird/45.2.0 MIME-version: 1.0 In-reply-to: <1478613535-32564-1-git-send-email-j.anaszewski@samsung.com> Content-type: text/plain; charset=windows-1252; format=flowed Content-transfer-encoding: 7bit X-Brightmail-Tracker: H4sIAAAAAAAAA+NgFnrHIsWRmVeSWpSXmKPExsWy7djPc7rHeZQjDD58NbN4c3w6k8XlXXPY LLa+WcfowOzxft9VNo/Pm+QCmKK4bFJSczLLUov07RK4Mi51fGQu2OdUceDXUfYGxulmXYyc HBICJhItbxsYIWwxiQv31rN1MXJxCAksZZRo236BFcL5zCjxcMkTdpiOWROeMUMkljFKtJ44 D1X1jFFi4rd1YFXCAt4SV592Ac3l4BARkJPYeaYSJMws4Crxvm812Do2AUOJny9eM4HYvAJ2 EievrAOzWQRUJXZM2cIG0ioqECGx+24qRImgxI/J91hAbE4BT4mFDz4xQYx0lHiwaCcrhC0v sXnNW7DbJATes0l0zf3FDjJHQkBWYtMBZoj7XSRu3L3OBmELS7w6vgXqLxmJzo6DTBC9kxkl Lh67yQrhrGaU2NjZyQJRZS3R8P8XC8Q2PolJ26YzQyzglehoE4Io8ZDY9mQJVLmjxN2Fi1kg 4TMHGKQ73zJPYJSfheShWUiemIXkiQWMzKsYRVJLi3PTU4uN9IoTc4tL89L1kvNzNzECU8Hp f8c/7mB8f8LqEKMAB6MSD2/GQ8UIIdbEsuLK3EOMEhzMSiK8sdzKEUK8KYmVValF+fFFpTmp xYcYpTlYlMR59yy4Ei4kkJ5YkpqdmlqQWgSTZeLglGpgVDNm6hTlTyvWYdWazDQv8nidnPF6 ES+utI8MHc8K6hi8NJ7+/aT5v8fk2b0E770M04/092wrFxFrnpJqxnzlzMbyc4uuJ0sevj95 i9vSVaEVK61n7qqeVtm37vHqMImub0Gr2vKbrisoBnxnORuuKiB3a9GhPi6XXnvxq7+n7OsU WHbv1+GDSizFGYmGWsxFxYkAuIjkGAEDAAA= X-Brightmail-Tracker: H4sIAAAAAAAAA+NgFuplkeLIzCtJLcpLzFFi42I5/e/4Vd3jPMoRBk3TJCzeHJ/OZHF51xw2 i61v1jE6MHu833eVzePzJrkApig3m4zUxJTUIoXUvOT8lMy8dFul0BA3XQslhbzE3FRbpQhd 35AgJYWyxJxSIM/IAA04OAe4Byvp2yW4ZVzq+MhcsM+p4sCvo+wNjNPNuhg5OSQETCRmTXjG DGGLSVy4t56ti5GLQ0hgCaPExinrGEESQgLPGCV29FqC2MIC3hJXn3YBxTk4RATkJHaeqYSo n8co8a5tFRNIDbOAq8T7vtVgvWwChhI/X7wGi/MK2EmcvLIOzGYRUJXYMWULG4gtKhAhcWvV R0aIGkGJH5PvsYDYnAKeEgsffIKaaSux4P06FghbXmLzmrfMExgFZiFpmYWkbBaSsgWMzKsY RVJLi3PTc4sN9YoTc4tL89L1kvNzNzECo2LbsZ+bdzBe2hh8iFGAg1GJhzfjoWKEEGtiWXFl 7iFGCQ5mJRHeWG7lCCHelMTKqtSi/Pii0pzU4kOMpkBPTGSWEk3OB0ZsXkm8oYmhuaWhkbGF hbmRkZI4b8mHK+FCAumJJanZqakFqUUwfUwcnFINjHNVl4kt3LzM79NF9Xes78/YzX++O+6t f3tabLXxwa/cj9Z6LVmu2HfrR0KG9/Oyxkcev87/aCjo4jLcHhUVE79PW7dHKNbQ+kSa6YH2 TM4g46esng7G7scOTVqQfMQra5bwdO2zjocfKH6ynOLwZ5X4sTXl2Q9vvS5JZ9edmRjU+/fA Ka9fKUosxRmJhlrMRcWJAJCFGNugAgAA X-MTR: 20000000000000000@CPGS X-CMS-MailID: 20161109114719eucas1p2bd01846e323d55794c0bf46dd47f8122 X-Msg-Generator: CA X-Sender-IP: 182.198.249.179 X-Local-Sender: =?UTF-8?B?SmFjZWsgQW5hc3pld3NraRtTUlBPTC1TeXN0ZW0gRlcgIChN?= =?UTF-8?B?Qikb7IK87ISx7KCE7J6QG1NlbmlvciBTb2Z0d2FyZSBFbmdpbmVlcg==?= X-Global-Sender: =?UTF-8?B?SmFjZWsgQW5hc3pld3NraRtTUlBPTC1TeXN0ZW0gRlcgIChN?= =?UTF-8?B?QikbU2Ftc3VuZyBFbGVjdHJvbmljcxtTZW5pb3IgU29mdHdhcmUgRW5naW5l?= =?UTF-8?B?ZXI=?= X-Sender-Code: =?UTF-8?B?QzEwG0VIURtDMTBDRDAyQ0QwMjc1MjY=?= CMS-TYPE: 201P X-HopCount: 7 X-CMS-RootMailID: 20161108140208epcas2p20b5c081dd491ccf39108031f6a7e03a3 X-RootMTR: 20161108140208epcas2p20b5c081dd491ccf39108031f6a7e03a3 References: <1478613535-32564-1-git-send-email-j.anaszewski@samsung.com> Sender: linux-kernel-owner@vger.kernel.org List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Length: 8280 Lines: 230 Since we've already agreed on keeping the flags in the leds.h, then I'm applying this patch to the for-next branch of linux-leds.git. Thanks, Jacek Anaszewski On 11/08/2016 02:58 PM, Jacek Anaszewski wrote: > From: Hans de Goede > > All the LED_BLINK* flags are accessed read-modify-write from e.g. > led_set_brightness and led_blink_set_oneshot while both > set_brightness_work and the blink_timer may be running. > > If these race then the modify step done by one of them may be lost, > switch the LED_BLINK* flags to a new atomic work_flags bit-field > to avoid this race. > > Signed-off-by: Hans de Goede > Signed-off-by: Jacek Anaszewski > --- > Changes since v1: > - keep set_brightness_work in linux/leds.h > > drivers/leds/led-class.c | 1 + > drivers/leds/led-core.c | 52 +++++++++++++++++++++++++----------------------- > include/linux/leds.h | 24 ++++++++++++---------- > 3 files changed, 42 insertions(+), 35 deletions(-) > > diff --git a/drivers/leds/led-class.c b/drivers/leds/led-class.c > index c8d2d67..b12f861 100644 > --- a/drivers/leds/led-class.c > +++ b/drivers/leds/led-class.c > @@ -212,6 +212,7 @@ int led_classdev_register(struct device *parent, struct led_classdev *led_cdev) > return -ENODEV; > } > > + led_cdev->work_flags = 0; > #ifdef CONFIG_LEDS_TRIGGERS > init_rwsem(&led_cdev->trigger_lock); > #endif > diff --git a/drivers/leds/led-core.c b/drivers/leds/led-core.c > index e2e5cc7..620257a 100644 > --- a/drivers/leds/led-core.c > +++ b/drivers/leds/led-core.c > @@ -61,12 +61,13 @@ static void led_timer_function(unsigned long data) > > if (!led_cdev->blink_delay_on || !led_cdev->blink_delay_off) { > led_set_brightness_nosleep(led_cdev, LED_OFF); > - led_cdev->flags &= ~LED_BLINK_SW; > + clear_bit(LED_BLINK_SW, &led_cdev->work_flags); > return; > } > > - if (led_cdev->flags & LED_BLINK_ONESHOT_STOP) { > - led_cdev->flags &= ~(LED_BLINK_ONESHOT_STOP | LED_BLINK_SW); > + if (test_and_clear_bit(LED_BLINK_ONESHOT_STOP, > + &led_cdev->work_flags)) { > + clear_bit(LED_BLINK_SW, &led_cdev->work_flags); > return; > } > > @@ -81,10 +82,9 @@ static void led_timer_function(unsigned long data) > * Do it only if there is no pending blink brightness > * change, to avoid overwriting the new value. > */ > - if (!(led_cdev->flags & LED_BLINK_BRIGHTNESS_CHANGE)) > + if (!test_and_clear_bit(LED_BLINK_BRIGHTNESS_CHANGE, > + &led_cdev->work_flags)) > led_cdev->blink_brightness = brightness; > - else > - led_cdev->flags &= ~LED_BLINK_BRIGHTNESS_CHANGE; > brightness = LED_OFF; > delay = led_cdev->blink_delay_off; > } > @@ -95,13 +95,15 @@ static void led_timer_function(unsigned long data) > * the final blink state so that the led is toggled each delay_on + > * delay_off milliseconds in worst case. > */ > - if (led_cdev->flags & LED_BLINK_ONESHOT) { > - if (led_cdev->flags & LED_BLINK_INVERT) { > + if (test_bit(LED_BLINK_ONESHOT, &led_cdev->work_flags)) { > + if (test_bit(LED_BLINK_INVERT, &led_cdev->work_flags)) { > if (brightness) > - led_cdev->flags |= LED_BLINK_ONESHOT_STOP; > + set_bit(LED_BLINK_ONESHOT_STOP, > + &led_cdev->work_flags); > } else { > if (!brightness) > - led_cdev->flags |= LED_BLINK_ONESHOT_STOP; > + set_bit(LED_BLINK_ONESHOT_STOP, > + &led_cdev->work_flags); > } > } > > @@ -114,10 +116,9 @@ static void set_brightness_delayed(struct work_struct *ws) > container_of(ws, struct led_classdev, set_brightness_work); > int ret = 0; > > - if (led_cdev->flags & LED_BLINK_DISABLE) { > + if (test_and_clear_bit(LED_BLINK_DISABLE, &led_cdev->work_flags)) { > led_cdev->delayed_set_value = LED_OFF; > led_stop_software_blink(led_cdev); > - led_cdev->flags &= ~LED_BLINK_DISABLE; > } > > ret = __led_set_brightness(led_cdev, led_cdev->delayed_set_value); > @@ -160,7 +161,7 @@ static void led_set_software_blink(struct led_classdev *led_cdev, > return; > } > > - led_cdev->flags |= LED_BLINK_SW; > + set_bit(LED_BLINK_SW, &led_cdev->work_flags); > mod_timer(&led_cdev->blink_timer, jiffies + 1); > } > > @@ -169,7 +170,7 @@ static void led_blink_setup(struct led_classdev *led_cdev, > unsigned long *delay_on, > unsigned long *delay_off) > { > - if (!(led_cdev->flags & LED_BLINK_ONESHOT) && > + if (!test_bit(LED_BLINK_ONESHOT, &led_cdev->work_flags) && > led_cdev->blink_set && > !led_cdev->blink_set(led_cdev, delay_on, delay_off)) > return; > @@ -196,8 +197,8 @@ void led_blink_set(struct led_classdev *led_cdev, > { > del_timer_sync(&led_cdev->blink_timer); > > - led_cdev->flags &= ~LED_BLINK_ONESHOT; > - led_cdev->flags &= ~LED_BLINK_ONESHOT_STOP; > + clear_bit(LED_BLINK_ONESHOT, &led_cdev->work_flags); > + clear_bit(LED_BLINK_ONESHOT_STOP, &led_cdev->work_flags); > > led_blink_setup(led_cdev, delay_on, delay_off); > } > @@ -208,17 +209,17 @@ void led_blink_set_oneshot(struct led_classdev *led_cdev, > unsigned long *delay_off, > int invert) > { > - if ((led_cdev->flags & LED_BLINK_ONESHOT) && > + if (test_bit(LED_BLINK_ONESHOT, &led_cdev->work_flags) && > timer_pending(&led_cdev->blink_timer)) > return; > > - led_cdev->flags |= LED_BLINK_ONESHOT; > - led_cdev->flags &= ~LED_BLINK_ONESHOT_STOP; > + set_bit(LED_BLINK_ONESHOT, &led_cdev->work_flags); > + clear_bit(LED_BLINK_ONESHOT_STOP, &led_cdev->work_flags); > > if (invert) > - led_cdev->flags |= LED_BLINK_INVERT; > + set_bit(LED_BLINK_INVERT, &led_cdev->work_flags); > else > - led_cdev->flags &= ~LED_BLINK_INVERT; > + clear_bit(LED_BLINK_INVERT, &led_cdev->work_flags); > > led_blink_setup(led_cdev, delay_on, delay_off); > } > @@ -229,7 +230,7 @@ void led_stop_software_blink(struct led_classdev *led_cdev) > del_timer_sync(&led_cdev->blink_timer); > led_cdev->blink_delay_on = 0; > led_cdev->blink_delay_off = 0; > - led_cdev->flags &= ~LED_BLINK_SW; > + clear_bit(LED_BLINK_SW, &led_cdev->work_flags); > } > EXPORT_SYMBOL_GPL(led_stop_software_blink); > > @@ -240,17 +241,18 @@ void led_set_brightness(struct led_classdev *led_cdev, > * If software blink is active, delay brightness setting > * until the next timer tick. > */ > - if (led_cdev->flags & LED_BLINK_SW) { > + if (test_bit(LED_BLINK_SW, &led_cdev->work_flags)) { > /* > * If we need to disable soft blinking delegate this to the > * work queue task to avoid problems in case we are called > * from hard irq context. > */ > if (brightness == LED_OFF) { > - led_cdev->flags |= LED_BLINK_DISABLE; > + set_bit(LED_BLINK_DISABLE, &led_cdev->work_flags); > schedule_work(&led_cdev->set_brightness_work); > } else { > - led_cdev->flags |= LED_BLINK_BRIGHTNESS_CHANGE; > + set_bit(LED_BLINK_BRIGHTNESS_CHANGE, > + &led_cdev->work_flags); > led_cdev->blink_brightness = brightness; > } > return; > diff --git a/include/linux/leds.h b/include/linux/leds.h > index eebcd8c..1dc69df 100644 > --- a/include/linux/leds.h > +++ b/include/linux/leds.h > @@ -43,16 +43,20 @@ struct led_classdev { > #define LED_UNREGISTERING (1 << 1) > /* Upper 16 bits reflect control information */ > #define LED_CORE_SUSPENDRESUME (1 << 16) > -#define LED_BLINK_SW (1 << 17) > -#define LED_BLINK_ONESHOT (1 << 18) > -#define LED_BLINK_ONESHOT_STOP (1 << 19) > -#define LED_BLINK_INVERT (1 << 20) > -#define LED_BLINK_BRIGHTNESS_CHANGE (1 << 21) > -#define LED_BLINK_DISABLE (1 << 22) > -#define LED_SYSFS_DISABLE (1 << 23) > -#define LED_DEV_CAP_FLASH (1 << 24) > -#define LED_HW_PLUGGABLE (1 << 25) > -#define LED_PANIC_INDICATOR (1 << 26) > +#define LED_SYSFS_DISABLE (1 << 17) > +#define LED_DEV_CAP_FLASH (1 << 18) > +#define LED_HW_PLUGGABLE (1 << 19) > +#define LED_PANIC_INDICATOR (1 << 20) > + > + /* set_brightness_work / blink_timer flags, atomic, private. */ > + unsigned long work_flags; > + > +#define LED_BLINK_SW 0 > +#define LED_BLINK_ONESHOT 1 > +#define LED_BLINK_ONESHOT_STOP 2 > +#define LED_BLINK_INVERT 3 > +#define LED_BLINK_BRIGHTNESS_CHANGE 4 > +#define LED_BLINK_DISABLE 5 > > /* Set LED brightness level > * Must not sleep. Use brightness_set_blocking for drivers > -- Best regards, Jacek Anaszewski