1361 lines · 28.6 KB · C
RawDownload
/* * basic Flash Translation Layer driver *	see for instance the Intel technical paper *	``Understanding the Flash Translation Layer (FTL) Specification'' *	Order number 297816-001 (online at www.intel.com) * * a public driver by David Hinds, dhinds@allegro.stanford.edu * further helps with some details. * * this driver uses the common simplification of never storing * the VBM on the medium (a waste of precious flash!) but * rather building it on the fly as the block maps are read. * * Plan 9 driver (c) 1997 by C H Forsyth (forsyth@terzarima.net) *	This driver may be used or adapted by anyone for any non-commercial purpose. * * adapted for Inferno 1998 by C H Forsyth, Vita Nuova Limited, York, England (forsyth@vitanuova.com) * * TO DO: *	check error handling details for get/put flash *	bad block handling *	reserved space in formatted size *	possibly block size as parameter */ #include "u.h"#include "../port/lib.h"#include "../port/error.h"#include "mem.h"#include "dat.h"#include "fns.h" #include "kernel.h" typedef struct Ftl Ftl;typedef struct Merase Merase;typedef struct Terase Terase; enum {	Eshift = 18,	/* 2^18=256k; log2(eraseunit) */	Flashseg = 1<<Eshift,	Bshift = 9,		/* 2^9=512 */	Bsize = 1<<Bshift,	BAMoffset = 0x100,	Nolimit = ~0,	USABLEPCT = 95,	/* release only this % to client */ 	FTLDEBUG = 0, 	NPART = 4,}; /* erase unit header (defined by FTL specification) */struct Merase {	uchar	linktuple[5];	uchar	orgtuple[10];	uchar	nxfer;	uchar	nerase[4];	uchar	id[2];	uchar	bshift;	uchar	eshift;	uchar	pstart[2];	uchar	nunits[2];	uchar	psize[4];	uchar	vbmbase[4];	uchar	nvbm[2];	uchar	flags;	uchar	code;	uchar	serial[4];	uchar	altoffset[4];	uchar	bamoffset[4];	uchar	rsv2[12];};#define	ERASEHDRLEN	64 enum {	/* special unit IDs */	XferID = 0xffff,	XferBusy = 0x7fff, 	/* special BAM addresses */	Bfree = 0xffffffff,	Bwriting = 0xfffffffe,	Bdeleted = 0, 	/* block types */	TypeShift = 7,	BlockType = (1<<TypeShift)-1,	ControlBlock = 0x30,	DataBlock = 0x40,	ReplacePage = 0x60,	BadBlock = 0x70,}; #define	BTYPE(b)	((b) & BlockType)#define	BADDR(b)	((b) & ~BlockType)#define	BNO(va)	(((ulong)(va))>>Bshift)#define	MKBAM(b,t)	(((b)<<Bshift)|(t)) struct Terase {	int	x;	int	id;	ulong	offset;	ulong	bamoffset;	ulong	nbam;	ulong*	bam;	ulong	bamx;	ulong	nfree;	ulong	nused;	ulong	ndead;	ulong	nbad;	ulong	nerase;}; struct Ftl {	QLock;	Ref; 	Chan*	flash;	Chan*	flashctl;	ulong	base;	/* base of flash region */	ulong	size;	/* size of flash region */	ulong	segsize;	/* size of flash segment (erase unit) */	int	eshift;	/* log2(erase-unit-size) */	int	bshift;	/* log2(bsize) */	int	bsize;	int	nunit;	/* number of segments (erase units) */	Terase**	unit;	int	lastx;	/* index in unit of last allocation */	int	xfer;		/* index in unit of current transfer unit (-1 if none) */	ulong	nfree;	/* total free space in blocks */	ulong	nblock;	/* total space in blocks */	ulong	rwlimit;	/* user-visible block limit (`formatted size') */	ulong*	vbm;		/* virtual block map */	ulong	fstart;	/* address of first block of data in a segment */	int	trace;	/* (debugging) trace of read/write actions */	int	detach;	/* free Ftl on last close */ 	/* scavenging variables */	QLock	wantq;	Rendez	wantr;	Rendez	workr;	int	needspace;	int	hasproc;	int	npart;		/* over and above ftldata */	struct {		ulong start, size;		ulong rwlimit;		char *name;	/* nil if slot unused */	} part[NPART];}; enum {	/* Ftl.detach */	Detached = 1,	/* detach on close */	Deferred	/* scavenger must free it */}; /* little endian */#define	GET2(p)	(((p)[1]<<8)|(p)[0])#define	GET4(p)	(((((((p)[3]<<8)|(p)[2])<<8)|(p)[1])<<8)|(p)[0])#define	PUT2(p,v)	(((p)[1]=(v)>>8),((p)[0]=(v)))#define	PUT4(p,v)	(((p)[3]=(v)>>24),((p)[2]=(v)>>16),((p)[1]=(v)>>8),((p)[0]=(v))) static	Lock	ftllock;static	Ftl	*ftls;static	int	ftlpct = USABLEPCT; static	ulong	allocblk(Ftl*);static	int	erasedetect(Ftl *ftl, ulong base, ulong size, ushort *pstart, ushort *nunits);static	void	eraseflash(Ftl*, ulong);static	void	erasefree(Terase*);static	void	eraseinit(Ftl*, ulong, int, int);static	Terase*	eraseload(Ftl*, int, ulong);static	void	ftlfree(Ftl*);static	void	getflash(Ftl*, void*, ulong, long);static	int	mapblk(Ftl*, ulong, Terase**, ulong*);static	Ftl*	mkftl(char*, ulong, ulong, int, char*);static	void	putbam(Ftl*, Terase*, int, ulong);static	void	putflash(Ftl*, ulong, void*, long);static	int	scavenge(Ftl*); enum {	Qdir,	Qctl,	Qdata,}; #define DATAQID(q) ((q) >= Qdata && (q) <= Qdata + NPART) static voidftlpartcmd(Ftl *ftl, char **fields, int nfields){	ulong start, end;	char *p;	int n, newn; 	/* name start [end] */	if(nfields < 2 || nfields > 3)		error(Ebadarg);	if(ftl->npart >= NPART)		error("table full");	if(strcmp(fields[0], "ctl") == 0 || strcmp(fields[0], "data") == 0)		error(Ebadarg);	newn = -1;	for(n = 0; n < NPART; n++){		if(ftl->part[n].name == nil){			if(newn < 0)				newn = n;			continue;		}		if(strcmp(fields[0], ftl->part[n].name + 3) == 0)			error(Ebadarg);	}	start = strtoul(fields[1], 0, 0);	if(nfields > 2)		end = strtoul(fields[2], 0, 0);	else		end = ftl->rwlimit * Bsize;	if(start >= end || start % Bsize || end % Bsize)		error(Ebadarg);	ftl->part[newn].start = start;	ftl->part[newn].size = end - start;	ftl->part[newn].rwlimit = end / Bsize;	free(ftl->part[newn].name);	p = malloc(strlen(fields[0]) + 3 + 1);	strcpy(p, "ftl");	strcat(p, fields[0]);	ftl->part[newn].name = p;	ftl->npart++;}		static voidftldelpartcmd(Ftl *ftl, char **fields, int nfields){	int n;	// name	if(nfields != 1)		error(Ebadarg);	for(n = 0; n < NPART; n++)		if(strcmp(fields[0], ftl->part[n].name + 3) == 0){			free(ftl->part[n].name);			ftl->part[n].name = nil;			ftl->npart--;			return;		}	error(Ebadarg);} static intftlgen(Chan *c, char*, Dirtab*, int, int i, Dir *dp){	int n;	switch(i){	case DEVDOTDOT:		devdir(c, (Qid){Qdir, 0, QTDIR}, "#X", 0, eve, 0555, dp);		break;	case 0:		devdir(c, (Qid){Qctl, 0, QTFILE}, "ftlctl", 0, eve, 0660, dp);		break;	case 1:		devdir(c, (Qid){Qdata, 0, QTFILE}, "ftldata", ftls ? ftls->rwlimit * Bsize : 0, eve, 0660, dp);		break;	default:		if(ftls == nil)			return -1;		i -= 2;		if(i >= ftls->npart)			return -1;		for(n = 0; n < NPART; n++)			if(ftls->part[n].name != nil){				if(i == 0)					break;				i--;			}		if(i != 0){			print("wierd\n");			return -1;		}		devdir(c, (Qid){Qdata + 1 + n, 0, QTFILE}, ftls->part[n].name, ftls->part[n].size, eve, 0660, dp);	}	return 1;} static Ftl *ftlget(void){	Ftl *ftl; 	lock(&ftllock);	ftl = ftls;	if(ftl != nil)		incref(ftl);	unlock(&ftllock);	return ftl;} static voidftlput(Ftl *ftl){	if(ftl != nil){		lock(&ftllock);		if(decref(ftl) == 0 && ftl->detach == Detached){			ftls = nil;			if(ftl->hasproc){	/* no lock needed: can't change if ftl->ref==0 */				ftl->detach = Deferred;				wakeup(&ftl->workr);			}else				ftlfree(ftl);		}		unlock(&ftllock);	}} static Chan *ftlattach(char *spec){	return devattach('X', spec);} static Walkqid*ftlwalk(Chan *c, Chan *nc, char **name, int nname){	Walkqid *wq; 	wq = devwalk(c, nc, name, nname, 0, 0, ftlgen);	if(wq != nil && wq->clone != nil && wq->clone != c)		if(DATAQID(wq->clone->qid.path))			wq->clone->aux = ftlget();	return wq;} static intftlstat(Chan *c, uchar *dp, int n){	return devstat(c, dp, n, 0, 0, ftlgen);} static Chan*ftlopen(Chan *c, int omode){	Ftl *ftl;	omode = openmode(omode);	if(DATAQID(c->qid.path)){		ftl = ftls;		if(ftl == nil)			error(Enodev);		if(strcmp(up->env->user, eve)!=0)			error(Eperm);	}	else if(c->qid.path == Qctl){		if(strcmp(up->env->user, eve)!=0)			error(Eperm);	}	c = devopen(c, omode, 0, 0, ftlgen);	if(DATAQID(c->qid.path)){		c->aux = ftlget();		if(c->aux == nil)			error(Enodev);	}	return c;} static void	 ftlclose(Chan *c){	if(DATAQID(c->qid.path) && (c->flag&COPEN) != 0)		ftlput((Ftl*)c->aux);} static long	 ftlread(Chan *c, void *buf, long n, vlong offset){	Ftl *ftl;	Terase *e;	int nb;	uchar *a;	ulong pb;	if(c->qid.type & QTDIR)		return devdirread(c, buf, n, 0, 0, ftlgen); 	if(DATAQID(c->qid.path)){		ulong rwlimit; 		if(n <= 0 || n%Bsize || offset%Bsize)			error(Eio);		ftl = c->aux;		if(c->qid.path > Qdata){			int p = c->qid.path - Qdata - 1;			offset += ftl->part[p].start;			rwlimit = ftl->part[p].rwlimit;		}		else			rwlimit = ftl->rwlimit;		nb = n/Bsize;		offset /= Bsize;		if(offset >= rwlimit)			return 0;		if(offset+nb > rwlimit)			nb = rwlimit - offset;		a = buf;		for(n = 0; n < nb; n++){			qlock(ftl);			if(waserror()){				qunlock(ftl);				nexterror();			}			if(mapblk(ftl, offset+n, &e, &pb))				getflash(ftl, a, e->offset + pb*Bsize, Bsize);			else				memset(a, 0, Bsize);			poperror();			qunlock(ftl);			a += Bsize;		}		return a-(uchar*)buf;	} 	if(c->qid.path != Qctl)		error(Egreg); 	return 0;} static long	 ftlwrite(Chan *c, void *buf, long n, vlong offset){	char cmd[64], *fields[6];	int ns, i, k, nb;	uchar *a;	Terase *e, *oe;	ulong ob, v, base, size, segsize;	Ftl *ftl; 	if(n <= 0)		return 0; 	if(DATAQID(c->qid.path)){		ulong rwlimit;		ftl = c->aux;		if(n <= 0 || n%Bsize || offset%Bsize)			error(Eio);		if(c->qid.path > Qdata){			int p = c->qid.path - Qdata - 1;			offset += ftl->part[p].start;			rwlimit = ftl->part[p].rwlimit;		}		else			rwlimit = ftl->rwlimit;		nb = n/Bsize;		offset /= Bsize;		if(offset >= rwlimit)			return 0;		if(offset+nb > rwlimit)			nb = rwlimit - offset;		a = buf;		for(n = 0; n < nb; n++){			ns = 0;			while((v = allocblk(ftl)) == 0)				if(!scavenge(ftl) || ++ns > 3){					static int stop; 					if(stop < 3){						stop++;						print("ftl: flash memory full\n");					}					error("flash memory full");				}			qlock(ftl);			if(waserror()){				qunlock(ftl);				nexterror();			}			if(!mapblk(ftl, offset+n, &oe, &ob))				oe = nil;			e = ftl->unit[v>>16];			v &= 0xffff;			putflash(ftl, e->offset + v*Bsize, a, Bsize);			putbam(ftl, e, v, MKBAM(offset+n, DataBlock));			/* both old and new block references exist in this window (can't be closed?) */			ftl->vbm[offset+n] = (e->x<<16) | v;			if(oe != nil){				putbam(ftl, oe, ob, Bdeleted);				oe->ndead++;			}			poperror();			qunlock(ftl);			a += Bsize;		}		return a-(uchar*)buf;	}	else if(c->qid.path == Qctl){		if(n > sizeof(cmd)-1)			n = sizeof(cmd)-1;		memmove(cmd, buf, n);		cmd[n] = 0;		i = getfields(cmd, fields, 6, 1, " \t\n");		if(i <= 0)			error(Ebadarg);		if(i >= 2 && (strcmp(fields[0], "init") == 0 || strcmp(fields[0], "format") == 0)){

Lines 1–500 of 1361.