lkml.org 
[lkml]   [2021]   [May]   [20]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    From
    Subject[PATCH 4.4 058/190] misc: lis3lv02d: Fix false-positive WARN on various HP models
    Date
    From: Hans de Goede <hdegoede@redhat.com>

    commit 3641762c1c9c7cfd84a7061a0a73054f09b412e3 upstream.

    Before this commit lis3lv02d_get_pwron_wait() had a WARN_ONCE() to catch
    a potential divide by 0. WARN macros should only be used to catch internal
    kernel bugs and that is not the case here. We have been receiving a lot of
    bug reports about kernel backtraces caused by this WARN.

    The div value being checked comes from the lis3->odrs[] array. Which
    is sized to be a power-of-2 matching the number of bits in lis3->odr_mask.

    The only lis3 model where this array is not entirely filled with non zero
    values. IOW the only model where we can hit the div == 0 check is the
    3dc ("8 bits 3DC sensor") model:

    int lis3_3dc_rates[16] = {0, 1, 10, 25, 50, 100, 200, 400, 1600, 5000};

    Note the 0 value at index 0, according to the datasheet an odr index of 0
    means "Power-down mode". HP typically uses a lis3 accelerometer for HDD
    fall protection. What I believe is happening here is that on newer
    HP devices, which only contain a SDD, the BIOS is leaving the lis3 device
    powered-down since it is not used for HDD fall protection.

    Note that the lis3_3dc_rates array initializer only specifies 10 values,
    which matches the datasheet. So it also contains 6 zero values at the end.

    Replace the WARN with a normal check, which treats an odr index of 0
    as power-down and uses a normal dev_err() to report the error in case
    odr index point past the initialized part of the array.

    Fixes: 1510dd5954be ("lis3lv02d: avoid divide by zero due to unchecked")
    Cc: stable@vger.kernel.org
    Signed-off-by: Hans de Goede <hdegoede@redhat.com>
    BugLink: https://bugzilla.redhat.com/show_bug.cgi?id=785814
    BugLink: https://bugzilla.redhat.com/show_bug.cgi?id=1817027
    BugLink: https://bugs.centos.org/view.php?id=10720
    Link: https://lore.kernel.org/r/20210217102501.31758-1-hdegoede@redhat.com
    Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
    Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
    ---
    drivers/misc/lis3lv02d/lis3lv02d.c | 21 ++++++++++++++++-----
    1 file changed, 16 insertions(+), 5 deletions(-)

    --- a/drivers/misc/lis3lv02d/lis3lv02d.c
    +++ b/drivers/misc/lis3lv02d/lis3lv02d.c
    @@ -220,7 +220,7 @@ static int lis3_3dc_rates[16] = {0, 1, 1
    static int lis3_3dlh_rates[4] = {50, 100, 400, 1000};

    /* ODR is Output Data Rate */
    -static int lis3lv02d_get_odr(struct lis3lv02d *lis3)
    +static int lis3lv02d_get_odr_index(struct lis3lv02d *lis3)
    {
    u8 ctrl;
    int shift;
    @@ -228,15 +228,23 @@ static int lis3lv02d_get_odr(struct lis3
    lis3->read(lis3, CTRL_REG1, &ctrl);
    ctrl &= lis3->odr_mask;
    shift = ffs(lis3->odr_mask) - 1;
    - return lis3->odrs[(ctrl >> shift)];
    + return (ctrl >> shift);
    }

    static int lis3lv02d_get_pwron_wait(struct lis3lv02d *lis3)
    {
    - int div = lis3lv02d_get_odr(lis3);
    + int odr_idx = lis3lv02d_get_odr_index(lis3);
    + int div = lis3->odrs[odr_idx];

    - if (WARN_ONCE(div == 0, "device returned spurious data"))
    + if (div == 0) {
    + if (odr_idx == 0) {
    + /* Power-down mode, not sampling no need to sleep */
    + return 0;
    + }
    +
    + dev_err(&lis3->pdev->dev, "Error unknown odrs-index: %d\n", odr_idx);
    return -ENXIO;
    + }

    /* LIS3 power on delay is quite long */
    msleep(lis3->pwron_delay / div);
    @@ -819,9 +827,12 @@ static ssize_t lis3lv02d_rate_show(struc
    struct device_attribute *attr, char *buf)
    {
    struct lis3lv02d *lis3 = dev_get_drvdata(dev);
    + int odr_idx;

    lis3lv02d_sysfs_poweron(lis3);
    - return sprintf(buf, "%d\n", lis3lv02d_get_odr(lis3));
    +
    + odr_idx = lis3lv02d_get_odr_index(lis3);
    + return sprintf(buf, "%d\n", lis3->odrs[odr_idx]);
    }

    static ssize_t lis3lv02d_rate_set(struct device *dev,

    \
     
     \ /
      Last update: 2021-05-20 13:35    [W:3.384 / U:0.048 seconds]
    ©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site