[Devel] [PATCH VZ9 2/3] block/blk-cbt: allow multiple cbts in a single queue
Pavel Tikhomirov
ptikhomirov at virtuozzo.com
Wed Jan 22 13:51:46 MSK 2025
> +static struct cbt_info *blk_cbt_find(struct request_queue *q, __u8 *name)
> +{
> + struct cbt_info *cbt;
> +
> + list_for_each_entry(cbt, &q->cbt_list, list)
> + if (!memcmp(name, cbt->name, CBT_NAME_LENGTH))
> + return cbt;
> +
> + return NULL;
> +}
> +
> static int blk_cbt_snap_create(struct request_queue *q, __u8 *name,
> struct blk_user_cbt_snap_create __user *arg)
> {
> @@ -382,8 +397,7 @@ static int blk_cbt_snap_create(struct request_queue *q, __u8 *name,
> return -EFAULT;
>
> mutex_lock(&cbt_mutex);
> - cbt = q->cbt;
> -
> + cbt = blk_cbt_find(q, name);
> if (!cbt) {
> mutex_unlock(&cbt_mutex);
> return -ENOENT;
> @@ -392,11 +406,6 @@ static int blk_cbt_snap_create(struct request_queue *q, __u8 *name,
> BUG_ON(!cbt->map);
> BUG_ON(!cbt->block_max);
>
> - if (!name || memcmp(name, cbt->name, sizeof(cbt->name))) {
> - mutex_unlock(&cbt_mutex);
> - return -EINVAL;
> - }
> -
> if (cbt->snp_map) {
> mutex_unlock(&cbt_mutex);
> return -EBUSY;
...
> @@ -667,35 +665,46 @@ static void cbt_release_callback(struct rcu_head *head)
> kfree(cbt);
> }
>
> -void blk_cbt_release(struct request_queue *q)
> +static void blk_cbt_del(struct cbt_info *cbt)
> {
> - struct cbt_info *cbt;
> - int in_use = 0;
> unsigned long flags;
> + int in_use;
>
> - cbt = q->cbt;
> - if (!cbt)
> - return;
> spin_lock_irqsave(&cbt->lock, flags);
> set_bit(CBT_DEAD, &cbt->flags);
> - rcu_assign_pointer(q->cbt, NULL);
> + list_del_rcu(&cbt->list);
> in_use = cbt->count;
> spin_unlock_irqrestore(&cbt->lock, flags);
> if (!in_use)
> call_rcu(&cbt->rcu, &cbt_release_callback);
> }
Why we don't take rcu_read_lock around list iteration in blk_cbt_find?
If we don't take rcu_read_lock there, the cbt returned from blk_cbt_find
can be already freed.
I see other places of _rcu walk without rcu_read_lock, e.g.:
cbt_page_alloc (!in_rcu) case, blk_cbt_update_size.
Also list can be broken probably if blk_cbt_del runs concurrently with
itself (blk_cbt_release vs cbt_ioc_stop). I think the whole loop in
blk_cbt_release should be protected against concurrent cbt_ioc_stop
(that may be true as blk_cbt_release probably runs at the very end).
--
Best regards, Tikhomirov Pavel
Senior Software Developer, Virtuozzo.
More information about the Devel
mailing list