Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

null_blk: Fix handling of submit_queues and poll_queues attributes

Commit 0a593fbbc245 ("null_blk: poll queue support") introduced the poll
queue feature to null_blk. After this change, null_blk device has both
submit queues and poll queues, and null_map_queues() callback maps the
both queues for corresponding hardware contexts. The commit also added
the device configuration attribute 'poll_queues' in same manner as the
existing attribute 'submit_queues'. These attributes allow to modify the
numbers of queues. However, when the new values are stored to these
attributes, the values are just handled only for the corresponding
queue. When number of submit_queue is updated, number of poll_queue is
not counted, or vice versa. This caused inconsistent number of queues
and queue mapping and resulted in null-ptr-dereference. This failure was
observed in blktests block/029 and block/030.

To avoid the inconsistency, fix the attribute updates to care both
submit_queues and poll_queues. Introduce the helper function
nullb_update_nr_hw_queues() to handle stores to the both two attributes.
Add poll_queues field to the struct nullb_device to track the number in
same manner as submit_queues. Add two more fields prev_submit_queues and
prev_poll_queues to keep the previous values before change. In case the
block layer failed to update the nr_hw_queues, refer the previous values
in null_map_queues() to map queues in same manner as before change.

Also add poll_queues value checks in nullb_update_nr_hw_queues() and
null_validate_conf(). They ensure the poll_queues value of each device
is within the range from 1 to module parameter value of poll_queues.

Fixes: 0a593fbbc245 ("null_blk: poll queue support")
Reported-by: Yi Zhang <yi.zhang@redhat.com>
Signed-off-by: Shin'ichiro Kawasaki <shinichiro.kawasaki@wdc.com>
Link: https://lore.kernel.org/r/20211029103926.845635-1-shinichiro.kawasaki@wdc.com
Signed-off-by: Jens Axboe <axboe@kernel.dk>

authored by

Shin'ichiro Kawasaki and committed by
Jens Axboe
15dfc662 df75db1f

+89 -19
+87 -19
drivers/block/null_blk/main.c
··· 328 328 } \ 329 329 CONFIGFS_ATTR(nullb_device_, NAME); 330 330 331 - static int nullb_apply_submit_queues(struct nullb_device *dev, 332 - unsigned int submit_queues) 333 - { 334 - struct nullb *nullb = dev->nullb; 335 - struct blk_mq_tag_set *set; 331 + static int nullb_update_nr_hw_queues(struct nullb_device *dev, 332 + unsigned int submit_queues, 333 + unsigned int poll_queues) 336 334 337 - if (!nullb) 335 + { 336 + struct blk_mq_tag_set *set; 337 + int ret, nr_hw_queues; 338 + 339 + if (!dev->nullb) 338 340 return 0; 341 + 342 + /* 343 + * Make sure at least one queue exists for each of submit and poll. 344 + */ 345 + if (!submit_queues || !poll_queues) 346 + return -EINVAL; 339 347 340 348 /* 341 349 * Make sure that null_init_hctx() does not access nullb->queues[] past 342 350 * the end of that array. 343 351 */ 344 - if (submit_queues > nr_cpu_ids) 352 + if (submit_queues > nr_cpu_ids || poll_queues > g_poll_queues) 345 353 return -EINVAL; 346 - set = nullb->tag_set; 347 - blk_mq_update_nr_hw_queues(set, submit_queues); 348 - return set->nr_hw_queues == submit_queues ? 0 : -ENOMEM; 354 + 355 + /* 356 + * Keep previous and new queue numbers in nullb_device for reference in 357 + * the call back function null_map_queues(). 358 + */ 359 + dev->prev_submit_queues = dev->submit_queues; 360 + dev->prev_poll_queues = dev->poll_queues; 361 + dev->submit_queues = submit_queues; 362 + dev->poll_queues = poll_queues; 363 + 364 + set = dev->nullb->tag_set; 365 + nr_hw_queues = submit_queues + poll_queues; 366 + blk_mq_update_nr_hw_queues(set, nr_hw_queues); 367 + ret = set->nr_hw_queues == nr_hw_queues ? 0 : -ENOMEM; 368 + 369 + if (ret) { 370 + /* on error, revert the queue numbers */ 371 + dev->submit_queues = dev->prev_submit_queues; 372 + dev->poll_queues = dev->prev_poll_queues; 373 + } 374 + 375 + return ret; 376 + } 377 + 378 + static int nullb_apply_submit_queues(struct nullb_device *dev, 379 + unsigned int submit_queues) 380 + { 381 + return nullb_update_nr_hw_queues(dev, submit_queues, dev->poll_queues); 382 + } 383 + 384 + static int nullb_apply_poll_queues(struct nullb_device *dev, 385 + unsigned int poll_queues) 386 + { 387 + return nullb_update_nr_hw_queues(dev, dev->submit_queues, poll_queues); 349 388 } 350 389 351 390 NULLB_DEVICE_ATTR(size, ulong, NULL); 352 391 NULLB_DEVICE_ATTR(completion_nsec, ulong, NULL); 353 392 NULLB_DEVICE_ATTR(submit_queues, uint, nullb_apply_submit_queues); 354 - NULLB_DEVICE_ATTR(poll_queues, uint, nullb_apply_submit_queues); 393 + NULLB_DEVICE_ATTR(poll_queues, uint, nullb_apply_poll_queues); 355 394 NULLB_DEVICE_ATTR(home_node, uint, NULL); 356 395 NULLB_DEVICE_ATTR(queue_mode, uint, NULL); 357 396 NULLB_DEVICE_ATTR(blocksize, uint, NULL); ··· 638 599 dev->size = g_gb * 1024; 639 600 dev->completion_nsec = g_completion_nsec; 640 601 dev->submit_queues = g_submit_queues; 602 + dev->prev_submit_queues = g_submit_queues; 641 603 dev->poll_queues = g_poll_queues; 604 + dev->prev_poll_queues = g_poll_queues; 642 605 dev->home_node = g_home_node; 643 606 dev->queue_mode = g_queue_mode; 644 607 dev->blocksize = g_bs; ··· 1506 1465 { 1507 1466 struct nullb *nullb = set->driver_data; 1508 1467 int i, qoff; 1468 + unsigned int submit_queues = g_submit_queues; 1469 + unsigned int poll_queues = g_poll_queues; 1470 + 1471 + if (nullb) { 1472 + struct nullb_device *dev = nullb->dev; 1473 + 1474 + /* 1475 + * Refer nr_hw_queues of the tag set to check if the expected 1476 + * number of hardware queues are prepared. If block layer failed 1477 + * to prepare them, use previous numbers of submit queues and 1478 + * poll queues to map queues. 1479 + */ 1480 + if (set->nr_hw_queues == 1481 + dev->submit_queues + dev->poll_queues) { 1482 + submit_queues = dev->submit_queues; 1483 + poll_queues = dev->poll_queues; 1484 + } else if (set->nr_hw_queues == 1485 + dev->prev_submit_queues + dev->prev_poll_queues) { 1486 + submit_queues = dev->prev_submit_queues; 1487 + poll_queues = dev->prev_poll_queues; 1488 + } else { 1489 + pr_warn("tag set has unexpected nr_hw_queues: %d\n", 1490 + set->nr_hw_queues); 1491 + return -EINVAL; 1492 + } 1493 + } 1509 1494 1510 1495 for (i = 0, qoff = 0; i < set->nr_maps; i++) { 1511 1496 struct blk_mq_queue_map *map = &set->map[i]; 1512 1497 1513 1498 switch (i) { 1514 1499 case HCTX_TYPE_DEFAULT: 1515 - if (nullb) 1516 - map->nr_queues = nullb->dev->submit_queues; 1517 - else 1518 - map->nr_queues = g_submit_queues; 1500 + map->nr_queues = submit_queues; 1519 1501 break; 1520 1502 case HCTX_TYPE_READ: 1521 1503 map->nr_queues = 0; 1522 1504 continue; 1523 1505 case HCTX_TYPE_POLL: 1524 - if (nullb) 1525 - map->nr_queues = nullb->dev->poll_queues; 1526 - else 1527 - map->nr_queues = g_poll_queues; 1506 + map->nr_queues = poll_queues; 1528 1507 break; 1529 1508 } 1530 1509 map->queue_offset = qoff; ··· 1914 1853 dev->submit_queues = nr_cpu_ids; 1915 1854 else if (dev->submit_queues == 0) 1916 1855 dev->submit_queues = 1; 1856 + dev->prev_submit_queues = dev->submit_queues; 1857 + 1858 + if (dev->poll_queues > g_poll_queues) 1859 + dev->poll_queues = g_poll_queues; 1860 + else if (dev->poll_queues == 0) 1861 + dev->poll_queues = 1; 1862 + dev->prev_poll_queues = dev->poll_queues; 1917 1863 1918 1864 dev->queue_mode = min_t(unsigned int, dev->queue_mode, NULL_Q_MQ); 1919 1865 dev->irqmode = min_t(unsigned int, dev->irqmode, NULL_IRQ_TIMER);
+2
drivers/block/null_blk/null_blk.h
··· 86 86 unsigned int zone_max_open; /* max number of open zones */ 87 87 unsigned int zone_max_active; /* max number of active zones */ 88 88 unsigned int submit_queues; /* number of submission queues */ 89 + unsigned int prev_submit_queues; /* number of submission queues before change */ 89 90 unsigned int poll_queues; /* number of IOPOLL submission queues */ 91 + unsigned int prev_poll_queues; /* number of IOPOLL submission queues before change */ 90 92 unsigned int home_node; /* home node for the device */ 91 93 unsigned int queue_mode; /* block interface */ 92 94 unsigned int blocksize; /* block size */