Re: [PATCH v1 05/10] bcache: stop dc->writeback_rate_update if cache set is stopping

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On 04/01/2018 12:47 AM, tang.junhui@xxxxxxxxxx wrote:
> From: Tang Junhui <tang.junhui@xxxxxxxxxx>
> 
> Hello Coly,
> 
>> dc->writeback_rate_update is a special delayed worker, it re-arms itself
>> to run after several seconds by,
>>>>     schedule_delayed_work(&dc->writeback_rate_update,
>>>>                   dc->writeback_rate_update_seconds * HZ);
>>
>> I check the workqueue code, it seems cancel_delayed_work_sync() does not
>> prevent a delayed work to re-arm itself inside worker routine. And in my
>> test, around 5 seconds after cancel_delayed_work_sync() called, a NULL
>> pointer difference oops happens. 
> I think I got how this issue would be occured:
> 1)work writeback_rate_update is running;
> 2)cancel_delayed_work_sync() is called, and waiting for work 
>   writeback_rate_update to run over, and conntinue to release cche set 
>   and cached device resources.
> 3)In the end of step 1, work writeback_rate_update re-armed again, and
>   5s later, the work runs again.
> 
>> Cache set memory is freed within 2 seconds
>> after __cache_set_unregister() called, so inside
>> __update_writeback_rate() struct cache_set is referenced and causes the
>> NULL pointer deference bug.
>>
> So, if it occured like I said above, it is not safty to judged by:
>>    struct cache_set *c = dc->disk.c;
>>    if (test_bit(CACHE_SET_STOPPING, &c->flags))
>>        return;
> because cache_set, even bcache device and cache device are all released.                                             
> Is that right?

Hi Junhui,

It is about an implicit and interesting ordering, a simple patch with a
lot detail behind. Let me explain why it's safe,

- cancel_delayed_work_sync() is called in bcache_device_detach() when
dc->count is 0. But cancel_delayed_work_sync() is called before
closure_put(&d->c->caching) in bcache_device_detach().

- After closure_put(&d->c->caching) called in bcache_device_detach(),
refcount of c->caching becomes 0, and cache_set_flush() set in
__cache_set_unregister() continue to execute.

- See continue_at() and closure_put_after_sub(), after c->caching
refoucnt reaches 0, and c->caching->fn gets called, closure_put(parent)
will be called. Here the parent of c->caching is c->cl, so refcount of
c->cl will reach 0, and closure_put_after_sub() will be called again for
c->cl and call its cl->fn which is cache_set_free(). It is set in
bch_cache_set_alloc().

- So before closure_put(&d->c->caching) is called in
bcache_device_detach(), cache_set_flush() in __cache_set_unregister()
won't be executed and memory of cache_set won't be freed.

Now back to delayed worker routine update_writeback_rate(), I check
c->flags inside it, in this moment cancel_delayed_work_sync() is still
waiting for update_writeback_rate() to exit, so refcount of c->caching
is still hold and cache set memory will only be freed after
cancel_delayed_work_sync() returns and refcount of c->caching dropped.

And in cached_dev_free(), bcache_device_free() and
kobject_put(&dc->disk.kobj) are called after cancel_delayed_work_sync()
returns. Therefore when update_writeback_rate() is executing and
accessing c->flgas inside, memory of cache_set, bcache_device and
cached_dev are all there.

This is why it is safe ;-)

Coly Li



[Index of Archives]     [Linux RAID]     [Linux SCSI]     [Linux ATA RAID]     [IDE]     [Linux Wireless]     [Linux Kernel]     [ATH6KL]     [Linux Bluetooth]     [Linux Netdev]     [Kernel Newbies]     [Security]     [Git]     [Netfilter]     [Bugtraq]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Device Mapper]

  Powered by Linux