Implement FLUSH command and honour FUA flag

I changed the request struct to break the 32 bits reserved for the
request type into two.  The first part of this is used for the flags
(such as FUA), and the second part for the command type.  Previously
we'd masked the top two bytes, thus ignoring any flags.
This commit is contained in:
Patrick J Cherry
2018-02-01 22:13:59 +00:00
parent 25cc084108
commit 1f0ef0aad6
4 changed files with 50 additions and 23 deletions

View File

@@ -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 );

View File

@@ -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;

View File

@@ -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;
}

View File

@@ -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;
}
}