ggate: Add support for O_DIRECT access

Adds support for controlling O_DIRECT access to ggated, ggatec, and
ggatel.

Reviewed by:	markj
Relnotes:	yes
MFC after:	1 month
Differential Revision:	https://reviews.freebsd.org/D45056
This commit is contained in:
David E. Cross 2024-09-21 03:22:57 -04:00 committed by Mark Johnston
parent e36af20691
commit 1b1e392aed
7 changed files with 192 additions and 52 deletions

View File

@ -34,6 +34,7 @@
.Op Fl n
.Op Fl v
.Op Fl o Cm ro | wo | rw
.Op Fl o Cm direct
.Op Fl p Ar port
.Op Fl q Ar queue_size
.Op Fl R Ar rcvbuf
@ -48,6 +49,7 @@
.Op Fl n
.Op Fl v
.Op Fl o Cm ro | wo | rw
.Op Fl o Cm direct
.Op Fl p Ar port
.Op Fl R Ar rcvbuf
.Op Fl S Ar sndbuf
@ -108,13 +110,21 @@ provider (cancels all pending requests).
Do not use
.Dv TCP_NODELAY
option on TCP sockets.
.It Fl o Cm ro | wo | rw
Specify permissions to use when opening the file or device: read-only
.Pq Cm ro ,
.It Fl o Ar option
Specify permissions and options to use when opening the file or device.
.Bl -tag -width indent
.It Cm ro
read-only
.It Cm wo
write-only
.Pq Cm wo ,
or read-write
.Pq Cm rw .
.It Cm rw
read-write
.It Cm direct
open with
.Dv O_DIRECT
option on the file
.El
.Pp
Default is
.Cm rw .
.It Fl p Ar port
@ -160,11 +170,14 @@ Use a CD-ROM device on a remote host.
.Bd -literal -offset indent
server# cat /etc/gg.exports
client RO /dev/cd0
client RW /tmp/image
server# ggated
client# ggatec create -o ro server /dev/cd0
ggate0
client# mount_cd9660 /dev/ggate0 /cdrom
client# ggatec create -o rw -o direct server /tmp/image
ggate1
.Ed
.Sh SEE ALSO
.Xr geom 4 ,

View File

@ -52,7 +52,6 @@
#include <netinet/tcp.h>
#include <arpa/inet.h>
#include <geom/gate/g_gate.h>
#include "ggate.h"
@ -62,6 +61,7 @@ static const char *path = NULL;
static const char *host = NULL;
static int unit = G_GATE_UNIT_AUTO;
static unsigned flags = 0;
static int direct_flag = 0;
static int force = 0;
static unsigned queue_size = G_GATE_QUEUE_SIZE;
static unsigned port = G_GATE_PORT;
@ -78,10 +78,12 @@ static void
usage(void)
{
fprintf(stderr, "usage: %s create [-nv] [-o <ro|wo|rw>] [-p port] "
fprintf(stderr, "usage: %s create [-nv] [-o <ro|wo|rw>] "
"[-o <direct>] [-p port] "
"[-q queue_size] [-R rcvbuf] [-S sndbuf] [-s sectorsize] "
"[-t timeout] [-u unit] <host> <path>\n", getprogname());
fprintf(stderr, " %s rescue [-nv] [-o <ro|wo|rw>] [-p port] "
fprintf(stderr, " %s rescue [-nv] [-o <ro|wo|rw>] "
"[-o <direct>] [-p port] "
"[-R rcvbuf] [-S sndbuf] <-u unit> <host> <path>\n", getprogname());
fprintf(stderr, " %s destroy [-f] <-u unit>\n", getprogname());
fprintf(stderr, " %s list [-v] [-u unit]\n", getprogname());
@ -361,7 +363,7 @@ handshake(int dir)
close(sfd);
return (-1);
}
cinit.gc_flags = flags | dir;
cinit.gc_flags = flags | direct_flag | dir;
cinit.gc_token = token;
cinit.gc_nconn = 2;
g_gate_swap2n_cinit(&cinit);
@ -585,6 +587,8 @@ main(int argc, char *argv[])
flags = G_GATE_FLAG_WRITEONLY;
else if (strcasecmp("rw", optarg) == 0)
flags = 0;
else if (strcasecmp("direct", optarg) == 0)
direct_flag = GGATE_FLAG_DIRECT;
else {
errx(EXIT_FAILURE,
"Invalid argument for '-o' option.");

View File

@ -85,10 +85,51 @@ An alternate location for the exports file.
.Pp
The format of an exports file is as follows:
.Bd -literal -offset indent
1.2.3.4 RO /dev/cd0
1.2.3.0/24 RW /tmp/test.img
hostname WO /tmp/image
1.2.3.4 RO /dev/cd0
1.2.3.0/24 RW /tmp/test.img
hostname WO /tmp/image
hostname RW,DIRECT /tmp/direct-image
hostname RW,NODIRECT /tmp/nodirect-image
.Ed
.Pp
The first colunm specifies the ip, network with netmask, or the hostname
that the export applies to.
.Pp
The next column is the access flags that apply to the export
.Bl -tag -width ".Cm NODIRECT"
.It Cm RO
Read-Only the path specified will be exported to the client read only.
.It Cm WO
Write-Only the path specified will be exported to the client write only.
.It Cm RW
Read-Write the path specified will be exported to the client read-write.
.It Cm DIRECT
The path specified will always be opened with O_DIRECT for clients.
.It Cm NODIRECT
The path specified will never be opened with O_DIRECT for clients.
.El
.Pp
The final column specifies the path to export.
.Pp
Files are opened with the least common flags between the client and the
server. A client may request read or write only to a read-write export
and the server will honor the client request and open the file in the
requested mode. A client requesting greater access than permissions listed
in the file will be rejected.
.Pp
DIRECT and NODIRECT are used to coerce the use of the O_DIRECT flag to
.Xr open 2 when the specified path is opened. If DIRECT is specified the
path is always opened with O_DIRECT. If NODIRECT is specified the path is
never opened with O_DIRECT. DIRECT access limits the cache effects of
IO operaions on the file. This has the effect of having clients accessing
exports to not impact the cache of the local machine, however it
will cause greater IO utilization to the devices on which the files reside.
.Pp
If neither is specified the server will use
the preference specified by the client, with the default to not use O_DIRECT.
If the client specifies a preference against the server's configuration the
client preference will be silently ignored.
.Pp
.Sh FILES
.Bl -tag -width ".Pa /var/run/ggated.pid" -compact
.It Pa /var/run/ggated.pid
@ -104,13 +145,18 @@ should be called with the
.Fl v
option.
.Sh EXAMPLES
Export CD-ROM device and a file:
Export CD-ROM device, a file, and a file with
.Dv O_DIRECT
option:
.Bd -literal -offset indent
# echo "1.2.3.0/24 RO /dev/cd0" > /etc/gg.exports
# echo "client RW /image" >> /etc/gg.exports
# echo "client RW,DIRECT /image2" >> /etc/gg.exports
# echo "client RW,NODIRECT /image3" >> /etc/gg.exports
# ggated
.Ed
.Sh SEE ALSO
.Xr open 2 ,
.Xr geom 4 ,
.Xr ggatec 8 ,
.Xr ggatel 8

View File

@ -64,7 +64,7 @@
struct ggd_connection {
off_t c_mediasize;
unsigned c_sectorsize;
unsigned c_flags; /* flags (RO/RW) */
int c_flags; /* flags (RO/RW) */
int c_diskfd;
int c_sendfd;
int c_recvfd;
@ -85,11 +85,18 @@ struct ggd_request {
#define r_length r_hdr.gh_length
#define r_error r_hdr.gh_error
#define EFLAGS_RDONLY 0x0000
#define EFLAGS_WRONLY 0x0001
#define EFLAGS_RDWR 0x0002
#define EFLAGS_ACCMODE 0x0003
#define EFLAGS_DIRECT 0x0004
#define EFLAGS_NODIRECT 0x0008
struct ggd_export {
char *e_path; /* path to device/file */
in_addr_t e_ip; /* remote IP address */
in_addr_t e_mask; /* IP mask */
unsigned e_flags; /* flags (RO/RW) */
int e_flags; /* flags (WO/RO/RW/DIRECT/NODIRECT) */
SLIST_ENTRY(ggd_export) e_next;
};
@ -146,20 +153,61 @@ countmask(unsigned m)
return (mask);
}
static int
parse_flags(const char *flagsstr, int lineno)
{
char *flagscpy;
char *word, *brkf;
int access_flags = -1;
int direct_flags = 0;
flagscpy = strdup(flagsstr);
if (flagscpy == NULL) {
g_gate_xlog("Not enough memory.");
}
for (word = strtok_r(flagscpy, ",", &brkf);
word != NULL;
word = strtok_r(NULL, ",", &brkf)) {
if (strcasecmp("ro", word) == 0 ||
strcasecmp("rd", word) == 0) {
access_flags = EFLAGS_RDONLY;
} else if (strcasecmp("wo", word) == 0) {
access_flags = EFLAGS_WRONLY;
} else if (strcasecmp("rw", word) == 0) {
access_flags = EFLAGS_RDWR;
} else if (strcasecmp("direct", word) == 0) {
direct_flags = EFLAGS_DIRECT;
} else if (strcasecmp("nodirect", word) == 0) {
direct_flags = EFLAGS_NODIRECT;
} else {
g_gate_xlog("Invalid value (%s) in flags field at "
"line %u.", word, lineno);
}
}
free(flagscpy);
if (access_flags == -1) {
g_gate_xlog("Invalid value (%s) in flags field at "
"line %u.", flagsstr, lineno);
}
return (direct_flags | access_flags);
}
static void
line_parse(char *line, unsigned lineno)
{
struct ggd_export *ex;
char *word, *path, *sflags;
unsigned flags, i, vmask;
char *word, *path, *sflags, *brkl;
unsigned i, vmask;
int flags;
in_addr_t ip, mask;
ip = mask = flags = vmask = 0;
path = NULL;
sflags = NULL;
for (i = 0, word = strtok(line, " \t"); word != NULL;
i++, word = strtok(NULL, " \t")) {
for (i = 0, word = strtok_r(line, " \t", &brkl); word != NULL;
i++, word = strtok_r(NULL, " \t", &brkl)) {
switch (i) {
case 0: /* IP address or host name */
ip = g_gate_str2ip(strsep(&word, "/"));
@ -185,17 +233,7 @@ line_parse(char *line, unsigned lineno)
mask = countmask(vmask);
break;
case 1: /* flags */
if (strcasecmp("rd", word) == 0 ||
strcasecmp("ro", word) == 0) {
flags = O_RDONLY;
} else if (strcasecmp("wo", word) == 0) {
flags = O_WRONLY;
} else if (strcasecmp("rw", word) == 0) {
flags = O_RDWR;
} else {
g_gate_xlog("Invalid value in flags field at "
"line %u.", lineno);
}
flags = parse_flags(word, lineno);
sflags = word;
break;
case 2: /* path */
@ -306,13 +344,16 @@ exports_check(struct ggd_export *ex, struct g_gate_cinit *cinit,
struct ggd_connection *conn)
{
char ipmask[32]; /* 32 == strlen("xxx.xxx.xxx.xxx/xxx.xxx.xxx.xxx")+1 */
int error = 0, flags;
int error = 0, flags, access_flags, direct_flags = 0;
strlcpy(ipmask, ip2str(ex->e_ip), sizeof(ipmask));
strlcat(ipmask, "/", sizeof(ipmask));
strlcat(ipmask, ip2str(ex->e_mask), sizeof(ipmask));
access_flags = ex->e_flags & EFLAGS_ACCMODE;
if ((cinit->gc_flags & GGATE_FLAG_RDONLY) != 0) {
if (ex->e_flags == O_WRONLY) {
if (access_flags == EFLAGS_WRONLY) {
g_gate_log(LOG_WARNING, "Read-only access requested, "
"but %s (%s) is exported write-only.", ex->e_path,
ipmask);
@ -321,7 +362,7 @@ exports_check(struct ggd_export *ex, struct g_gate_cinit *cinit,
conn->c_flags |= GGATE_FLAG_RDONLY;
}
} else if ((cinit->gc_flags & GGATE_FLAG_WRONLY) != 0) {
if (ex->e_flags == O_RDONLY) {
if (access_flags == EFLAGS_RDONLY) {
g_gate_log(LOG_WARNING, "Write-only access requested, "
"but %s (%s) is exported read-only.", ex->e_path,
ipmask);
@ -330,24 +371,41 @@ exports_check(struct ggd_export *ex, struct g_gate_cinit *cinit,
conn->c_flags |= GGATE_FLAG_WRONLY;
}
} else {
if (ex->e_flags == O_RDONLY) {
if (access_flags == EFLAGS_RDONLY) {
g_gate_log(LOG_WARNING, "Read-write access requested, "
"but %s (%s) is exported read-only.", ex->e_path,
ipmask);
return (EPERM);
} else if (ex->e_flags == O_WRONLY) {
} else if (access_flags == EFLAGS_WRONLY) {
g_gate_log(LOG_WARNING, "Read-write access requested, "
"but %s (%s) is exported write-only.", ex->e_path,
ipmask);
return (EPERM);
}
}
if ((cinit->gc_flags & GGATE_FLAG_DIRECT) != 0) {
if (ex->e_flags & EFLAGS_NODIRECT) {
g_gate_log(LOG_WARNING, "Direct IO requested, "
"but %s (%s) is exported NODIRECT.", ex->e_path,
ipmask);
} else {
conn->c_flags |= GGATE_FLAG_DIRECT;
direct_flags = O_DIRECT;
}
}
if (ex->e_flags & EFLAGS_DIRECT) {
direct_flags = O_DIRECT;
}
if ((conn->c_flags & GGATE_FLAG_RDONLY) != 0)
flags = O_RDONLY;
else if ((conn->c_flags & GGATE_FLAG_WRONLY) != 0)
flags = O_WRONLY;
else
flags = O_RDWR;
flags |= direct_flags;
if (conn->c_diskfd != -1) {
if (strcmp(conn->c_path, ex->e_path) != 0) {
g_gate_log(LOG_ERR, "old %s and new %s: "

View File

@ -32,7 +32,7 @@
.Nm
.Cm create
.Op Fl v
.Op Fl o Cm ro | wo | rw
.Oo Fl o option Oc ...
.Op Fl s Ar sectorsize
.Op Fl t Ar timeout
.Op Fl u Ar unit
@ -48,7 +48,7 @@
.Nm
.Cm rescue
.Op Fl v
.Op Fl o Cm ro | wo | rw
.Oo Fl o option Oc ...
.Fl u Ar unit
.Ar path
.Sh DESCRIPTION
@ -92,13 +92,21 @@ Available options:
Forcibly destroy
.Nm ggate
provider (cancels all pending requests).
.It Fl o Cm ro | wo | rw
Specify permissions to use when opening the file or device: read-only
.Pq Cm ro ,
.It Fl o Ar option
Specify permissions and options to use when opening the file or device.
.Bl -tag -width indent
.It Cm ro
read-only
.It Cm wo
write-only
.Pq Cm wo ,
or read-write
.Pq Cm rw .
.It Cm rw
read-write
.It Cm direct
open with
.Dv O_DIRECT
option on the file
.El
.Pp
Default is
.Cm rw .
.It Fl s Ar sectorsize

View File

@ -43,7 +43,6 @@
#include <sys/stat.h>
#include <sys/syslog.h>
#include <geom/gate/g_gate.h>
#include "ggate.h"
@ -52,6 +51,7 @@ static enum { UNSET, CREATE, DESTROY, LIST, RESCUE } action = UNSET;
static const char *path = NULL;
static int unit = G_GATE_UNIT_AUTO;
static unsigned flags = 0;
static int direct_flag = 0;
static int force = 0;
static unsigned sectorsize = 0;
static unsigned timeout = G_GATE_TIMEOUT;
@ -60,24 +60,30 @@ static void
usage(void)
{
fprintf(stderr, "usage: %s create [-v] [-o <ro|wo|rw>] "
fprintf(stderr, "usage: %s create [-v] [-o option] ... "
"[-s sectorsize] [-t timeout] [-u unit] <path>\n", getprogname());
fprintf(stderr, " %s rescue [-v] [-o <ro|wo|rw>] <-u unit> "
fprintf(stderr, " %s rescue [-v] [-o option] ... <-u unit> "
"<path>\n", getprogname());
fprintf(stderr, " %s destroy [-f] <-u unit>\n", getprogname());
fprintf(stderr, " %s list [-v] [-u unit]\n", getprogname());
fprintf(stderr, " option = {ro, wo, rw, direct}\n");
exit(EXIT_FAILURE);
}
static int
g_gate_openflags(unsigned ggflags)
{
int openflags = O_RDWR;
if ((ggflags & G_GATE_FLAG_READONLY) != 0)
return (O_RDONLY);
openflags = O_RDONLY;
else if ((ggflags & G_GATE_FLAG_WRITEONLY) != 0)
return (O_WRONLY);
return (O_RDWR);
openflags = O_WRONLY;
if (direct_flag)
openflags |= O_DIRECT;
return (openflags);
}
static void
@ -248,6 +254,8 @@ main(int argc, char *argv[])
flags = G_GATE_FLAG_WRITEONLY;
else if (strcasecmp("rw", optarg) == 0)
flags = 0;
else if (strcasecmp("direct", optarg) == 0)
direct_flag = 1;
else {
errx(EXIT_FAILURE,
"Invalid argument for '-o' option.");

View File

@ -29,6 +29,7 @@
#ifndef _GGATE_H_
#define _GGATE_H_
#include <geom/gate/g_gate.h>
#include <sys/endian.h>
#include <stdarg.h>
@ -42,8 +43,8 @@
#define GGATE_MAGIC "GEOM_GATE "
#define GGATE_VERSION 0
#define GGATE_FLAG_RDONLY 0x0001
#define GGATE_FLAG_WRONLY 0x0002
#define GGATE_FLAG_RDONLY G_GATE_FLAG_READONLY
#define GGATE_FLAG_WRONLY G_GATE_FLAG_WRITEONLY
/*
* If neither the GGATE_FLAG_SEND nor the GGATE_FLAG_RECV flag is
* set - this is initial connection.
@ -53,6 +54,8 @@
#define GGATE_FLAG_SEND 0x0004
#define GGATE_FLAG_RECV 0x0008
#define GGATE_FLAG_DIRECT 0x0010
#define GGATE_CMD_READ 0
#define GGATE_CMD_WRITE 1
#define GGATE_CMD_FLUSH 3