diff --git a/src/common/nbdtypes.c b/src/common/nbdtypes.c index 08dc0c9..f45bf0f 100644 --- a/src/common/nbdtypes.c +++ b/src/common/nbdtypes.c @@ -28,7 +28,8 @@ void nbd_h2r_init( struct nbd_init * from, struct nbd_init_raw * to) void nbd_r2h_request( struct nbd_request_raw *from, struct nbd_request * to ) { to->magic = htobe32( from->magic ); - to->type = htobe32( from->type ); + to->flags = htobe16( from->flags ); + to->type = htobe16( from->type ); to->handle.w = from->handle.w; to->from = htobe64( from->from ); to->len = htobe32( from->len ); @@ -37,7 +38,8 @@ void nbd_r2h_request( struct nbd_request_raw *from, struct nbd_request * to ) void nbd_h2r_request( struct nbd_request * from, struct nbd_request_raw * to ) { to->magic = be32toh( from->magic ); - to->type = be32toh( from->type ); + to->flags = be16toh( from->flags ); + to->type = be16toh( from->type ); to->handle.w = from->handle.w; to->from = be64toh( from->from ); to->len = be32toh( from->len ); diff --git a/src/common/nbdtypes.h b/src/common/nbdtypes.h index bc4f413..7f406c2 100644 --- a/src/common/nbdtypes.h +++ b/src/common/nbdtypes.h @@ -8,20 +8,27 @@ #define REQUEST_MAGIC 0x25609513 #define REPLY_MAGIC 0x67446698 #define REQUEST_READ 0 + #define REQUEST_WRITE 1 #define REQUEST_DISCONNECT 2 +#define REQUEST_FLUSH 3 +#define REQUEST_TRIM 4 +#define REQUEST_WRITE_ZEROES 6 -#define NBD_FLAG_HAS_FLAGS (1 << 0) -#define NBD_FLAG_READ_ONLY (1 << 1) -#define NBD_FLAG_SEND_FLUSH (1 << 2) -#define NBD_FLAG_SEND_FUA (1 << 3) -#define NBD_FLAG_ROTATIONAL (1 << 4) -#define NBD_FLAG_SEND_TRIM (1 << 5) -#define NBD_FLAG_SEND_WRITE_ZEROES (1 << 6) +#define FLAG_HAS_FLAGS (1 << 0) +#define FLAG_READ_ONLY (1 << 1) +#define FLAG_SEND_FLUSH (1 << 2) +#define FLAG_SEND_FUA (1 << 3) +#define FLAG_ROTATIONAL (1 << 4) +#define FLAG_SEND_TRIM (1 << 5) +#define FLAG_SEND_WRITE_ZEROES (1 << 6) +#define FLAG_CAN_MULTI_CONN (1 << 8) /* multiple connections are okay */ + +#define CMD_FLAG_FUA (1 << 0) +#define CMD_FLAG_NO_HOLE (1 << 1) /* The top 2 bytes of the type field are overloaded and can contain flags */ -#define REQUEST_MASK 0x0000ffff - +// #define REQUEST_MASK 0x0000ffff /* 1MiB is the de-facto standard for maximum size of header + data */ #define NBD_MAX_SIZE ( 32 * 1024 * 1024 ) @@ -52,7 +59,8 @@ struct nbd_init_raw { struct nbd_request_raw { __be32 magic; - __be32 type; /* == READ || == WRITE */ + __be16 flags; + __be16 type; /* == READ || == WRITE || == FLUSH */ nbd_handle_t handle; __be64 from; __be32 len; @@ -74,7 +82,8 @@ struct nbd_init { struct nbd_request { uint32_t magic; - uint32_t type; /* == READ || == WRITE || == DISCONNECT */ + uint16_t flags; + uint16_t type; /* == READ || == WRITE || == DISCONNECT || == FLUSH */ nbd_handle_t handle; uint64_t from; uint32_t len; diff --git a/src/proxy/proxy.c b/src/proxy/proxy.c index bdc1407..c8af40e 100644 --- a/src/proxy/proxy.c +++ b/src/proxy/proxy.c @@ -305,7 +305,7 @@ int proxy_prefetch_for_request( struct proxier* proxy, int state ) struct nbd_request_raw* req_raw = (struct nbd_request_raw*) proxy->req.buf; struct nbd_reply_raw *rsp_raw = (struct nbd_reply_raw*) proxy->rsp.buf; - int is_read = ( req->type & REQUEST_MASK ) == REQUEST_READ; + int is_read = req->type == REQUEST_READ; if ( is_read ) { /* See if we can respond with what's in our prefetch @@ -435,19 +435,19 @@ int proxy_read_from_downstream( struct proxier *proxy, int state ) if ( proxy->req.needle == NBD_REQUEST_SIZE ) { nbd_r2h_request( request_raw, request ); - if ( ( request->type & REQUEST_MASK ) == REQUEST_DISCONNECT ) { + if ( request->type == REQUEST_DISCONNECT ) { info( "Received disconnect request from client" ); return EXIT; } /* Simple validations */ - if ( ( request->type & REQUEST_MASK ) == REQUEST_READ ) { + if ( request->type == REQUEST_READ ) { if (request->len > ( NBD_MAX_SIZE - NBD_REPLY_SIZE ) ) { warn( "NBD read request size %"PRIu32" too large", request->len ); return EXIT; } } - if ( (request->type & REQUEST_MASK ) == REQUEST_WRITE ) { + if ( request->type == REQUEST_WRITE ) { if (request->len > ( NBD_MAX_SIZE - NBD_REQUEST_SIZE ) ) { warn( "NBD write request size %"PRIu32" too large", request->len ); return EXIT; @@ -607,7 +607,7 @@ int proxy_read_from_upstream( struct proxier* proxy, int state ) goto disconnect; } - if ( ( proxy->req_hdr.type & REQUEST_MASK ) == REQUEST_READ ) { + if ( proxy->req_hdr.type == REQUEST_READ ) { /* Get the read reply data too. */ proxy->rsp.size += proxy->req_hdr.len; } diff --git a/src/server/client.c b/src/server/client.c index 9c23a4d..58ba199 100644 --- a/src/server/client.c +++ b/src/server/client.c @@ -308,7 +308,7 @@ void client_write_init( struct client * client, uint64_t size ) init.magic = INIT_MAGIC; init.size = size; // TODO actually implement these flags! - init.flags = NBD_FLAG_HAS_FLAGS | NBD_FLAG_SEND_FLUSH | NBD_FLAG_SEND_FUA; + init.flags = FLAG_HAS_FLAGS | FLAG_SEND_FLUSH | FLAG_SEND_FUA; // memset( init.reserved, 0, 124 ); nbd_h2r_init( &init, &init_raw ); @@ -385,8 +385,8 @@ int client_request_needs_reply( struct client * client, } debug( - "request type=%"PRIu32", from=%"PRIu64", len=%"PRIu32", handle=0x%08X", - request.type, request.from, request.len, request.handle + "request type=%"PRIu16", flags=%"PRIu16", from=%"PRIu64", len=%"PRIu32", handle=0x%08X", + request.type, request.flags, request.from, request.len, request.handle ); /* check it's not out of range */ @@ -413,7 +413,8 @@ int client_request_needs_reply( struct client * client, debug("request disconnect"); client->disconnect = 1; return 0; - + case REQUEST_FLUSH: + break; default: fatal("Unknown request 0x%08X", request.type); } @@ -474,7 +475,8 @@ void client_reply_to_write( struct client* client, struct nbd_request request ) bitset_set_range(client->serve->allocation_map, request.from, request.len); } - if (1) /* not sure whether this is necessary... */ + // Only flush if FUA is set + if (request.flags & CMD_FLAG_FUA) { /* multiple of 4K page size */ uint64_t from_rounded = request.from & (!0xfff); @@ -490,6 +492,17 @@ void client_reply_to_write( struct client* client, struct nbd_request request ) client_write_reply( client, &request, 0); } +void client_reply_to_flush( struct client* client, struct nbd_request request ) +{ + debug("request flush from=%"PRIu64", len=%"PRIu32", handle=0x%08X", request.from, request.len, request.handle); + + ERROR_IF_NEGATIVE( + fsync(client->fileno), + "flush failed" + ); + + client_write_reply( client, &request, 0); +} void client_reply( struct client* client, struct nbd_request request ) { @@ -500,6 +513,9 @@ void client_reply( struct client* client, struct nbd_request request ) case REQUEST_WRITE: client_reply_to_write( client, request ); break; + case REQUEST_FLUSH: + client_reply_to_flush( client, request ); + break; } }