diff options
author | Anton Ivanov <anton.ivanov@cambridgegreys.com> | 2018-11-14 13:41:09 -0500 |
---|---|---|
committer | Richard Weinberger <richard@nod.at> | 2018-12-27 16:48:20 -0500 |
commit | 50109b5a03b4024eb6b8df3ab8f427625f54fe92 (patch) | |
tree | 763dffe32e8a11bf9c6ac69bbf4a45cbc8e1ebff /arch/um/drivers | |
parent | a41421edb926fcc8f212742b2e7a1f21c9047853 (diff) |
um: Add support for DISCARD in the UBD Driver
Support for DISCARD and WRITE_ZEROES in the ubd driver using
fallocate.
DISCARD is enabled by default and can be disabled using a new
UBD command line flag.
If the underlying fs on which the UBD image is stored does not
support DISCARD the support for both DISCARD and WRITE_ZEROES
is turned off.
Signed-off-by: Anton Ivanov <anton.ivanov@cambridgegreys.com>
Signed-off-by: Richard Weinberger <richard@nod.at>
Diffstat (limited to 'arch/um/drivers')
-rw-r--r-- | arch/um/drivers/ubd_kern.c | 65 |
1 files changed, 54 insertions, 11 deletions
diff --git a/arch/um/drivers/ubd_kern.c b/arch/um/drivers/ubd_kern.c index 1672e3c49bfb..7aaa473909be 100644 --- a/arch/um/drivers/ubd_kern.c +++ b/arch/um/drivers/ubd_kern.c | |||
@@ -154,6 +154,7 @@ struct ubd { | |||
154 | struct openflags openflags; | 154 | struct openflags openflags; |
155 | unsigned shared:1; | 155 | unsigned shared:1; |
156 | unsigned no_cow:1; | 156 | unsigned no_cow:1; |
157 | unsigned no_trim:1; | ||
157 | struct cow cow; | 158 | struct cow cow; |
158 | struct platform_device pdev; | 159 | struct platform_device pdev; |
159 | struct request_queue *queue; | 160 | struct request_queue *queue; |
@@ -177,6 +178,7 @@ struct ubd { | |||
177 | .boot_openflags = OPEN_FLAGS, \ | 178 | .boot_openflags = OPEN_FLAGS, \ |
178 | .openflags = OPEN_FLAGS, \ | 179 | .openflags = OPEN_FLAGS, \ |
179 | .no_cow = 0, \ | 180 | .no_cow = 0, \ |
181 | .no_trim = 0, \ | ||
180 | .shared = 0, \ | 182 | .shared = 0, \ |
181 | .cow = DEFAULT_COW, \ | 183 | .cow = DEFAULT_COW, \ |
182 | .lock = __SPIN_LOCK_UNLOCKED(ubd_devs.lock), \ | 184 | .lock = __SPIN_LOCK_UNLOCKED(ubd_devs.lock), \ |
@@ -323,7 +325,7 @@ static int ubd_setup_common(char *str, int *index_out, char **error_out) | |||
323 | *index_out = n; | 325 | *index_out = n; |
324 | 326 | ||
325 | err = -EINVAL; | 327 | err = -EINVAL; |
326 | for (i = 0; i < sizeof("rscd="); i++) { | 328 | for (i = 0; i < sizeof("rscdt="); i++) { |
327 | switch (*str) { | 329 | switch (*str) { |
328 | case 'r': | 330 | case 'r': |
329 | flags.w = 0; | 331 | flags.w = 0; |
@@ -337,12 +339,15 @@ static int ubd_setup_common(char *str, int *index_out, char **error_out) | |||
337 | case 'c': | 339 | case 'c': |
338 | ubd_dev->shared = 1; | 340 | ubd_dev->shared = 1; |
339 | break; | 341 | break; |
342 | case 't': | ||
343 | ubd_dev->no_trim = 1; | ||
344 | break; | ||
340 | case '=': | 345 | case '=': |
341 | str++; | 346 | str++; |
342 | goto break_loop; | 347 | goto break_loop; |
343 | default: | 348 | default: |
344 | *error_out = "Expected '=' or flag letter " | 349 | *error_out = "Expected '=' or flag letter " |
345 | "(r, s, c, or d)"; | 350 | "(r, s, c, t or d)"; |
346 | goto out; | 351 | goto out; |
347 | } | 352 | } |
348 | str++; | 353 | str++; |
@@ -415,6 +420,7 @@ __uml_help(ubd_setup, | |||
415 | " 'c' will cause the device to be treated as being shared between multiple\n" | 420 | " 'c' will cause the device to be treated as being shared between multiple\n" |
416 | " UMLs and file locking will be turned off - this is appropriate for a\n" | 421 | " UMLs and file locking will be turned off - this is appropriate for a\n" |
417 | " cluster filesystem and inappropriate at almost all other times.\n\n" | 422 | " cluster filesystem and inappropriate at almost all other times.\n\n" |
423 | " 't' will disable trim/discard support on the device (enabled by default).\n\n" | ||
418 | ); | 424 | ); |
419 | 425 | ||
420 | static int udb_setup(char *str) | 426 | static int udb_setup(char *str) |
@@ -513,9 +519,17 @@ static void ubd_handler(void) | |||
513 | for (count = 0; count < n/sizeof(struct io_thread_req *); count++) { | 519 | for (count = 0; count < n/sizeof(struct io_thread_req *); count++) { |
514 | struct io_thread_req *io_req = (*irq_req_buffer)[count]; | 520 | struct io_thread_req *io_req = (*irq_req_buffer)[count]; |
515 | 521 | ||
516 | if (!blk_update_request(io_req->req, io_req->error, io_req->length)) | 522 | if ((io_req->error == BLK_STS_NOTSUPP) && (req_op(io_req->req) == REQ_OP_DISCARD)) { |
517 | __blk_mq_end_request(io_req->req, io_req->error); | 523 | blk_queue_max_discard_sectors(io_req->req->q, 0); |
518 | 524 | blk_queue_max_write_zeroes_sectors(io_req->req->q, 0); | |
525 | blk_queue_flag_clear(QUEUE_FLAG_DISCARD, io_req->req->q); | ||
526 | } | ||
527 | if ((io_req->error) || (io_req->buffer == NULL)) | ||
528 | blk_mq_end_request(io_req->req, io_req->error); | ||
529 | else { | ||
530 | if (!blk_update_request(io_req->req, io_req->error, io_req->length)) | ||
531 | __blk_mq_end_request(io_req->req, io_req->error); | ||
532 | } | ||
519 | kfree(io_req); | 533 | kfree(io_req); |
520 | } | 534 | } |
521 | } | 535 | } |
@@ -829,6 +843,13 @@ static int ubd_open_dev(struct ubd *ubd_dev) | |||
829 | if(err < 0) goto error; | 843 | if(err < 0) goto error; |
830 | ubd_dev->cow.fd = err; | 844 | ubd_dev->cow.fd = err; |
831 | } | 845 | } |
846 | if (ubd_dev->no_trim == 0) { | ||
847 | ubd_dev->queue->limits.discard_granularity = SECTOR_SIZE; | ||
848 | ubd_dev->queue->limits.discard_alignment = SECTOR_SIZE; | ||
849 | blk_queue_max_discard_sectors(ubd_dev->queue, UBD_MAX_REQUEST); | ||
850 | blk_queue_max_write_zeroes_sectors(ubd_dev->queue, UBD_MAX_REQUEST); | ||
851 | blk_queue_flag_set(QUEUE_FLAG_DISCARD, ubd_dev->queue); | ||
852 | } | ||
832 | blk_queue_flag_set(QUEUE_FLAG_NONROT, ubd_dev->queue); | 853 | blk_queue_flag_set(QUEUE_FLAG_NONROT, ubd_dev->queue); |
833 | return 0; | 854 | return 0; |
834 | error: | 855 | error: |
@@ -1372,6 +1393,10 @@ static blk_status_t ubd_queue_rq(struct blk_mq_hw_ctx *hctx, | |||
1372 | case REQ_OP_WRITE: | 1393 | case REQ_OP_WRITE: |
1373 | ret = queue_rw_req(hctx, req); | 1394 | ret = queue_rw_req(hctx, req); |
1374 | break; | 1395 | break; |
1396 | case REQ_OP_DISCARD: | ||
1397 | case REQ_OP_WRITE_ZEROES: | ||
1398 | ret = ubd_queue_one_vec(hctx, req, (u64)blk_rq_pos(req) << 9, NULL); | ||
1399 | break; | ||
1375 | default: | 1400 | default: |
1376 | WARN_ON_ONCE(1); | 1401 | WARN_ON_ONCE(1); |
1377 | res = BLK_STS_NOTSUPP; | 1402 | res = BLK_STS_NOTSUPP; |
@@ -1463,7 +1488,7 @@ static int update_bitmap(struct io_thread_req *req) | |||
1463 | 1488 | ||
1464 | n = os_pwrite_file(req->fds[1], &req->bitmap_words, | 1489 | n = os_pwrite_file(req->fds[1], &req->bitmap_words, |
1465 | sizeof(req->bitmap_words), req->cow_offset); | 1490 | sizeof(req->bitmap_words), req->cow_offset); |
1466 | if(n != sizeof(req->bitmap_words)) | 1491 | if (n != sizeof(req->bitmap_words)) |
1467 | return map_error(-n); | 1492 | return map_error(-n); |
1468 | 1493 | ||
1469 | return map_error(0); | 1494 | return map_error(0); |
@@ -1471,11 +1496,13 @@ static int update_bitmap(struct io_thread_req *req) | |||
1471 | 1496 | ||
1472 | static void do_io(struct io_thread_req *req) | 1497 | static void do_io(struct io_thread_req *req) |
1473 | { | 1498 | { |
1474 | char *buf; | 1499 | char *buf = NULL; |
1475 | unsigned long len; | 1500 | unsigned long len; |
1476 | int n, nsectors, start, end, bit; | 1501 | int n, nsectors, start, end, bit; |
1477 | __u64 off; | 1502 | __u64 off; |
1478 | 1503 | ||
1504 | /* FLUSH is really a special case, we cannot "case" it with others */ | ||
1505 | |||
1479 | if (req_op(req->req) == REQ_OP_FLUSH) { | 1506 | if (req_op(req->req) == REQ_OP_FLUSH) { |
1480 | /* fds[0] is always either the rw image or our cow file */ | 1507 | /* fds[0] is always either the rw image or our cow file */ |
1481 | req->error = map_error(-os_sync_file(req->fds[0])); | 1508 | req->error = map_error(-os_sync_file(req->fds[0])); |
@@ -1495,26 +1522,42 @@ static void do_io(struct io_thread_req *req) | |||
1495 | off = req->offset + req->offsets[bit] + | 1522 | off = req->offset + req->offsets[bit] + |
1496 | start * req->sectorsize; | 1523 | start * req->sectorsize; |
1497 | len = (end - start) * req->sectorsize; | 1524 | len = (end - start) * req->sectorsize; |
1498 | buf = &req->buffer[start * req->sectorsize]; | 1525 | if (req->buffer != NULL) |
1526 | buf = &req->buffer[start * req->sectorsize]; | ||
1499 | 1527 | ||
1500 | if (req_op(req->req) == REQ_OP_READ) { | 1528 | switch (req_op(req->req)) { |
1529 | case REQ_OP_READ: | ||
1501 | n = 0; | 1530 | n = 0; |
1502 | do { | 1531 | do { |
1503 | buf = &buf[n]; | 1532 | buf = &buf[n]; |
1504 | len -= n; | 1533 | len -= n; |
1505 | n = os_pread_file(req->fds[bit], buf, len, off); | 1534 | n = os_pread_file(req->fds[bit], buf, len, off); |
1506 | if(n < 0){ | 1535 | if (n < 0) { |
1507 | req->error = map_error(-n); | 1536 | req->error = map_error(-n); |
1508 | return; | 1537 | return; |
1509 | } | 1538 | } |
1510 | } while((n < len) && (n != 0)); | 1539 | } while((n < len) && (n != 0)); |
1511 | if (n < len) memset(&buf[n], 0, len - n); | 1540 | if (n < len) memset(&buf[n], 0, len - n); |
1512 | } else { | 1541 | break; |
1542 | case REQ_OP_WRITE: | ||
1513 | n = os_pwrite_file(req->fds[bit], buf, len, off); | 1543 | n = os_pwrite_file(req->fds[bit], buf, len, off); |
1514 | if(n != len){ | 1544 | if(n != len){ |
1515 | req->error = map_error(-n); | 1545 | req->error = map_error(-n); |
1516 | return; | 1546 | return; |
1517 | } | 1547 | } |
1548 | break; | ||
1549 | case REQ_OP_DISCARD: | ||
1550 | case REQ_OP_WRITE_ZEROES: | ||
1551 | n = os_falloc_punch(req->fds[bit], off, len); | ||
1552 | if (n) { | ||
1553 | req->error = map_error(-n); | ||
1554 | return; | ||
1555 | } | ||
1556 | break; | ||
1557 | default: | ||
1558 | WARN_ON_ONCE(1); | ||
1559 | req->error = BLK_STS_NOTSUPP; | ||
1560 | return; | ||
1518 | } | 1561 | } |
1519 | 1562 | ||
1520 | start = end; | 1563 | start = end; |