Implement support for flush, fua and rotational.
authorAlex Bligh <alex@alex.org.uk>
Tue, 17 May 2011 18:35:41 +0000 (19:35 +0100)
committerAlex Bligh <alex@alex.org.uk>
Tue, 17 May 2011 18:35:41 +0000 (19:35 +0100)
This commit implements support for the flush, fua, and rotational directives
within the configuration file.

FUA means "force the current write to hit the media service" and FLUSH
means "empty the current write queue to disk". Broadly they have the same
semantics as the linux kernel REQ_FLUSH and REQ_FUA. FUA is implemented
through sync_file_range() (or if that doesn't exist, fdatasync() on the
file handle concerned), FLUSH through fsync() on all files. FUA and
FLUSH are selected in the config file, and set new flags bits which will
cause the client to sent FUA and FLUSH requests. The way these are
implemented is further explained in doc/proto.txt.

The purpose of this is reasonably obvious: without supporting either FUA
or FLUSH (and it's relatively easy to support both), filesystems on the
client have no way to ensure the relevant sectors have hit the disk.
The patch is implement such that the default behaviour is unchanged.

Additionally, it introduces an F_ROTATIONAL flag. This will turn off
the use of QUEUE_FLAG_NONROT in the client. QUEUE_FLAG_NONROT effectively
disables the elevator algorithm, making the algorithm merge only. That is
unhelpful where the server does not have its own elevator algorithm
or where the client elevator algorithm is neutered (e.g. writing to a
raw partition with F_SYNC with nbd-server). It's not going to be used
often where the backing store is a file.

It also incidentally fixes a bug where F_SYNC is ignored if F_COPYONWRITE
is set (not that this currently has much utility).

Note the following:

* The top 16 bits of the command type have been reserved
  (see NBD_CMD_MASK_COMMAND) for passing flags to be attached to commands.
  NBD_CMD_FLAG_FUA is the first of these.

* simple_test has been modified so it does not stomp over nbd.conf and
  pid files in the current directory.

* A new ioctl has been added which passes the export flags to the kernel.
  There is currently no support for this in the kernel (I will submit
  a patch in due course)

* "make check" now performs a flush and fua test. These have been verified
  by strace to "do the right thing".

* nbd-client incorrectly shifted the flags value left by 16 after reading it.
  This caused the test of the readonly bit to always fail. I suspect this
  may have been an attempt to combine server flags and export flags into
  a single 32 bit word. However, the low 16 bits were never set, and only
  the low 16 bits are tested. As the only thing it was testing for was the
  read only flag, and that is supplied in the flags, and as this allows
  transparent passing to the ioctl, I suggest it is changed as per this commit.
  If there's something else to add, it can be put in the upper 16 bits.

I have tested this lightly with nbd-client (obviously the kernel does not
yet send FLUSH or FUA) and with the test suite. It's probably ready to
go in marked "experimental".

cliserv.h
configure.ac
doc/proto.txt
lfs.h
man/nbd-server.5.in.sgml
nbd-client.c
nbd-server.c
nbd-tester-client.c
simple_test

index b32626f..51b1a9e 100644 (file)
--- a/cliserv.h
+++ b/cliserv.h
@@ -40,7 +40,12 @@ typedef unsigned long long u64;
 #include "nbd.h"
 
 #if NBD_LFS==1
+/* /usr/include/features.h (included from /usr/include/sys/types.h)
+   defines this when _GNU_SOURCE is defined
+ */
+#ifndef _LARGEFILE_SOURCE
 #define _LARGEFILE_SOURCE
+#endif
 #define _FILE_OFFSET_BITS 64
 #endif
 
index f50193f..c78177b 100644 (file)
@@ -94,6 +94,9 @@ AC_CHECK_SIZEOF(unsigned int)
 AC_CHECK_SIZEOF(unsigned long int)
 AC_CHECK_SIZEOF(unsigned long long int)
 AC_CHECK_FUNCS([llseek alarm gethostbyname inet_ntoa memset socket strerror strstr])
+AC_CHECK_FUNC([sync_file_range],
+       [AC_DEFINE([HAVE_SYNC_FILE_RANGE], [sync_file_range(2) is not supported], [sync_file_range(2) is supported])],
+        [])
 AC_FUNC_FORK
 AC_FUNC_SETVBUF_REVERSED
 AC_MSG_CHECKING(whether client should be built)
index fe5e819..d173365 100644 (file)
@@ -26,8 +26,8 @@ server during the handshake.
 There are two message types in the data pushing phase: the request, and
 the response.
 
-There are three request types in the data pushing phase: NBD_CMD_READ,
-NBD_CMD_WRITE, and NBD_CMD_DISC (disconnect).
+There are four request types in the data pushing phase: NBD_CMD_READ,
+NBD_CMD_WRITE, NBD_CMD_DISC (disconnect), and NBD_CMD_FLUSH.
 
 The request is sent by the client; the response by the server. A request
 header consists a 32 bit magic number (magic), a 32 bit field denoting
@@ -50,6 +50,16 @@ we change that to asynchronous handling, handling the disconnect request
 will probably be postponed until there are no other outstanding
 requests.
 
+A flush request will not be sent unless NBD_FLAG_SEND_FLUSH is set,
+and indicates the backing file should be fdatasync()'d to disk.
+
+The top 16 bits of the request are flags. NBD_CMD_FLAG_FUA implies
+a force unit access, and can currently only be usefully combined
+with NBD_CMD_WRITE. This is implementing using sync_file_range
+if present, else by fdatasync() of that file (note not all files
+in a multifile environment). NBD_CMD_FLAG_FUA will not be set
+unless NBD_FLAG_SEND_FUA is set.
+
 There are two versions of the negotiation: the 'old' style (nbd <=
 2.9.16) and the 'new' style (nbd >= 2.9.17, though due to a bug it does
 not work with anything below 2.9.18). What follows is a description of
diff --git a/lfs.h b/lfs.h
index 929ce08..480d6bf 100644 (file)
--- a/lfs.h
+++ b/lfs.h
@@ -4,7 +4,13 @@
 #include "config.h"
 #if NBD_LFS
 #define _FILE_OFFSET_BITS 64
+#ifndef _LARGEFILE_SOURCE
 #define _LARGEFILE_SOURCE
+#endif
+#ifdef HAVE_SYNC_FILE_RANGE
+#define USE_SYNC_FILE_RANGE
+#define _GNU_SOURCE
+#endif /* HAVE_SYNC_FILE_RANGE */
 #endif /* NBD_LFS */
 
 #endif /* LFS_H */
index 9fb2eff..07ed9fd 100644 (file)
@@ -410,6 +410,64 @@ manpage.1: manpage.sgml
        </listitem>
       </varlistentry>
       <varlistentry>
+        <term><option>flush</option></term>
+       <listitem>
+         <para>Optional; boolean.</para>
+         <para>When this option is enabled,
+           <command>nbd-server</command> will inform the client that it
+           supports and desires to be sent flush requests when the
+           elevator layer receives them. Receipt of a flush request
+           will cause an fdatasync() (or, if the sync option is set,
+           an fsync()) on the backend storage. This increases
+           reliability in the case of an unclean shutdown at
+           the expense of a degradation of performance. The default
+           state is disabled. This option will have no effect unless
+           supported by the client.
+         </para>
+       </listitem>
+      </varlistentry>
+      <varlistentry>
+        <term><option>fua</option></term>
+       <listitem>
+         <para>Optional; boolean.</para>
+         <para>When this option is enabled,
+           <command>nbd-server</command> will inform the client that it
+           supports and desires to be sent fua (force unit access) commands
+           when the elevator layer receives them. Receipt of a force unit
+           access command will cause the specified command to be synced
+           to backend storage using sync_file_range() if supported, or
+           fdatasync() otherwise. This increases
+           reliability in the case of an unclean shutdown at
+           the expense of a degradation of performance. The default
+           state is disabled. This option will have no effect unless
+           supported by the client.
+         </para>
+       </listitem>
+      </varlistentry>
+      <varlistentry>
+        <term><option>rotational</option></term>
+       <listitem>
+         <para>Optional; boolean.</para>
+         <para>When this option is enabled,
+           <command>nbd-server</command> will inform the client that it
+           it would prefer it to send requests in elevator order, perhaps
+           because it has a backing store and no local elevator. By
+           default, the client uses QUEUE_FLAG_NONROT, which effectively
+           restricts the function of the elevator to block merges. By
+           specifying this flag on the server, the client will not use
+           QUEUE_FLAG_NONROT, meaning the client elevator will perform
+           normal elevator ordering of I/O requests. Note that even when
+           the backing store is on rotating media, it is not normally
+           necessary to specify this flag, as the server's elevator
+           algorithm will be used. This flag is only required where
+           the server will not be using an elevator algorithm or where
+           the elevator algorithm is effectively neutered (e.g. with
+           the sync option set). This option will have no effect unless
+           supported by the client.
+         </para>
+       </listitem>
+      </varlistentry>
+      <varlistentry>
        <term><option>sparse_cow</option></term>
        <listitem>
          <para>Optional; boolean.</para>
index 0fc9c34..78e62f9 100644 (file)
@@ -150,7 +150,7 @@ void negotiate(int sock, u64 *rsize64, u32 *flags, char* name) {
                if(read(sock, &tmp, sizeof(uint16_t)) < 0) {
                        err("Failed reading flags: %m");
                }
-               *flags = ((u32)ntohs(tmp)) << 16;
+               *flags = ((u32)ntohs(tmp));
 
                /* reserved for future use*/
                if (write(sock, &reserved, sizeof(reserved)) < 0)
@@ -240,6 +240,9 @@ void setsizes(int nbd, u64 size64, int blocksize, u32 flags) {
 
        ioctl(nbd, NBD_CLEAR_SOCK);
 
+       /* ignore error as kernel may not support */
+       ioctl(nbd, NBD_SET_FLAGS, (unsigned long) flags);
+
        if (ioctl(nbd, BLKROSET, (unsigned long) &read_only) < 0)
                err("Unable to set read-only attribute for device");
 }
index cd584f0..602d42e 100644 (file)
@@ -137,11 +137,13 @@ int dontfork = 0;
 #define DEBUG2( a,b ) printf( a,b )
 #define DEBUG3( a,b,c ) printf( a,b,c )
 #define DEBUG4( a,b,c,d ) printf( a,b,c,d )
+#define DEBUG5( a,b,c,d,e ) printf( a,b,c,d,e )
 #else
 #define DEBUG( a )
 #define DEBUG2( a,b ) 
 #define DEBUG3( a,b,c ) 
 #define DEBUG4( a,b,c,d ) 
+#define DEBUG5( a,b,c,d,e ) 
 #endif
 #ifndef PACKAGE_VERSION
 #define PACKAGE_VERSION ""
@@ -163,6 +165,9 @@ int dontfork = 0;
 #define F_SPARSE 16      /**< flag to tell us copyronwrite should use a sparse file */
 #define F_SDP 32         /**< flag to tell us the export should be done using the Socket Direct Protocol for RDMA */
 #define F_SYNC 64        /**< Whether to fsync() after a write */
+#define F_FLUSH 128      /**< Whether server wants FLUSH to be sent by the client */
+#define F_FUA 256        /**< Whether server wants FUA to be sent by the client */
+#define F_ROTATIONAL 512  /**< Whether server wants the client to implement the elevator algorithm */
 GHashTable *children;
 char pidfname[256]; /**< name of our PID file */
 char pidftemplate[256]; /**< template to be used for the filename of the PID file */
@@ -720,6 +725,9 @@ GArray* parse_cfile(gchar* f, GError** e) {
                { "sparse_cow", FALSE,  PARAM_BOOL,     &(s.flags),             F_SPARSE },
                { "sdp",        FALSE,  PARAM_BOOL,     &(s.flags),             F_SDP },
                { "sync",       FALSE,  PARAM_BOOL,     &(s.flags),             F_SYNC },
+               { "flush",      FALSE,  PARAM_BOOL,     &(s.flags),             F_FLUSH },
+               { "fua",        FALSE,  PARAM_BOOL,     &(s.flags),             F_FUA },
+               { "rotational", FALSE,  PARAM_BOOL,     &(s.flags),             F_ROTATIONAL },
                { "listenaddr", FALSE,  PARAM_STRING,   &(s.listenaddr),        0 },
                { "maxconnections", FALSE, PARAM_INT,   &(s.max_connections),   0 },
        };
@@ -1055,7 +1063,7 @@ void myseek(int handle,off_t a) {
  * @param client The client we're serving for
  * @return The number of bytes actually written, or -1 in case of an error
  **/
-ssize_t rawexpwrite(off_t a, char *buf, size_t len, CLIENT *client) {
+ssize_t rawexpwrite(off_t a, char *buf, size_t len, CLIENT *client, int fua) {
        int fhandle;
        off_t foffset;
        size_t maxbytes;
@@ -1066,12 +1074,20 @@ ssize_t rawexpwrite(off_t a, char *buf, size_t len, CLIENT *client) {
        if(maxbytes && len > maxbytes)
                len = maxbytes;
 
-       DEBUG4("(WRITE to fd %d offset %llu len %u), ", fhandle, foffset, len);
+       DEBUG5("(WRITE to fd %d offset %llu len %u fua %d), ", fhandle, foffset, len, fua);
 
        myseek(fhandle, foffset);
        retval = write(fhandle, buf, len);
        if(client->server->flags & F_SYNC) {
                fsync(fhandle);
+       } else if (fua) {
+#ifdef USE_SYNC_FILE_RANGE
+               sync_file_range(fhandle, foffset, len,
+                               SYNC_FILE_RANGE_WAIT_BEFORE | SYNC_FILE_RANGE_WRITE |
+                               SYNC_FILE_RANGE_WAIT_AFTER);
+#else
+               fdatasync(fhandle);
+#endif
        }
        return retval;
 }
@@ -1080,10 +1096,10 @@ ssize_t rawexpwrite(off_t a, char *buf, size_t len, CLIENT *client) {
  * Call rawexpwrite repeatedly until all data has been written.
  * @return 0 on success, nonzero on failure
  **/
-int rawexpwrite_fully(off_t a, char *buf, size_t len, CLIENT *client) {
+int rawexpwrite_fully(off_t a, char *buf, size_t len, CLIENT *client, int fua) {
        ssize_t ret=0;
 
-       while(len > 0 && (ret=rawexpwrite(a, buf, len, client)) > 0 ) {
+       while(len > 0 && (ret=rawexpwrite(a, buf, len, client, fua)) > 0 ) {
                a += ret;
                buf += ret;
                len -= ret;
@@ -1184,7 +1200,7 @@ int expread(off_t a, char *buf, size_t len, CLIENT *client) {
  * @param client The client we're going to write for.
  * @return 0 on success, nonzero on failure
  **/
-int expwrite(off_t a, char *buf, size_t len, CLIENT *client) {
+int expwrite(off_t a, char *buf, size_t len, CLIENT *client, int fua) {
        char pagebuf[DIFFPAGESIZE];
        off_t mapcnt,mapl,maph;
        off_t wrlen,rdlen; 
@@ -1192,7 +1208,7 @@ int expwrite(off_t a, char *buf, size_t len, CLIENT *client) {
        off_t offset;
 
        if (!(client->server->flags & F_COPYONWRITE))
-               return(rawexpwrite_fully(a, buf, len, client)); 
+               return(rawexpwrite_fully(a, buf, len, client, fua)); 
        DEBUG3("Asked to write %d bytes at %llu.\n", len, (unsigned long long)a);
 
        mapl=a/DIFFPAGESIZE ; maph=(a+len-1)/DIFFPAGESIZE ;
@@ -1225,6 +1241,33 @@ int expwrite(off_t a, char *buf, size_t len, CLIENT *client) {
                }                                                   
                len-=wrlen ; a+=wrlen ; buf+=wrlen ;
        }
+       if (client->server->flags & F_SYNC) {
+               fsync(client->difffile);
+       } else if (fua) {
+               /* open question: would it be cheaper to do multiple sync_file_ranges?
+                  as we iterate through the above?
+                */
+               fdatasync(client->difffile);
+       }
+       return 0;
+}
+
+int expflush(CLIENT *client) {
+       int fhandle;
+       off_t foffset;
+       size_t maxbytes;
+       gint i;
+
+        if (client->server->flags & F_COPYONWRITE) {
+               return fsync(client->difffile);
+       }
+       
+       for (i = 0; i < client->export->len; i++) {
+               FILE_INFO fi = g_array_index(client->export, FILE_INFO, i);
+               if (fsync(fi.fhandle) < 0)
+                       return -1;
+       }
+       
        return 0;
 }
 
@@ -1317,6 +1360,12 @@ CLIENT* negotiate(int net, CLIENT *client, GArray* servers) {
                err("Negotiation failed: %m");
        if (client->server->flags & F_READONLY)
                flags |= NBD_FLAG_READ_ONLY;
+       if (client->server->flags & F_FLUSH)
+               flags |= NBD_FLAG_SEND_FLUSH;
+       if (client->server->flags & F_FUA)
+               flags |= NBD_FLAG_SEND_FUA;
+       if (client->server->flags & F_ROTATIONAL)
+               flags |= NBD_FLAG_ROTATIONAL;
        if (!client->modern) {
                /* oldstyle */
                flags = htonl(flags);
@@ -1366,6 +1415,7 @@ int mainloop(CLIENT *client) {
                size_t len;
                size_t currlen;
                size_t writelen;
+               uint16_t command;
 #ifdef DODBG
                i++;
                printf("%d: ", i);
@@ -1373,8 +1423,9 @@ int mainloop(CLIENT *client) {
                readit(client->net, &request, sizeof(request));
                request.from = ntohll(request.from);
                request.type = ntohl(request.type);
+               command = request.type & NBD_CMD_MASK_COMMAND;
 
-               if (request.type==NBD_CMD_DISC) {
+               if (command==NBD_CMD_DISC) {
                        msg2(LOG_INFO, "Disconnect request received.");
                        if (client->server->flags & F_COPYONWRITE) { 
                                if (client->difmap) g_free(client->difmap) ;
@@ -1397,7 +1448,7 @@ int mainloop(CLIENT *client) {
                        currlen = len;
                }
 #ifdef DODBG
-               printf("%s from %llu (%llu) len %d, ", request.type ? "WRITE" :
+               printf("%s from %llu (%llu) len %d, ", command ? "WRITE" :
                                "READ", (unsigned long long)request.from,
                                (unsigned long long)request.from / 512, len);
 #endif
@@ -1414,7 +1465,7 @@ int mainloop(CLIENT *client) {
                        continue;
                }
 
-               if (request.type==NBD_CMD_WRITE) {
+               if (command==NBD_CMD_WRITE) {
                        DEBUG("wr: net->buf, ");
                        while(len > 0) {
                                readit(client->net, buf, currlen);
@@ -1425,7 +1476,8 @@ int mainloop(CLIENT *client) {
                                        ERROR(client, reply, EPERM);
                                        continue;
                                }
-                               if (expwrite(request.from, buf, len, client)) {
+                               if (expwrite(request.from, buf, len, client,
+                                            request.type & NBD_CMD_FLAG_FUA)) {
                                        DEBUG("Write failed: %m" );
                                        ERROR(client, reply, errno);
                                        continue;
@@ -1437,27 +1489,43 @@ int mainloop(CLIENT *client) {
                        }
                        continue;
                }
-               /* READ */
-
-               DEBUG("exp->buf, ");
-               memcpy(buf, &reply, sizeof(struct nbd_reply));
-               p = buf + sizeof(struct nbd_reply);
-               writelen = currlen + sizeof(struct nbd_reply);
-               while(len > 0) {
-                       if (expread(request.from, p, currlen, client)) {
-                               DEBUG("Read failed: %m");
+
+               if (command==NBD_CMD_FLUSH) {
+                       DEBUG("fl: ");
+                       if (expflush(client)) {
+                               DEBUG("Flush failed: %m");
                                ERROR(client, reply, errno);
                                continue;
                        }
+                       SEND(client->net, reply);
+                       DEBUG("OK!\n");
+                       continue;
+               }
 
-                       DEBUG("buf->net, ");
-                       writeit(client->net, buf, writelen);
-                       len -= currlen;
-                       currlen = (len < BUFSIZE) ? len : BUFSIZE;
-                       p = buf;
-                       writelen = currlen;
+               if (command==NBD_CMD_READ) {
+                       DEBUG("exp->buf, ");
+                       memcpy(buf, &reply, sizeof(struct nbd_reply));
+                       p = buf + sizeof(struct nbd_reply);
+                       writelen = currlen + sizeof(struct nbd_reply);
+                       while(len > 0) {
+                               if (expread(request.from, p, currlen, client)) {
+                                       DEBUG("Read failed: %m");
+                                       ERROR(client, reply, errno);
+                                       continue;
+                               }
+                               
+                               DEBUG("buf->net, ");
+                               writeit(client->net, buf, writelen);
+                               len -= currlen;
+                               currlen = (len < BUFSIZE) ? len : BUFSIZE;
+                               p = buf;
+                               writelen = currlen;
+                       }
+                       DEBUG("OK!\n");
+                       continue;
                }
-               DEBUG("OK!\n");
+
+               DEBUG ("Ignoring unknown command\n");
        }
        return 0;
 }
index 1b7ec52..78d4a1f 100644 (file)
@@ -356,7 +356,7 @@ int throughput_test(gchar* hostname, int port, char* name, int sock,
        if (!(testflags & TEST_WRITE))
                testflags &= ~TEST_FLUSH;
 
-       memset (writebuf, 'X', sizeof(1024));
+       memset (writebuf, 'X', 1024);
        size=0;
        if(!sock_is_open) {
                if((sock=setup_connection(hostname, port, name, CONNECTION_TYPE_FULL, &serverflags))<0) {
@@ -380,8 +380,8 @@ int throughput_test(gchar* hostname, int port, char* name, int sock,
        }
        for(i=0;i+1024<=size;i+=1024) {
                if(do_write) {
-                       int sendfua = (testflags & TEST_FLUSH) && ((i & 15) == 3);
-                       int sendflush = (testflags & TEST_FLUSH) && ((i & 15) == 11);
+                       int sendfua = (testflags & TEST_FLUSH) && (((i>>10) & 15) == 3);
+                       int sendflush = (testflags & TEST_FLUSH) && (((i>>10) & 15) == 11);
                        req.type=htonl((testflags & TEST_WRITE)?NBD_CMD_WRITE:NBD_CMD_READ);
                        if (sendfua)
                                req.type = htonl(NBD_CMD_WRITE | NBD_CMD_FLAG_FUA);
@@ -473,7 +473,7 @@ int throughput_test(gchar* hostname, int port, char* name, int sock,
                speed=speed/1024.0;
                speedchar[0]='G';
        }
-       g_message("%d: Throughput %s test complete. Took %.3f seconds to complete, %.3f%sib/s", (int)getpid(), (testflags & TEST_WRITE)?"write":"read", timespan, speed, speedchar);
+       g_message("%d: Throughput %s test (%s flushes) complete. Took %.3f seconds to complete, %.3f%sib/s", (int)getpid(), (testflags & TEST_WRITE)?"write":"read", (testflags & TEST_FLUSH)?"with":"without", timespan, speed, speedchar);
 
 err_open:
        if(close_sock) {
index a01d3dc..0f12126 100755 (executable)
@@ -2,6 +2,8 @@
 # Yes, that's POSIX sh, not bash!
 
 tmpnam=`mktemp`
+conffile=${tmpnam}.conf
+pidfile=${tmpnam}.pid
 
 ulimit -c unlimited
 
@@ -13,7 +15,7 @@ echo $1
 case $1 in
        */cmd)
                # Test with export specified on command line
-               ./nbd-server -C /dev/null -p `pwd`/nbd-server.pid 11111 $tmpnam &
+               ./nbd-server -C /dev/null -p ${pidfile} 11111 $tmpnam &
                # -p only works if nbd-server wasn't compiled with -DNOFORK or
                # -DNODAEMON, which I sometimes do for testing and debugging.
                PID=$!
@@ -23,7 +25,7 @@ case $1 in
        ;;
        */cfgsize)
                # Test oversized requests
-               ./nbd-server -C /dev/null -p `pwd`/nbd-server.pid 11111 $tmpnam &
+               ./nbd-server -C /dev/null -p ${pidfile} 11111 $tmpnam &
                # -p only works if nbd-server wasn't compiled with -DNOFORK or
                # -DNODAEMON, which I sometimes do for testing and debugging.
                PID=$!
@@ -33,14 +35,14 @@ case $1 in
        ;;
        */cfg1)
                # Test with export specified in config file
-               cat > nbd-server.conf <<EOF
+               cat > ${conffile} <<EOF
 [generic]
        oldstyle = true
 [export]
        exportname = $tmpnam
        port = 11112
 EOF
-               ./nbd-server -C nbd-server.conf -p `pwd`/nbd-server.pid &
+               ./nbd-server -C ${conffile} -p ${pidfile} &
                PID=$!
                sleep 1
                ./nbd-tester-client 127.0.0.1 11112
@@ -49,7 +51,7 @@ EOF
        */cfgmulti)
                # Test with multiple exports specified in config file, and
                # testing more options too
-               cat >nbd-server.conf <<EOF
+               cat >${conffile} <<EOF
 [generic]
        oldstyle = true
 [export1]
@@ -63,23 +65,23 @@ EOF
        readonly = true
        listenaddr = 127.0.0.1
 EOF
-               ./nbd-server -C nbd-server.conf -p `pwd`/nbd-server.pid &
+               ./nbd-server -C ${conffile} -p ${pidfile} &
                PID=$!
                sleep 1
                ./nbd-tester-client localhost 11113
                retval=$?
                if [ $retval -ne 0 ]
                then
-                       if [ -f nbd-server.pid ]
+                       if [ -f ${pidfile} ]
                        then
-                               kill `cat nbd-server.pid`
-                               rm -f nbd-server.pid
+                               kill `cat ${pidfile}`
+                               rm -f ${pidfile}
                        else
                                kill $PID
                        fi
                        if [ -z "$2" ]
                        then
-                               rm -f $tmpnam nbd-server.conf
+                               rm -f $tmpnam ${conffile}
                        fi
                        exit $retval
                fi
@@ -88,38 +90,41 @@ EOF
        ;;
        */cfgnew)
                # Test new-style exports
-               cat >nbd-server.conf <<EOF
+               cat >${conffile} <<EOF
 [generic]
 [export1]
        exportname = $tmpnam
 EOF
-               ./nbd-server -C nbd-server.conf -p `pwd`/nbd-server.pid &
+               ./nbd-server -C ${conffile} -p ${pidfile} &
                PID=$!
                sleep 1
                ./nbd-tester-client localhost -N export1
                retval=$?
        ;;
        */write)
-               # Test new-style exports
-               cat >nbd-server.conf <<EOF
+               # Test writing
+               cat >${conffile} <<EOF
 [generic]
 [export1]
        exportname = $tmpnam
 EOF
-               ./nbd-server -C nbd-server.conf -p `pwd`/nbd-server.pid &
+               ./nbd-server -C ${conffile} -p ${pidfile} &
                PID=$!
                sleep 1
                ./nbd-tester-client localhost -N export1 -w
                retval=$?
        ;;
        */flush)
-               # Test new-style exports
-               cat >nbd-server.conf <<EOF
+               # Test writes with flush
+               cat >${conffile} <<EOF
 [generic]
 [export1]
        exportname = $tmpnam
+       flush = true
+       fua = true
+       rotational = true
 EOF
-               ./nbd-server -C nbd-server.conf -p `pwd`/nbd-server.pid &
+               ./nbd-server -C ${conffile} -p ${pidfile} &
                PID=$!
                sleep 1
                ./nbd-tester-client localhost -N export1 -w -f
@@ -130,16 +135,16 @@ EOF
                exit 1
        ;;
 esac
-if [ -f nbd-server.pid ]
+if [ -f ${pidfile} ]
 then
-       kill `cat nbd-server.pid`
-       rm -f nbd-server.pid
+       kill `cat ${pidfile}`
+       rm -f ${pidfile}
 else
        kill $PID
 fi
 if [ -z "$2" ]
 then
-       rm -f $tmpnam nbd-server.conf
+       rm -f $tmpnam ${conffile}
 fi
 if [ $retval -ne 0 ]
 then