diff options
author | Willy Tarreau <w@1wt.eu> | 2017-09-07 09:37:30 -0400 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2017-09-18 10:06:00 -0400 |
commit | 93dc1774d2a4c7a298d5cdf78cc8acdcb7b1428d (patch) | |
tree | 32e7bcd21dfd863d3005e730ded8f1123519faf7 | |
parent | d53bebdf4d779497b29e1aad26e19cac1d446f42 (diff) |
auxdisplay: charlcd: properly restore atomic counter on error path
Commit f4757af ("staging: panel: Fix single-open policy race condition")
introduced in 3.19-rc1 attempted to fix a race condition on the open, but
failed to properly do it and used to exit without restoring the semaphore.
This results in -EBUSY being returned after the first open error until
the module is reloaded or the system restarted (ie: consecutive to a
dual open resulting in -EBUSY or to a permission error).
[ Note for stable maintainers: the code moved from drivers/misc/panel.c
to drivers/auxdisplay/{charlcd,panel}.c during 4.12. The patch easily
applies there (modulo the renamed atomic counter) but I can provide a
tested backport if desired. ]
Fixes: f4757af85 # 3.19-rc1
Cc: stable@vger.kernel.org
Cc: Mariusz Gorski <marius.gorski@gmail.com>
Cc: Geert Uytterhoeven <geert@linux-m68k.org>
Cc: Miguel Ojeda Sandonis <miguel.ojeda.sandonis@gmail.com>
Signed-off-by: Willy Tarreau <w@1wt.eu>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
-rw-r--r-- | drivers/auxdisplay/charlcd.c | 11 | ||||
-rw-r--r-- | drivers/auxdisplay/panel.c | 11 |
2 files changed, 18 insertions, 4 deletions
diff --git a/drivers/auxdisplay/charlcd.c b/drivers/auxdisplay/charlcd.c index cfeb049a01ef..642afd88870b 100644 --- a/drivers/auxdisplay/charlcd.c +++ b/drivers/auxdisplay/charlcd.c | |||
@@ -647,18 +647,25 @@ static ssize_t charlcd_write(struct file *file, const char __user *buf, | |||
647 | static int charlcd_open(struct inode *inode, struct file *file) | 647 | static int charlcd_open(struct inode *inode, struct file *file) |
648 | { | 648 | { |
649 | struct charlcd_priv *priv = to_priv(the_charlcd); | 649 | struct charlcd_priv *priv = to_priv(the_charlcd); |
650 | int ret; | ||
650 | 651 | ||
652 | ret = -EBUSY; | ||
651 | if (!atomic_dec_and_test(&charlcd_available)) | 653 | if (!atomic_dec_and_test(&charlcd_available)) |
652 | return -EBUSY; /* open only once at a time */ | 654 | goto fail; /* open only once at a time */ |
653 | 655 | ||
656 | ret = -EPERM; | ||
654 | if (file->f_mode & FMODE_READ) /* device is write-only */ | 657 | if (file->f_mode & FMODE_READ) /* device is write-only */ |
655 | return -EPERM; | 658 | goto fail; |
656 | 659 | ||
657 | if (priv->must_clear) { | 660 | if (priv->must_clear) { |
658 | charlcd_clear_display(&priv->lcd); | 661 | charlcd_clear_display(&priv->lcd); |
659 | priv->must_clear = false; | 662 | priv->must_clear = false; |
660 | } | 663 | } |
661 | return nonseekable_open(inode, file); | 664 | return nonseekable_open(inode, file); |
665 | |||
666 | fail: | ||
667 | atomic_inc(&charlcd_available); | ||
668 | return ret; | ||
662 | } | 669 | } |
663 | 670 | ||
664 | static int charlcd_release(struct inode *inode, struct file *file) | 671 | static int charlcd_release(struct inode *inode, struct file *file) |
diff --git a/drivers/auxdisplay/panel.c b/drivers/auxdisplay/panel.c index df126dcdaf18..6911acd896d9 100644 --- a/drivers/auxdisplay/panel.c +++ b/drivers/auxdisplay/panel.c | |||
@@ -1105,14 +1105,21 @@ static ssize_t keypad_read(struct file *file, | |||
1105 | 1105 | ||
1106 | static int keypad_open(struct inode *inode, struct file *file) | 1106 | static int keypad_open(struct inode *inode, struct file *file) |
1107 | { | 1107 | { |
1108 | int ret; | ||
1109 | |||
1110 | ret = -EBUSY; | ||
1108 | if (!atomic_dec_and_test(&keypad_available)) | 1111 | if (!atomic_dec_and_test(&keypad_available)) |
1109 | return -EBUSY; /* open only once at a time */ | 1112 | goto fail; /* open only once at a time */ |
1110 | 1113 | ||
1114 | ret = -EPERM; | ||
1111 | if (file->f_mode & FMODE_WRITE) /* device is read-only */ | 1115 | if (file->f_mode & FMODE_WRITE) /* device is read-only */ |
1112 | return -EPERM; | 1116 | goto fail; |
1113 | 1117 | ||
1114 | keypad_buflen = 0; /* flush the buffer on opening */ | 1118 | keypad_buflen = 0; /* flush the buffer on opening */ |
1115 | return 0; | 1119 | return 0; |
1120 | fail: | ||
1121 | atomic_inc(&keypad_available); | ||
1122 | return ret; | ||
1116 | } | 1123 | } |
1117 | 1124 | ||
1118 | static int keypad_release(struct inode *inode, struct file *file) | 1125 | static int keypad_release(struct inode *inode, struct file *file) |