Browse Source

Import upstream version 14

Sam Hopkins 12 years ago
parent
commit
1886dfdb73
12 changed files with 473 additions and 113 deletions
  1. 25 0
      NEWS
  2. 53 3
      README
  3. 157 66
      aoe.c
  4. 42 34
      ata.c
  5. 2 0
      config.h
  6. 18 0
      contrib/README
  7. 135 0
      contrib/o_direct.diff
  8. 4 4
      dat.h
  9. 3 1
      fns.h
  10. 6 0
      freebsd.c
  11. 15 2
      linux.c
  12. 13 3
      vblade.8

+ 25 - 0
NEWS

@@ -1,4 +1,29 @@
 -*- change-log -*-
+2006-11-20 Sam Hopkins <sah@coraid.com>
+	apply contrib jumbo patch to standard distribution
+	add jumbo configuration app. note in README
+	add jumbo README reference to manpage
+	add mask feature; -m flag
+	update manpage to describe -m flag
+	vblade-14
+
+2006-10-05 Sam Hopkins <sah@coraid.com>
+	fix confcmd memcpy bug
+	correct scnt return value in read/write ata response
+	replace O_RDONLY fallback with explicit stat.  root always wins.
+	vblade-13
+
+2006-10-04 Sam Hopkins <sah@coraid.com>
+	fix confcmd buglets
+	fix atacmd buglets
+	add atacmd handling for bad argument errors
+	add O_RDONLY open if O_RDWR fails
+	add contrib patch directory
+	add contrib/README
+	add jumbo patch to contrib
+	add o_direct patch to contrib
+	vblade-12
+
 2006-09-21 "Adam J. Richter" <adam@yggdrasil.com>
 	add install target for makefile
 	vblade-11

+ 53 - 3
README

@@ -1,3 +1,7 @@
+
+INTRODUCTION
+------------
+
 The vblade is the virtual EtherDrive (R) blade, a program that makes a
 seekable file available over an ethernet local area network (LAN) via
 the ATA over Ethernet (AoE) protocol.
@@ -7,6 +11,9 @@ regular files will work.  When vblade exports the block storage over
 AoE it becomes a storage target.  Another host on the same LAN can
 access the storage if it has a compatible aoe kernel driver.
 
+BUILDING
+--------
+
 The following command should build the vblade program on a Linux-based
 system:
 
@@ -16,6 +23,9 @@ For FreeBSD systems, include an extra parameter like so:
 
   make PLATFORM=freebsd
 
+EXAMPLES
+--------
+
 There is a "vbladed" script that daemonizes the program and sends its
 output to the logger program.  Make sure you have logger installed if
 you would like to run vblade as a daemon with the vbladed script.
@@ -75,12 +85,52 @@ another host on the LAN.
 
 Remember: be as careful with these devices as you would with /dev/hda!
 
+Jumbo Frame Compatibility
+-------------------------
+
+Vblade can use jumbo frames provided your initiator is jumbo frame
+capable.  There is one small configuration gotcha to consider 
+to avoid having the vblade kernel frequently drop frames.
+
+Vblade uses a raw socket to perform AoE.  The linux kernel will
+only buffer a certain amount of data for a raw socket.  For 2.6
+kernels, this value is managed through /proc:
+
+root@nai aoe# grep . /proc/sys/net/core/rmem_*
+/proc/sys/net/core/rmem_default:128000
+/proc/sys/net/core/rmem_max:128000
+
+rmem_max is the max amount a user process may expand the receive
+buffer to -- through setsockopt(...) -- and rmem_default is, as you
+might expect, the default.
+
+The gotcha is that this amount to buffer does not relate
+to the amount of user data buffered, but the amount of
+real data buffered.  As an example, the Intel GbE controller
+must be given 16KB frames to use an MTU over 8KB.
+For each received frame, the kernel must be able to buffer
+16KB, even if the aoe frame is only 60 bytes in length.
+
+The linux aoe initiator will use 16 outstanding frames when
+used with vblade.  A good default for ensuring frames are
+not dropped is to allocate 16KB for 17 frames:
+
+for f in /proc/sys/net/core/rmem_*; do echo $((17 * 16 * 1024)) >$f; done
+
+Be sure to start vblade after changing the buffering defaults
+as the buffer value is set when the socket is opened.
+
 AoE Initiator Compatibility
 ---------------------------
 
-  The Linux aoe driver for the 2.6 kernel is compatible if you use
-  aoe-2.6-7 or newer.  You can use older aoe drivers but you will only
-  be able to see one vblade per MAC address.
+The Linux aoe driver for the 2.6 kernel is compatible if you use
+aoe-2.6-7 or newer.  You can use older aoe drivers but you will only
+be able to see one vblade per MAC address.
+
+Contrib Patches
+---------------
+
+see contrib/README
 
 Kvblade
 -------

+ 157 - 66
aoe.c

@@ -5,39 +5,30 @@
 #include <stdlib.h>
 #include <unistd.h>
 #include <sys/types.h>
+#include <sys/stat.h>
 #include <fcntl.h>
 #include <netinet/in.h>
 #include "dat.h"
 #include "fns.h"
 
-char cfg_str[1024];
-int cfg_str_len = 0;
+enum {
+	Nmasks= 32,
+	Alen= 6,
+};
 
-int
-qcget(uchar *p, int len)
-{
-	if (len > cfg_str_len)
-		len = cfg_str_len;
-	memcpy(p, cfg_str, len);
-	return len;
-}
-
-int
-qcset(uchar *p, int len)
-{
-	if (len > sizeof(cfg_str))
-		len = sizeof(cfg_str);
-	memcpy(cfg_str, p, len);
-	cfg_str_len = len;
-	return len;
-}
+uchar masks[Nmasks*Alen];
+int nmasks;
+char config[Nconfig];
+int nconfig = 0;
+int maxscnt = 2;
+char *ifname;
 
 void
 aoead(int fd)			// advertise the virtual blade
 {
-	int len;
 	uchar buf[2000];
 	Conf *p;
+	int i;
 
 	p = (Conf *)buf;
 	memset(p, 0, sizeof *p);
@@ -49,12 +40,21 @@ aoead(int fd)			// advertise the virtual blade
 	p->h.min = slot;
 	p->h.cmd = Config;
 	p->bufcnt = htons(Bufcount);
+	p->scnt = maxscnt = (getmtu(sfd, ifname) - sizeof (Ata)) / 512;
 	p->firmware = htons(FWV);
 	p->vercmd = 0x10 | Qread;
-	len = qcget(p->data, 1024);
-	p->len = htons(len);
-	if (putpkt(fd, buf, sizeof *p - sizeof p->data + len) == -1)
+	memcpy(p->data, config, nconfig);
+	p->len = htons(nconfig);
+	if (nmasks == 0)
+	if (putpkt(fd, buf, sizeof *p - sizeof p->data + nconfig) == -1) {
 		perror("putpkt aoe id");
+		return;
+	}
+	for (i=0; i<nmasks; i++) {
+		memcpy(p->h.dst, &masks[i*Alen], Alen);
+		if (putpkt(fd, buf, sizeof *p - sizeof p->data + nconfig) == -1)
+			perror("putpkt aoe id");
+	}
 }
 
 int
@@ -82,68 +82,77 @@ aoeata(Ata *p)	// do ATA reqeust
 {
 	Ataregs r;
 	int len = 60;
+	int n;
 
-	if (p->sectors && (p->aflag & Write) == 0)
-		len = sizeof (Ata);
 	r.lba = getlba(p->lba);
 	r.sectors = p->sectors;
 	r.feature = p->err;
 	r.cmd = p->cmd;
-	atacmd(&r, p->data);
+	if (atacmd(&r, (uchar *)(p+1), maxscnt*512) < 0) {
+		p->h.flags |= Error;
+		p->h.error = BadArg;
+		return len;
+	}
+	if (!(p->aflag & Write))
+	if ((n = p->sectors)) {
+		n -= r.sectors;
+		len = sizeof (Ata) + (n*512);
+	}
 	p->sectors = r.sectors;
 	p->err = r.err;
 	p->cmd = r.status;
 	return len;
 }
 
+#define QCMD(x) ((x)->vercmd & 0xf)
+
+// yes, this makes unnecessary copies.
+
 int
 confcmd(Conf *p)	// process conf request
 {
-	uchar buf[1024];
-	int len = 0, len2;
+	int len;
 
-	switch (p->vercmd & 0xf) {
-	case Qread:
-		len = qcget(p->data, 1024);
-		p->len = htons(len);
-		break;
+	len = ntohs(p->len);
+	if (QCMD(p) != Qread)
+	if (len > Nconfig)
+		return 0;	// if you can't play nice ...
+	switch (QCMD(p)) {
 	case Qtest:
-		len = qcget(buf, 1024);
-		if (len != ntohs(p->len))
+		if (len != nconfig)
 			return 0;
-		if (memcmp(buf, p->data, len) != 0)
-			return 0;
-		memmove(p->data, buf, len);
-		break;
+		// fall thru
 	case Qprefix:
-		len = qcget(buf, 1024);
-		len2 = ntohs(p->len);
-		if (len2 > len)
+		if (len > nconfig)
 			return 0;
-		if (memcmp(buf, p->data, len2) != 0)
+		if (memcmp(config, p->data, len))
 			return 0;
-		memmove(p->data, buf, len);
-		p->len = htons(len);
+		// fall thru
+	case Qread:
 		break;
 	case Qset:
-		len = qcget(buf, 1024);
-		if (len > 0 && (len != p->len || memcmp(buf, p->data, len))) {
+		if (nconfig)
+		if (nconfig != len || memcmp(config, p->data, len)) {
 			p->h.flags |= Error;
 			p->h.error = ConfigErr;
 			break;
 		}
 		// fall thru
 	case Qfset:
-		len = ntohs(p->len);
-		qcset(p->data, len);
+		nconfig = len;
+		memcpy(config, p->data, nconfig);
 		break;
 	default:
-		p->h.flags |= BadArg;
+		p->h.flags |= Error;
+		p->h.error = BadArg;
 	}
+	memmove(p->data, config, nconfig);
+	p->len = htons(nconfig);
 	p->bufcnt = htons(Bufcount);
+	p->scnt = maxscnt = (getmtu(sfd, ifname) - sizeof (Ata)) / 512;
 	p->firmware = htons(FWV);
-	p->vercmd |= 0x10;
-	return len + sizeof(Conf) - 1024;
+	p->vercmd = 0x10 | QCMD(p);	// aoe v.1
+	return nconfig + sizeof *p - sizeof p->data;
 }
 
 void
@@ -156,7 +165,8 @@ doaoe(Aoehdr *p)
 		len = aoeata((Ata*)p);
 		break;
 	case Config:
-		if ((len = confcmd((Conf *)p)) == 0)
+		len = confcmd((Conf *)p);
+		if (len == 0)
 			return;
 		break;
 	default:
@@ -179,20 +189,22 @@ void
 aoe(void)
 {
 	Aoehdr *p;
-	uchar buf[1400];
+	uchar *buf;
 	int n, sh;
+	enum { bufsz = 1<<16, };
 
+	buf = malloc(bufsz);
 	aoead(sfd);
 
 	for (;;) {
-		n = getpkt(sfd, buf, sizeof buf);
+		n = getpkt(sfd, buf, bufsz);
 		if (n < 0) {
 			perror("read network");
 			exit(1);
 		}
 		if (n < 60)
 			continue;
-		p = (Aoehdr *)buf;
+		p = (Aoehdr *) buf;
 		if (ntohs(p->type) != 0x88a2)
 			continue;
 		if (p->flags & Resp)
@@ -202,40 +214,119 @@ aoe(void)
 			continue;
 		if (p->min != slot && p->min != (uchar)~0)
 			continue;
+		if (nmasks && !maskok(p->src))
+			continue;
 		doaoe(p);
 	}
+	free(buf);
 }
 
 void
 usage(void)
 {
-	fprintf(stderr, "usage: %s <shelf> <slot> <ethn> <device>\n", 
+	fprintf(stderr, "usage: %s [ -m mac[,mac...] ] shelf slot netif filename\n", 
 		progname);
 	exit(1);
 }
 
+/* parseether from plan 9 */
+int
+parseether(uchar *to, char *from)
+{
+	char nip[4];
+	char *p;
+	int i;
+
+	p = from;
+	for(i = 0; i < 6; i++){
+		if(*p == 0)
+			return -1;
+		nip[0] = *p++;
+		if(*p == 0)
+			return -1;
+		nip[1] = *p++;
+		nip[2] = 0;
+		to[i] = strtoul(nip, 0, 16);
+		if(*p == ':')
+			p++;
+	}
+	return 0;
+}
+
+void
+setmask(char *ml)
+{
+	char *p;
+	int n;
+
+	for (; ml; ml=p) {
+		p = strchr(ml, ',');
+		if (p)
+			*p++ = '\0';
+		n = parseether(&masks[nmasks*Alen], ml);
+		if (n < 0)
+			fprintf(stderr, "ignoring mask %s, parseether failure\n", ml);
+		else
+			nmasks++;
+	}
+}
+
+int
+maskok(uchar *ea)
+{
+	int i, ok = 0;
+
+	for (i=0; !ok && i<nmasks; i++)
+		ok = memcmp(ea, &masks[i*Alen], Alen) == 0;
+	return ok;
+}
+
 int
 main(int argc, char **argv)
 {
+	int ch, omode = O_RDONLY;
+	struct stat s;
+
 	setbuf(stdin, NULL);
 	atainit();
 	progname = *argv;
-	if (argc != 5)
+	while ((ch = getopt(argc, argv, "m:")) != -1) {
+		switch (ch) {
+		case 'm':
+			setmask(optarg);
+			break;
+		case '?':
+		default:
+			usage();
+		}
+	}
+	argc -= optind;
+	argv += optind;
+	if (argc != 4)
 		usage();
-	bfd = open(argv[4], O_RDWR);
+	if (stat(argv[3], &s) < 0) {
+		perror("stat");
+		exit(1);
+	}
+	if (s.st_mode & (S_IWUSR|S_IWGRP|S_IWOTH))
+		omode = O_RDWR;
+	bfd = open(argv[3], omode);
 	if (bfd == -1) {
 		perror("open");
 		exit(1);
 	}
-	shelf = atoi(argv[1]);
-	slot = atoi(argv[2]);
+	shelf = atoi(argv[0]);
+	slot = atoi(argv[1]);
 	size = getsize(bfd);
 	size /= 512;
-	sfd = dial(argv[3]);
-	getea(sfd, argv[3], mac);
-	printf("pid %ld: e%d.%d, %lld sectors\n",
-		(long) getpid(), shelf, slot, size);
+	ifname = argv[2];
+	sfd = dial(ifname);
+	getea(sfd, ifname, mac);
+	printf("pid %ld: e%d.%d, %lld sectors %s\n",
+		(long) getpid(), shelf, slot, size,
+		omode == O_RDWR ? "O_RDWR" : "O_RDONLY");
 	fflush(stdout);
 	aoe();
 	return 0;
 }
+

+ 42 - 34
ata.c

@@ -18,7 +18,7 @@ enum {
 	// status bits
 	BSY =	1<<7,
 	DRDY =	1<<6,
-	DF =		1<<5,
+	DF =	1<<5,
 	DRQ =	1<<3,
 	ERR =	1<<0,
 };
@@ -97,26 +97,26 @@ atainit(void)
  * with LBA 28 you shouldn't see an LBA of all ones.  Still, we don't
  * check for that.
  */
-void
-atacmd(Ataregs *p, uchar *dp)		// do the ata cmd
+int
+atacmd(Ataregs *p, uchar *dp, int ndp)		// do the ata cmd
 {
 	vlong lba;
 	ushort *ip;
 	int n;
 	enum { MAXLBA28SIZE = 0x0fffffff };
+	extern int maxscnt;
 
+	p->status = 0;
 	switch (p->cmd) {
-	case 0x20:		// read sectors
-	case 0x30:		// write sectors
-		lba = p->lba & MAXLBA28SIZE;
-		break;
-	case 0x24:		// read sectors ext
-	case 0x34:		// write sectors ext
-		lba = p->lba & 0x0000ffffffffffffLL;	// full 48
-		break;
+	default:
+		p->status = DRDY | ERR;
+		p->err = ABRT;
+		return 0;
 	case 0xe7:		// flush cache
-		return;
+		return 0;
 	case 0xec:		// identify device
+		if (p->sectors != 1 || ndp < 512)
+			return -1;
 		memmove(dp, ident, 512);
 		ip = (ushort *)dp;
 		if (size & ~MAXLBA28SIZE)
@@ -126,39 +126,47 @@ atacmd(Ataregs *p, uchar *dp)		// do the ata cmd
 		setlba48(ip, size);
 		p->err = 0;
 		p->status = DRDY;
-		return;
+		p->sectors = 0;
+		return 0;
 	case 0xe5:		// check power mode
 		p->err = 0;
 		p->sectors = 0xff; // the device is active or idle
 		p->status = DRDY;
-		return;
-	default:
-		p->status = DRDY | ERR;
-		p->err = ABRT;
-		return;
+		return 0;
+	case 0x20:		// read sectors
+	case 0x30:		// write sectors
+		lba = p->lba & MAXLBA28SIZE;
+		break;
+	case 0x24:		// read sectors ext
+	case 0x34:		// write sectors ext
+		lba = p->lba & 0x0000ffffffffffffLL;	// full 48
+		break;
 	}
 
+	// we ought not be here unless we are a read/write
+
+	if (p->sectors > maxscnt || p->sectors*512 > ndp)
+		return -1;
+
 	if (lba + p->sectors > size) {
 		p->err = IDNF;
 		p->status = DRDY | ERR;
 		p->lba = lba;
-		return;
+		return 0;
 	}
-	if (p->cmd == 0x20 || p->cmd == 0x24) {
+	if (p->cmd == 0x20 || p->cmd == 0x24)
 		n = getsec(bfd, dp, lba, p->sectors);
-		if (n == -1) {
-			perror("read block device");
-			p->status |= ERR;
-		}
-	} else {
+	else
 		n = putsec(bfd, dp, lba, p->sectors);
-		if (n == -1) {
-			perror("write block device");
-			p->status |= ERR;
-		}
-	}
-	p->lba += p->sectors;
-	p->sectors = 0;
-	p->status = DRDY;
-	p->err = 0;
+	n /= 512;
+	if (n != p->sectors) {
+		p->err = ABRT;
+		p->status = ERR;
+	} else
+		p->err = 0;
+	p->status |= DRDY;
+	p->lba += n;
+	p->sectors -= n;
+	return 0;
 }
+

+ 2 - 0
config.h

@@ -0,0 +1,2 @@
+#define _FILE_OFFSET_BITS 64 
+typedef unsigned long long u64;

+ 18 - 0
contrib/README

@@ -0,0 +1,18 @@
+
+The patches in the contrib directory enable features
+that either don't work completely, aren't well tested, or
+are of limited general use.  They can be applied by 
+using patch in the vblade source directory as follows:
+
+forfeit:~/vblade-12 # patch -p1 <contrib/jumbo.diff
+patching file aoe.c
+patching file fns.h
+patching file freebsd.c
+patching file linux.c
+forfeit:~/vblade-12 # 
+
+contrib/o_direct.diff:
+
+Enable O_DIRECT on linux to minimize cache effects of the I/O to
+and from the file backing the vblade.
+

+ 135 - 0
contrib/o_direct.diff

@@ -0,0 +1,135 @@
+diff -uprN vblade-14rc1/aoe.c vblade-14rc2/aoe.c
+--- vblade-14rc1/aoe.c	2006-11-20 12:48:05.000000000 -0500
++++ vblade-14rc2/aoe.c	2006-11-20 13:14:04.000000000 -0500
+@@ -185,15 +185,42 @@ doaoe(Aoehdr *p)
+ 	}
+ }
+ 
++// allocate the buffer so that the ata data area
++// is page aligned for o_direct on linux
++
++void *
++bufalloc(uchar **buf, long len)
++{
++	long psize;
++	unsigned long n;
++
++	psize = sysconf(_SC_PAGESIZE);
++	if (psize == -1) {
++		perror("sysconf");
++		exit(EXIT_FAILURE);
++	}
++	n = len/psize + 3;
++	*buf = malloc(psize * n);
++	if (!*buf) {
++		perror("malloc");
++		exit(EXIT_FAILURE);
++	}
++	n = (unsigned long) *buf;
++	n += psize * 2;
++	n &= ~(psize - 1);
++	return (void *) (n - sizeof (Ata));
++}
++
+ void
+ aoe(void)
+ {
+ 	Aoehdr *p;
++	uchar *freeme;
+ 	uchar *buf;
+ 	int n, sh;
+ 	enum { bufsz = 1<<16, };
+ 
+-	buf = malloc(bufsz);
++	buf = bufalloc(&freeme, bufsz);
+ 	aoead(sfd);
+ 
+ 	for (;;) {
+@@ -218,7 +245,7 @@ aoe(void)
+ 			continue;
+ 		doaoe(p);
+ 	}
+-	free(buf);
++	free(freeme);
+ }
+ 
+ void
+@@ -310,7 +337,7 @@ main(int argc, char **argv)
+ 	}
+ 	if (s.st_mode & (S_IWUSR|S_IWGRP|S_IWOTH))
+ 		omode = O_RDWR;
+-	bfd = open(argv[3], omode);
++	bfd = opendisk(argv[3], omode);
+ 	if (bfd == -1) {
+ 		perror("open");
+ 		exit(1);
+diff -uprN vblade-14rc1/fns.h vblade-14rc2/fns.h
+--- vblade-14rc1/fns.h	2006-11-13 16:12:31.000000000 -0500
++++ vblade-14rc2/fns.h	2006-11-20 13:13:44.000000000 -0500
+@@ -21,6 +21,7 @@ int	atacmd(Ataregs *, uchar *, int);
+ 
+ int	dial(char *);
+ int	getea(int, char *, uchar *);
++int	opendisk(const char *, int);
+ int	putsec(int, uchar *, vlong, int);
+ int	getsec(int, uchar *, vlong, int);
+ int	putpkt(int, uchar *, int);
+diff -uprN vblade-14rc1/freebsd.c vblade-14rc2/freebsd.c
+--- vblade-14rc1/freebsd.c	2006-11-13 13:57:34.000000000 -0500
++++ vblade-14rc2/freebsd.c	2006-11-20 13:13:44.000000000 -0500
+@@ -241,6 +241,11 @@ getea(int s, char *eth, uchar *ea)
+ 	return(0);
+ }
+ 
++int
++opendisk(const char *disk, int omode)
++{
++	return open(disk, omode);
++}
+ 
+ int
+ getsec(int fd, uchar *place, vlong lba, int nsec)
+diff -uprN vblade-14rc1/linux.c vblade-14rc2/linux.c
+--- vblade-14rc1/linux.c	2006-11-13 13:57:34.000000000 -0500
++++ vblade-14rc2/linux.c	2006-11-20 13:13:44.000000000 -0500
+@@ -1,5 +1,6 @@
+ // linux.c: low level access routines for Linux
+ #include "config.h"
++#define _GNU_SOURCE
+ #include <sys/socket.h>
+ #include <stdio.h>
+ #include <string.h>
+@@ -22,6 +23,7 @@
+ #include <netinet/in.h>
+ #include <linux/fs.h>
+ #include <sys/stat.h>
++#include <fcntl.h>
+ 
+ #include "dat.h"
+ #include "fns.h"
+@@ -102,17 +104,21 @@ getmtu(int s, char *name)
+ }
+ 
+ int
++opendisk(const char *disk, int omode)
++{
++	return open(disk, omode|O_DIRECT);
++}
++
++int
+ getsec(int fd, uchar *place, vlong lba, int nsec)
+ {
+-	lseek(fd, lba * 512, 0);
+-	return read(fd, place, nsec * 512);
++	return pread(fd, place, nsec * 512, lba * 512);
+ }
+ 
+ int
+ putsec(int fd, uchar *place, vlong lba, int nsec)
+ {
+-	lseek(fd, lba * 512, 0);
+-	return write(fd, place, nsec * 512);
++	return pwrite(fd, place, nsec * 512, lba * 512);
+ }
+ 
+ int

+ 4 - 4
dat.h

@@ -6,7 +6,7 @@
  */
 
 enum {
-	VBLADE_VERSION		= 11,
+	VBLADE_VERSION		= 12,
 
 	// Firmware version
 	FWV			= 0x4000 + VBLADE_VERSION,
@@ -67,7 +67,6 @@ struct Ata
 	uchar	cmd;
 	uchar	lba[6];
 	uchar	resvd[2];
-	uchar	data[1024];
 };
 
 struct Conf
@@ -75,7 +74,7 @@ struct Conf
 	Aoehdr	h;
 	ushort	bufcnt;
 	ushort	firmware;
-	uchar	filler;
+	uchar	scnt;
 	uchar	vercmd;
 	ushort	len;
 	uchar	data[1024];
@@ -109,8 +108,9 @@ enum {
 	Qfset,
 
 	Nretries = 3,
+	Nconfig = 1024,
 
-	Bufcount = 32,
+	Bufcount = 16,
 };
 
 int	shelf, slot;

+ 3 - 1
fns.h

@@ -10,11 +10,12 @@ void	aoead(int);
 void	aoeflush(int, int);
 void	aoetick(void);
 void	aoerequest(int, int, vlong, int, uchar *, int);
+int	maskok(uchar *);
 
 // ata.c
 
 void	atainit(void);
-void	atacmd(Ataregs *, uchar *);
+int	atacmd(Ataregs *, uchar *, int);
 
 // os specific
 
@@ -25,3 +26,4 @@ int	getsec(int, uchar *, vlong, int);
 int	putpkt(int, uchar *, int);
 int	getpkt(int, uchar *, int);
 vlong	getsize(int);
+int	getmtu(int, char *);

+ 6 - 0
freebsd.c

@@ -290,6 +290,12 @@ putpkt(int fd, uchar *buf, int sz)
 	return write(fd, buf, sz);
 }
 
+int
+getmtu(int fd, char *name)
+{
+	return 1500;
+}
+
 vlong
 getsize(int fd)
 {

+ 15 - 2
linux.c

@@ -87,6 +87,21 @@ getea(int s, char *name, uchar *ea)
 }
 
 int
+getmtu(int s, char *name)
+{
+	struct ifreq xx;
+	int n;
+
+	strcpy(xx.ifr_name, name);
+	n = ioctl(s, SIOCGIFMTU, &xx);
+	if (n == -1) {
+		perror("Can't get mtu");
+		return 1500;
+	}
+	return xx.ifr_mtu;
+}
+
+int
 getsec(int fd, uchar *place, vlong lba, int nsec)
 {
 	lseek(fd, lba * 512, 0);
@@ -128,7 +143,5 @@ getsize(int fd)
 		}
 		size = s.st_size;
 	}
-	printf("ioctl returned %d\n", n);
-	printf("%lld bytes\n", size);
 	return size;
 }

+ 13 - 3
vblade.8

@@ -3,7 +3,7 @@
 vblade, vbladed \- export data via ATA over Ethernet
 .SH SYNOPSIS
 .nf
-.B vblade {shelf} {slot} {netif} {filename}
+.B vblade [ -m mac[,mac...] ] shelf slot netif filename
 .fi
 .SH DESCRIPTION
 The
@@ -32,6 +32,12 @@ communications.
 .TP
 \fBfilename\fP
 The name of the regular file or block device to export.
+.SS Options
+.TP
+\fB-m\fP
+The -m flag takes an argument, a comma separated list of MAC addresses
+permitted access to the vblade.  A MAC address can be specified in upper
+or lower case, with or without colons.
 .SH EXAMPLE
 In this example, the root user on a host named
 .I nai
@@ -42,8 +48,12 @@ would have resulted in the process running as a daemon in the
 background.
 .IP
 .EX
- nai:~# vblade 11 1 eth0 /data/3TB
+.nf
+nai:~# vblade 11 1 eth0 /data/3TB
+.fi
 .EE
-.LP
+.SH BUGS
+Users of Jumbo frames should read the README file distributed with
+vblade to learn about a workaround for kernel buffering limitations.
 .SH AUTHOR
 Brantley Coile (brantley@coraid.com)