Browse Source

Merge upstream version 14

David Martínez Moreno 17 years ago
parent
commit
9a70c0cadd
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 -*-
 -*- 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>
 2006-09-21 "Adam J. Richter" <adam@yggdrasil.com>
 	add install target for makefile
 	add install target for makefile
 	vblade-11
 	vblade-11

+ 53 - 3
README

@@ -1,3 +1,7 @@
+
+INTRODUCTION
+------------
+
 The vblade is the virtual EtherDrive (R) blade, a program that makes a
 The vblade is the virtual EtherDrive (R) blade, a program that makes a
 seekable file available over an ethernet local area network (LAN) via
 seekable file available over an ethernet local area network (LAN) via
 the ATA over Ethernet (AoE) protocol.
 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
 AoE it becomes a storage target.  Another host on the same LAN can
 access the storage if it has a compatible aoe kernel driver.
 access the storage if it has a compatible aoe kernel driver.
 
 
+BUILDING
+--------
+
 The following command should build the vblade program on a Linux-based
 The following command should build the vblade program on a Linux-based
 system:
 system:
 
 
@@ -16,6 +23,9 @@ For FreeBSD systems, include an extra parameter like so:
 
 
   make PLATFORM=freebsd
   make PLATFORM=freebsd
 
 
+EXAMPLES
+--------
+
 There is a "vbladed" script that daemonizes the program and sends its
 There is a "vbladed" script that daemonizes the program and sends its
 output to the logger program.  Make sure you have logger installed if
 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.
 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!
 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
 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
 Kvblade
 -------
 -------

+ 157 - 66
aoe.c

@@ -5,39 +5,30 @@
 #include <stdlib.h>
 #include <stdlib.h>
 #include <unistd.h>
 #include <unistd.h>
 #include <sys/types.h>
 #include <sys/types.h>
+#include <sys/stat.h>
 #include <fcntl.h>
 #include <fcntl.h>
 #include <netinet/in.h>
 #include <netinet/in.h>
 #include "dat.h"
 #include "dat.h"
 #include "fns.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
 void
 aoead(int fd)			// advertise the virtual blade
 aoead(int fd)			// advertise the virtual blade
 {
 {
-	int len;
 	uchar buf[2000];
 	uchar buf[2000];
 	Conf *p;
 	Conf *p;
+	int i;
 
 
 	p = (Conf *)buf;
 	p = (Conf *)buf;
 	memset(p, 0, sizeof *p);
 	memset(p, 0, sizeof *p);
@@ -49,12 +40,21 @@ aoead(int fd)			// advertise the virtual blade
 	p->h.min = slot;
 	p->h.min = slot;
 	p->h.cmd = Config;
 	p->h.cmd = Config;
 	p->bufcnt = htons(Bufcount);
 	p->bufcnt = htons(Bufcount);
+	p->scnt = maxscnt = (getmtu(sfd, ifname) - sizeof (Ata)) / 512;
 	p->firmware = htons(FWV);
 	p->firmware = htons(FWV);
 	p->vercmd = 0x10 | Qread;
 	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");
 		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
 int
@@ -82,68 +82,77 @@ aoeata(Ata *p)	// do ATA reqeust
 {
 {
 	Ataregs r;
 	Ataregs r;
 	int len = 60;
 	int len = 60;
+	int n;
 
 
-	if (p->sectors && (p->aflag & Write) == 0)
-		len = sizeof (Ata);
 	r.lba = getlba(p->lba);
 	r.lba = getlba(p->lba);
 	r.sectors = p->sectors;
 	r.sectors = p->sectors;
 	r.feature = p->err;
 	r.feature = p->err;
 	r.cmd = p->cmd;
 	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->sectors = r.sectors;
 	p->err = r.err;
 	p->err = r.err;
 	p->cmd = r.status;
 	p->cmd = r.status;
 	return len;
 	return len;
 }
 }
 
 
+#define QCMD(x) ((x)->vercmd & 0xf)
+
+// yes, this makes unnecessary copies.
+
 int
 int
 confcmd(Conf *p)	// process conf request
 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:
 	case Qtest:
-		len = qcget(buf, 1024);
-		if (len != ntohs(p->len))
+		if (len != nconfig)
 			return 0;
 			return 0;
-		if (memcmp(buf, p->data, len) != 0)
-			return 0;
-		memmove(p->data, buf, len);
-		break;
+		// fall thru
 	case Qprefix:
 	case Qprefix:
-		len = qcget(buf, 1024);
-		len2 = ntohs(p->len);
-		if (len2 > len)
+		if (len > nconfig)
 			return 0;
 			return 0;
-		if (memcmp(buf, p->data, len2) != 0)
+		if (memcmp(config, p->data, len))
 			return 0;
 			return 0;
-		memmove(p->data, buf, len);
-		p->len = htons(len);
+		// fall thru
+	case Qread:
 		break;
 		break;
 	case Qset:
 	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.flags |= Error;
 			p->h.error = ConfigErr;
 			p->h.error = ConfigErr;
 			break;
 			break;
 		}
 		}
 		// fall thru
 		// fall thru
 	case Qfset:
 	case Qfset:
-		len = ntohs(p->len);
-		qcset(p->data, len);
+		nconfig = len;
+		memcpy(config, p->data, nconfig);
 		break;
 		break;
 	default:
 	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->bufcnt = htons(Bufcount);
+	p->scnt = maxscnt = (getmtu(sfd, ifname) - sizeof (Ata)) / 512;
 	p->firmware = htons(FWV);
 	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
 void
@@ -156,7 +165,8 @@ doaoe(Aoehdr *p)
 		len = aoeata((Ata*)p);
 		len = aoeata((Ata*)p);
 		break;
 		break;
 	case Config:
 	case Config:
-		if ((len = confcmd((Conf *)p)) == 0)
+		len = confcmd((Conf *)p);
+		if (len == 0)
 			return;
 			return;
 		break;
 		break;
 	default:
 	default:
@@ -179,20 +189,22 @@ void
 aoe(void)
 aoe(void)
 {
 {
 	Aoehdr *p;
 	Aoehdr *p;
-	uchar buf[1400];
+	uchar *buf;
 	int n, sh;
 	int n, sh;
+	enum { bufsz = 1<<16, };
 
 
+	buf = malloc(bufsz);
 	aoead(sfd);
 	aoead(sfd);
 
 
 	for (;;) {
 	for (;;) {
-		n = getpkt(sfd, buf, sizeof buf);
+		n = getpkt(sfd, buf, bufsz);
 		if (n < 0) {
 		if (n < 0) {
 			perror("read network");
 			perror("read network");
 			exit(1);
 			exit(1);
 		}
 		}
 		if (n < 60)
 		if (n < 60)
 			continue;
 			continue;
-		p = (Aoehdr *)buf;
+		p = (Aoehdr *) buf;
 		if (ntohs(p->type) != 0x88a2)
 		if (ntohs(p->type) != 0x88a2)
 			continue;
 			continue;
 		if (p->flags & Resp)
 		if (p->flags & Resp)
@@ -202,40 +214,119 @@ aoe(void)
 			continue;
 			continue;
 		if (p->min != slot && p->min != (uchar)~0)
 		if (p->min != slot && p->min != (uchar)~0)
 			continue;
 			continue;
+		if (nmasks && !maskok(p->src))
+			continue;
 		doaoe(p);
 		doaoe(p);
 	}
 	}
+	free(buf);
 }
 }
 
 
 void
 void
 usage(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);
 		progname);
 	exit(1);
 	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
 int
 main(int argc, char **argv)
 main(int argc, char **argv)
 {
 {
+	int ch, omode = O_RDONLY;
+	struct stat s;
+
 	setbuf(stdin, NULL);
 	setbuf(stdin, NULL);
 	atainit();
 	atainit();
 	progname = *argv;
 	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();
 		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) {
 	if (bfd == -1) {
 		perror("open");
 		perror("open");
 		exit(1);
 		exit(1);
 	}
 	}
-	shelf = atoi(argv[1]);
-	slot = atoi(argv[2]);
+	shelf = atoi(argv[0]);
+	slot = atoi(argv[1]);
 	size = getsize(bfd);
 	size = getsize(bfd);
 	size /= 512;
 	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);
 	fflush(stdout);
 	aoe();
 	aoe();
 	return 0;
 	return 0;
 }
 }
+

+ 42 - 34
ata.c

@@ -18,7 +18,7 @@ enum {
 	// status bits
 	// status bits
 	BSY =	1<<7,
 	BSY =	1<<7,
 	DRDY =	1<<6,
 	DRDY =	1<<6,
-	DF =		1<<5,
+	DF =	1<<5,
 	DRQ =	1<<3,
 	DRQ =	1<<3,
 	ERR =	1<<0,
 	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
  * with LBA 28 you shouldn't see an LBA of all ones.  Still, we don't
  * check for that.
  * 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;
 	vlong lba;
 	ushort *ip;
 	ushort *ip;
 	int n;
 	int n;
 	enum { MAXLBA28SIZE = 0x0fffffff };
 	enum { MAXLBA28SIZE = 0x0fffffff };
+	extern int maxscnt;
 
 
+	p->status = 0;
 	switch (p->cmd) {
 	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
 	case 0xe7:		// flush cache
-		return;
+		return 0;
 	case 0xec:		// identify device
 	case 0xec:		// identify device
+		if (p->sectors != 1 || ndp < 512)
+			return -1;
 		memmove(dp, ident, 512);
 		memmove(dp, ident, 512);
 		ip = (ushort *)dp;
 		ip = (ushort *)dp;
 		if (size & ~MAXLBA28SIZE)
 		if (size & ~MAXLBA28SIZE)
@@ -126,39 +126,47 @@ atacmd(Ataregs *p, uchar *dp)		// do the ata cmd
 		setlba48(ip, size);
 		setlba48(ip, size);
 		p->err = 0;
 		p->err = 0;
 		p->status = DRDY;
 		p->status = DRDY;
-		return;
+		p->sectors = 0;
+		return 0;
 	case 0xe5:		// check power mode
 	case 0xe5:		// check power mode
 		p->err = 0;
 		p->err = 0;
 		p->sectors = 0xff; // the device is active or idle
 		p->sectors = 0xff; // the device is active or idle
 		p->status = DRDY;
 		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) {
 	if (lba + p->sectors > size) {
 		p->err = IDNF;
 		p->err = IDNF;
 		p->status = DRDY | ERR;
 		p->status = DRDY | ERR;
 		p->lba = lba;
 		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);
 		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);
 		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 {
 enum {
-	VBLADE_VERSION		= 11,
+	VBLADE_VERSION		= 12,
 
 
 	// Firmware version
 	// Firmware version
 	FWV			= 0x4000 + VBLADE_VERSION,
 	FWV			= 0x4000 + VBLADE_VERSION,
@@ -67,7 +67,6 @@ struct Ata
 	uchar	cmd;
 	uchar	cmd;
 	uchar	lba[6];
 	uchar	lba[6];
 	uchar	resvd[2];
 	uchar	resvd[2];
-	uchar	data[1024];
 };
 };
 
 
 struct Conf
 struct Conf
@@ -75,7 +74,7 @@ struct Conf
 	Aoehdr	h;
 	Aoehdr	h;
 	ushort	bufcnt;
 	ushort	bufcnt;
 	ushort	firmware;
 	ushort	firmware;
-	uchar	filler;
+	uchar	scnt;
 	uchar	vercmd;
 	uchar	vercmd;
 	ushort	len;
 	ushort	len;
 	uchar	data[1024];
 	uchar	data[1024];
@@ -109,8 +108,9 @@ enum {
 	Qfset,
 	Qfset,
 
 
 	Nretries = 3,
 	Nretries = 3,
+	Nconfig = 1024,
 
 
-	Bufcount = 32,
+	Bufcount = 16,
 };
 };
 
 
 int	shelf, slot;
 int	shelf, slot;

+ 3 - 1
fns.h

@@ -10,11 +10,12 @@ void	aoead(int);
 void	aoeflush(int, int);
 void	aoeflush(int, int);
 void	aoetick(void);
 void	aoetick(void);
 void	aoerequest(int, int, vlong, int, uchar *, int);
 void	aoerequest(int, int, vlong, int, uchar *, int);
+int	maskok(uchar *);
 
 
 // ata.c
 // ata.c
 
 
 void	atainit(void);
 void	atainit(void);
-void	atacmd(Ataregs *, uchar *);
+int	atacmd(Ataregs *, uchar *, int);
 
 
 // os specific
 // os specific
 
 
@@ -25,3 +26,4 @@ int	getsec(int, uchar *, vlong, int);
 int	putpkt(int, uchar *, int);
 int	putpkt(int, uchar *, int);
 int	getpkt(int, uchar *, int);
 int	getpkt(int, uchar *, int);
 vlong	getsize(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);
 	return write(fd, buf, sz);
 }
 }
 
 
+int
+getmtu(int fd, char *name)
+{
+	return 1500;
+}
+
 vlong
 vlong
 getsize(int fd)
 getsize(int fd)
 {
 {

+ 15 - 2
linux.c

@@ -87,6 +87,21 @@ getea(int s, char *name, uchar *ea)
 }
 }
 
 
 int
 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)
 getsec(int fd, uchar *place, vlong lba, int nsec)
 {
 {
 	lseek(fd, lba * 512, 0);
 	lseek(fd, lba * 512, 0);
@@ -128,7 +143,5 @@ getsize(int fd)
 		}
 		}
 		size = s.st_size;
 		size = s.st_size;
 	}
 	}
-	printf("ioctl returned %d\n", n);
-	printf("%lld bytes\n", size);
 	return size;
 	return size;
 }
 }

+ 13 - 3
vblade.8

@@ -3,7 +3,7 @@
 vblade, vbladed \- export data via ATA over Ethernet
 vblade, vbladed \- export data via ATA over Ethernet
 .SH SYNOPSIS
 .SH SYNOPSIS
 .nf
 .nf
-.B vblade {shelf} {slot} {netif} {filename}
+.B vblade [ -m mac[,mac...] ] shelf slot netif filename
 .fi
 .fi
 .SH DESCRIPTION
 .SH DESCRIPTION
 The
 The
@@ -32,6 +32,12 @@ communications.
 .TP
 .TP
 \fBfilename\fP
 \fBfilename\fP
 The name of the regular file or block device to export.
 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
 .SH EXAMPLE
 In this example, the root user on a host named
 In this example, the root user on a host named
 .I nai
 .I nai
@@ -42,8 +48,12 @@ would have resulted in the process running as a daemon in the
 background.
 background.
 .IP
 .IP
 .EX
 .EX
- nai:~# vblade 11 1 eth0 /data/3TB
+.nf
+nai:~# vblade 11 1 eth0 /data/3TB
+.fi
 .EE
 .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
 .SH AUTHOR
 Brantley Coile (brantley@coraid.com)
 Brantley Coile (brantley@coraid.com)