lkml.org 
[lkml]   [2015]   [Feb]   [9]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    From
    Subject[PATCH 3.18 33/39] ALSA: ak411x: Fix stall in work callback
    Date
    3.18-stable review patch.  If anyone has any objections, please let me know.

    ------------------

    From: Takashi Iwai <tiwai@suse.de>

    commit 4161b4505f1690358ac0a9ee59845a7887336b21 upstream.

    When ak4114 work calls its callback and the callback invokes
    ak4114_reinit(), it stalls due to flush_delayed_work(). For avoiding
    this, control the reentrance by introducing a refcount. Also
    flush_delayed_work() is replaced with cancel_delayed_work_sync().

    The exactly same bug is present in ak4113.c and fixed as well.

    Reported-by: Pavel Hofman <pavel.hofman@ivitera.com>
    Acked-by: Jaroslav Kysela <perex@perex.cz>
    Tested-by: Pavel Hofman <pavel.hofman@ivitera.com>
    Signed-off-by: Takashi Iwai <tiwai@suse.de>
    Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>

    ---
    include/sound/ak4113.h | 2 +-
    include/sound/ak4114.h | 2 +-
    sound/i2c/other/ak4113.c | 17 ++++++++---------
    sound/i2c/other/ak4114.c | 18 ++++++++----------
    4 files changed, 18 insertions(+), 21 deletions(-)

    --- a/include/sound/ak4113.h
    +++ b/include/sound/ak4113.h
    @@ -286,7 +286,7 @@ struct ak4113 {
    ak4113_write_t *write;
    ak4113_read_t *read;
    void *private_data;
    - unsigned int init:1;
    + atomic_t wq_processing;
    spinlock_t lock;
    unsigned char regmap[AK4113_WRITABLE_REGS];
    struct snd_kcontrol *kctls[AK4113_CONTROLS];
    --- a/include/sound/ak4114.h
    +++ b/include/sound/ak4114.h
    @@ -168,7 +168,7 @@ struct ak4114 {
    ak4114_write_t * write;
    ak4114_read_t * read;
    void * private_data;
    - unsigned int init: 1;
    + atomic_t wq_processing;
    spinlock_t lock;
    unsigned char regmap[6];
    unsigned char txcsb[5];
    --- a/sound/i2c/other/ak4113.c
    +++ b/sound/i2c/other/ak4113.c
    @@ -56,8 +56,7 @@ static inline unsigned char reg_read(str

    static void snd_ak4113_free(struct ak4113 *chip)
    {
    - chip->init = 1; /* don't schedule new work */
    - mb();
    + atomic_inc(&chip->wq_processing); /* don't schedule new work */
    cancel_delayed_work_sync(&chip->work);
    kfree(chip);
    }
    @@ -89,6 +88,7 @@ int snd_ak4113_create(struct snd_card *c
    chip->write = write;
    chip->private_data = private_data;
    INIT_DELAYED_WORK(&chip->work, ak4113_stats);
    + atomic_set(&chip->wq_processing, 0);

    for (reg = 0; reg < AK4113_WRITABLE_REGS ; reg++)
    chip->regmap[reg] = pgm[reg];
    @@ -139,13 +139,11 @@ static void ak4113_init_regs(struct ak41

    void snd_ak4113_reinit(struct ak4113 *chip)
    {
    - chip->init = 1;
    - mb();
    - flush_delayed_work(&chip->work);
    + if (atomic_inc_return(&chip->wq_processing) == 1)
    + cancel_delayed_work_sync(&chip->work);
    ak4113_init_regs(chip);
    /* bring up statistics / event queing */
    - chip->init = 0;
    - if (chip->kctls[0])
    + if (atomic_dec_and_test(&chip->wq_processing))
    schedule_delayed_work(&chip->work, HZ / 10);
    }
    EXPORT_SYMBOL_GPL(snd_ak4113_reinit);
    @@ -632,8 +630,9 @@ static void ak4113_stats(struct work_str
    {
    struct ak4113 *chip = container_of(work, struct ak4113, work.work);

    - if (!chip->init)
    + if (atomic_inc_return(&chip->wq_processing) == 1)
    snd_ak4113_check_rate_and_errors(chip, chip->check_flags);

    - schedule_delayed_work(&chip->work, HZ / 10);
    + if (atomic_dec_and_test(&chip->wq_processing))
    + schedule_delayed_work(&chip->work, HZ / 10);
    }
    --- a/sound/i2c/other/ak4114.c
    +++ b/sound/i2c/other/ak4114.c
    @@ -66,8 +66,7 @@ static void reg_dump(struct ak4114 *ak41

    static void snd_ak4114_free(struct ak4114 *chip)
    {
    - chip->init = 1; /* don't schedule new work */
    - mb();
    + atomic_inc(&chip->wq_processing); /* don't schedule new work */
    cancel_delayed_work_sync(&chip->work);
    kfree(chip);
    }
    @@ -100,6 +99,7 @@ int snd_ak4114_create(struct snd_card *c
    chip->write = write;
    chip->private_data = private_data;
    INIT_DELAYED_WORK(&chip->work, ak4114_stats);
    + atomic_set(&chip->wq_processing, 0);

    for (reg = 0; reg < 6; reg++)
    chip->regmap[reg] = pgm[reg];
    @@ -152,13 +152,11 @@ static void ak4114_init_regs(struct ak41

    void snd_ak4114_reinit(struct ak4114 *chip)
    {
    - chip->init = 1;
    - mb();
    - flush_delayed_work(&chip->work);
    + if (atomic_inc_return(&chip->wq_processing) == 1)
    + cancel_delayed_work_sync(&chip->work);
    ak4114_init_regs(chip);
    /* bring up statistics / event queing */
    - chip->init = 0;
    - if (chip->kctls[0])
    + if (atomic_dec_and_test(&chip->wq_processing))
    schedule_delayed_work(&chip->work, HZ / 10);
    }

    @@ -612,10 +610,10 @@ static void ak4114_stats(struct work_str
    {
    struct ak4114 *chip = container_of(work, struct ak4114, work.work);

    - if (!chip->init)
    + if (atomic_inc_return(&chip->wq_processing) == 1)
    snd_ak4114_check_rate_and_errors(chip, chip->check_flags);
    -
    - schedule_delayed_work(&chip->work, HZ / 10);
    + if (atomic_dec_and_test(&chip->wq_processing))
    + schedule_delayed_work(&chip->work, HZ / 10);
    }

    EXPORT_SYMBOL(snd_ak4114_create);



    \
     
     \ /
      Last update: 2015-02-09 09:41    [W:2.746 / U:1.584 seconds]
    ©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site