qemu with hax to log dma reads & writes jcs.org/2018/11/12/vfio

nbd: BLOCK_STATUS for standard get_block_status function: client part

Minimal realization: only one extent in server answer is supported.
Flag NBD_CMD_FLAG_REQ_ONE is used to force this behavior.

Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
Message-Id: <20180312152126.286890-6-vsementsov@virtuozzo.com>
Reviewed-by: Eric Blake <eblake@redhat.com>
[eblake: grammar tweaks, fix min_block check and 32-bit cap, use -1
instead of errno on failure in nbd_negotiate_simple_meta_context,
ensure that block status makes progress on success]
Signed-off-by: Eric Blake <eblake@redhat.com>

authored by

Vladimir Sementsov-Ogievskiy and committed by
Eric Blake
78a33ab5 1e98efc0

+279
+150
block/nbd-client.c
··· 228 228 return 0; 229 229 } 230 230 231 + /* nbd_parse_blockstatus_payload 232 + * support only one extent in reply and only for 233 + * base:allocation context 234 + */ 235 + static int nbd_parse_blockstatus_payload(NBDClientSession *client, 236 + NBDStructuredReplyChunk *chunk, 237 + uint8_t *payload, uint64_t orig_length, 238 + NBDExtent *extent, Error **errp) 239 + { 240 + uint32_t context_id; 241 + 242 + if (chunk->length != sizeof(context_id) + sizeof(extent)) { 243 + error_setg(errp, "Protocol error: invalid payload for " 244 + "NBD_REPLY_TYPE_BLOCK_STATUS"); 245 + return -EINVAL; 246 + } 247 + 248 + context_id = payload_advance32(&payload); 249 + if (client->info.meta_base_allocation_id != context_id) { 250 + error_setg(errp, "Protocol error: unexpected context id %d for " 251 + "NBD_REPLY_TYPE_BLOCK_STATUS, when negotiated context " 252 + "id is %d", context_id, 253 + client->info.meta_base_allocation_id); 254 + return -EINVAL; 255 + } 256 + 257 + extent->length = payload_advance32(&payload); 258 + extent->flags = payload_advance32(&payload); 259 + 260 + if (extent->length == 0 || 261 + (client->info.min_block && !QEMU_IS_ALIGNED(extent->length, 262 + client->info.min_block)) || 263 + extent->length > orig_length) 264 + { 265 + error_setg(errp, "Protocol error: server sent status chunk with " 266 + "invalid length"); 267 + return -EINVAL; 268 + } 269 + 270 + return 0; 271 + } 272 + 231 273 /* nbd_parse_error_payload 232 274 * on success @errp contains message describing nbd error reply 233 275 */ ··· 642 684 return iter.ret; 643 685 } 644 686 687 + static int nbd_co_receive_blockstatus_reply(NBDClientSession *s, 688 + uint64_t handle, uint64_t length, 689 + NBDExtent *extent, Error **errp) 690 + { 691 + NBDReplyChunkIter iter; 692 + NBDReply reply; 693 + void *payload = NULL; 694 + Error *local_err = NULL; 695 + bool received = false; 696 + 697 + assert(!extent->length); 698 + NBD_FOREACH_REPLY_CHUNK(s, iter, handle, s->info.structured_reply, 699 + NULL, &reply, &payload) 700 + { 701 + int ret; 702 + NBDStructuredReplyChunk *chunk = &reply.structured; 703 + 704 + assert(nbd_reply_is_structured(&reply)); 705 + 706 + switch (chunk->type) { 707 + case NBD_REPLY_TYPE_BLOCK_STATUS: 708 + if (received) { 709 + s->quit = true; 710 + error_setg(&local_err, "Several BLOCK_STATUS chunks in reply"); 711 + nbd_iter_error(&iter, true, -EINVAL, &local_err); 712 + } 713 + received = true; 714 + 715 + ret = nbd_parse_blockstatus_payload(s, &reply.structured, 716 + payload, length, extent, 717 + &local_err); 718 + if (ret < 0) { 719 + s->quit = true; 720 + nbd_iter_error(&iter, true, ret, &local_err); 721 + } 722 + break; 723 + default: 724 + if (!nbd_reply_type_is_error(chunk->type)) { 725 + s->quit = true; 726 + error_setg(&local_err, 727 + "Unexpected reply type: %d (%s) " 728 + "for CMD_BLOCK_STATUS", 729 + chunk->type, nbd_reply_type_lookup(chunk->type)); 730 + nbd_iter_error(&iter, true, -EINVAL, &local_err); 731 + } 732 + } 733 + 734 + g_free(payload); 735 + payload = NULL; 736 + } 737 + 738 + if (!extent->length && !iter.err) { 739 + error_setg(&iter.err, 740 + "Server did not reply with any status extents"); 741 + if (!iter.ret) { 742 + iter.ret = -EIO; 743 + } 744 + } 745 + error_propagate(errp, iter.err); 746 + return iter.ret; 747 + } 748 + 645 749 static int nbd_co_request(BlockDriverState *bs, NBDRequest *request, 646 750 QEMUIOVector *write_qiov) 647 751 { ··· 784 888 return nbd_co_request(bs, &request, NULL); 785 889 } 786 890 891 + int coroutine_fn nbd_client_co_block_status(BlockDriverState *bs, 892 + bool want_zero, 893 + int64_t offset, int64_t bytes, 894 + int64_t *pnum, int64_t *map, 895 + BlockDriverState **file) 896 + { 897 + int64_t ret; 898 + NBDExtent extent = { 0 }; 899 + NBDClientSession *client = nbd_get_client_session(bs); 900 + Error *local_err = NULL; 901 + 902 + NBDRequest request = { 903 + .type = NBD_CMD_BLOCK_STATUS, 904 + .from = offset, 905 + .len = MIN(MIN_NON_ZERO(QEMU_ALIGN_DOWN(INT_MAX, 906 + bs->bl.request_alignment), 907 + client->info.max_block), bytes), 908 + .flags = NBD_CMD_FLAG_REQ_ONE, 909 + }; 910 + 911 + if (!client->info.base_allocation) { 912 + *pnum = bytes; 913 + return BDRV_BLOCK_DATA; 914 + } 915 + 916 + ret = nbd_co_send_request(bs, &request, NULL); 917 + if (ret < 0) { 918 + return ret; 919 + } 920 + 921 + ret = nbd_co_receive_blockstatus_reply(client, request.handle, bytes, 922 + &extent, &local_err); 923 + if (local_err) { 924 + error_report_err(local_err); 925 + } 926 + if (ret < 0) { 927 + return ret; 928 + } 929 + 930 + assert(extent.length); 931 + *pnum = extent.length; 932 + return (extent.flags & NBD_STATE_HOLE ? 0 : BDRV_BLOCK_DATA) | 933 + (extent.flags & NBD_STATE_ZERO ? BDRV_BLOCK_ZERO : 0); 934 + } 935 + 787 936 void nbd_client_detach_aio_context(BlockDriverState *bs) 788 937 { 789 938 NBDClientSession *client = nbd_get_client_session(bs); ··· 828 977 829 978 client->info.request_sizes = true; 830 979 client->info.structured_reply = true; 980 + client->info.base_allocation = true; 831 981 ret = nbd_receive_negotiate(QIO_CHANNEL(sioc), export, 832 982 tlscreds, hostname, 833 983 &client->ioc, &client->info, errp);
+6
block/nbd-client.h
··· 61 61 void nbd_client_attach_aio_context(BlockDriverState *bs, 62 62 AioContext *new_context); 63 63 64 + int coroutine_fn nbd_client_co_block_status(BlockDriverState *bs, 65 + bool want_zero, 66 + int64_t offset, int64_t bytes, 67 + int64_t *pnum, int64_t *map, 68 + BlockDriverState **file); 69 + 64 70 #endif /* NBD_CLIENT_H */
+3
block/nbd.c
··· 585 585 .bdrv_detach_aio_context = nbd_detach_aio_context, 586 586 .bdrv_attach_aio_context = nbd_attach_aio_context, 587 587 .bdrv_refresh_filename = nbd_refresh_filename, 588 + .bdrv_co_block_status = nbd_client_co_block_status, 588 589 }; 589 590 590 591 static BlockDriver bdrv_nbd_tcp = { ··· 604 605 .bdrv_detach_aio_context = nbd_detach_aio_context, 605 606 .bdrv_attach_aio_context = nbd_attach_aio_context, 606 607 .bdrv_refresh_filename = nbd_refresh_filename, 608 + .bdrv_co_block_status = nbd_client_co_block_status, 607 609 }; 608 610 609 611 static BlockDriver bdrv_nbd_unix = { ··· 623 625 .bdrv_detach_aio_context = nbd_detach_aio_context, 624 626 .bdrv_attach_aio_context = nbd_attach_aio_context, 625 627 .bdrv_refresh_filename = nbd_refresh_filename, 628 + .bdrv_co_block_status = nbd_client_co_block_status, 626 629 }; 627 630 628 631 static void bdrv_nbd_init(void)
+3
include/block/nbd.h
··· 260 260 /* In-out fields, set by client before nbd_receive_negotiate() and 261 261 * updated by server results during nbd_receive_negotiate() */ 262 262 bool structured_reply; 263 + bool base_allocation; /* base:allocation context for NBD_CMD_BLOCK_STATUS */ 263 264 264 265 /* Set by server results during nbd_receive_negotiate() */ 265 266 uint64_t size; ··· 267 268 uint32_t min_block; 268 269 uint32_t opt_block; 269 270 uint32_t max_block; 271 + 272 + uint32_t meta_base_allocation_id; 270 273 }; 271 274 typedef struct NBDExportInfo NBDExportInfo; 272 275
+117
nbd/client.c
··· 595 595 return QIO_CHANNEL(tioc); 596 596 } 597 597 598 + /* nbd_negotiate_simple_meta_context: 599 + * Set one meta context. Simple means that reply must contain zero (not 600 + * negotiated) or one (negotiated) contexts. More contexts would be considered 601 + * as a protocol error. It's also implied that meta-data query equals queried 602 + * context name, so, if server replies with something different then @context, 603 + * it considered as error too. 604 + * return 1 for successful negotiation, context_id is set 605 + * 0 if operation is unsupported, 606 + * -1 with errp set for any other error 607 + */ 608 + static int nbd_negotiate_simple_meta_context(QIOChannel *ioc, 609 + const char *export, 610 + const char *context, 611 + uint32_t *context_id, 612 + Error **errp) 613 + { 614 + int ret; 615 + NBDOptionReply reply; 616 + uint32_t received_id; 617 + bool received; 618 + uint32_t export_len = strlen(export); 619 + uint32_t context_len = strlen(context); 620 + uint32_t data_len = sizeof(export_len) + export_len + 621 + sizeof(uint32_t) + /* number of queries */ 622 + sizeof(context_len) + context_len; 623 + char *data = g_malloc(data_len); 624 + char *p = data; 625 + 626 + stl_be_p(p, export_len); 627 + memcpy(p += sizeof(export_len), export, export_len); 628 + stl_be_p(p += export_len, 1); 629 + stl_be_p(p += sizeof(uint32_t), context_len); 630 + memcpy(p += sizeof(context_len), context, context_len); 631 + 632 + ret = nbd_send_option_request(ioc, NBD_OPT_SET_META_CONTEXT, data_len, data, 633 + errp); 634 + g_free(data); 635 + if (ret < 0) { 636 + return ret; 637 + } 638 + 639 + if (nbd_receive_option_reply(ioc, NBD_OPT_SET_META_CONTEXT, &reply, 640 + errp) < 0) 641 + { 642 + return -1; 643 + } 644 + 645 + ret = nbd_handle_reply_err(ioc, &reply, errp); 646 + if (ret <= 0) { 647 + return ret; 648 + } 649 + 650 + if (reply.type == NBD_REP_META_CONTEXT) { 651 + char *name; 652 + size_t len; 653 + 654 + if (nbd_read(ioc, &received_id, sizeof(received_id), errp) < 0) { 655 + return -1; 656 + } 657 + be32_to_cpus(&received_id); 658 + 659 + len = reply.length - sizeof(received_id); 660 + name = g_malloc(len + 1); 661 + if (nbd_read(ioc, name, len, errp) < 0) { 662 + g_free(name); 663 + return -1; 664 + } 665 + name[len] = '\0'; 666 + if (strcmp(context, name)) { 667 + error_setg(errp, "Failed to negotiate meta context '%s', server " 668 + "answered with different context '%s'", context, 669 + name); 670 + g_free(name); 671 + return -1; 672 + } 673 + g_free(name); 674 + 675 + received = true; 676 + 677 + /* receive NBD_REP_ACK */ 678 + if (nbd_receive_option_reply(ioc, NBD_OPT_SET_META_CONTEXT, &reply, 679 + errp) < 0) 680 + { 681 + return -1; 682 + } 683 + 684 + ret = nbd_handle_reply_err(ioc, &reply, errp); 685 + if (ret <= 0) { 686 + return ret; 687 + } 688 + } 689 + 690 + if (reply.type != NBD_REP_ACK) { 691 + error_setg(errp, "Unexpected reply type %" PRIx32 " expected %x", 692 + reply.type, NBD_REP_ACK); 693 + return -1; 694 + } 695 + 696 + if (received) { 697 + *context_id = received_id; 698 + return 1; 699 + } 700 + 701 + return 0; 702 + } 598 703 599 704 int nbd_receive_negotiate(QIOChannel *ioc, const char *name, 600 705 QCryptoTLSCreds *tlscreds, const char *hostname, ··· 606 711 int rc; 607 712 bool zeroes = true; 608 713 bool structured_reply = info->structured_reply; 714 + bool base_allocation = info->base_allocation; 609 715 610 716 trace_nbd_receive_negotiate(tlscreds, hostname ? hostname : "<null>"); 611 717 612 718 info->structured_reply = false; 719 + info->base_allocation = false; 613 720 rc = -EINVAL; 614 721 615 722 if (outioc) { ··· 698 805 goto fail; 699 806 } 700 807 info->structured_reply = result == 1; 808 + } 809 + 810 + if (info->structured_reply && base_allocation) { 811 + result = nbd_negotiate_simple_meta_context( 812 + ioc, name, "base:allocation", 813 + &info->meta_base_allocation_id, errp); 814 + if (result < 0) { 815 + goto fail; 816 + } 817 + info->base_allocation = result == 1; 701 818 } 702 819 703 820 /* Try NBD_OPT_GO first - if it works, we are done (it