## 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@caldo.demon.co.uk)# 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 (byteles@vitanuova.com)## C H Forsyth and Vita Nuova Limited expressly allow Lucent Technologies# to use this driver freely for any Inferno-related purposes whatever,# including commercial applications.## TO DO:# check error handling details for get/put flash# bad block handling# reserved space in formatted size# possibly block size as parameter# fetch parameters from header on init## Adapted to a ftl formatter for Inferno 2000 by J R Firth, Vita Nuova Limited# usage : ftl flashsize secsize inputfile outputfile# outputfile will then be a ftl image of inputfile# nb assumes the base address is zero## Converted to limbo for Inferno 2000 by JR Firth, Vita Nuova Holdings Limited# implement Ftlimage; include "sys.m";include "draw.m"; sys : Sys; OREAD, OWRITE, FD, open, create, read, write, print, fprint : import sys; Ftlimage : module{ init : fn(nil : ref Draw->Context, argv : list of string);}; stderr : ref FD; flashsize, secsize : int;flashm : array of byte;trace : int = 0; Eshift : con 18; # 2^18=256k; log2(eraseunit)Flashseg : con 1<<Eshift;Bshift : con 9; # 2^9=512Bsize : con 1<<Bshift;BAMoffset : con 16r100;Nolimit : con ~0;USABLEPCT : con 95; # release only this % to client FTLDEBUG : con 0; # erase unit header (defined by FTL specification)# offsets into MeraseO_LINKTUPLE : con 0;O_ORGTUPLE : con 5;O_NXFER : con 15;O_NERASE : con 16;O_ID : con 20;O_BSHIFT : con 22;O_ESHIFT : con 23;O_PSTART : con 24;O_NUNITS : con 26;O_PSIZE : con 28;O_VBMBASE : con 32;O_NVBM : con 36;O_FLAGS : con 38;O_CODE : con 39;O_SERIAL : con 40;O_ALTOFFSET : con 44;O_BAMOFFSET : con 48;O_RSV2 : con 52; ERASEHDRLEN : con 64; # special unit IDsXferID : con 16rffff;XferBusy : con 16r7fff; # special BAM addressesBfree : con -1; #16rffffffffBwriting : con -2; #16rfffffffeBdeleted : con 0; # block typesTypeShift : con 7;BlockType : con (1<<TypeShift)-1;ControlBlock : con 16r30;DataBlock : con 16r40;ReplacePage : con 16r60;BadBlock : con 16r70; BNO(va : int) : int{ return va>>Bshift;}MKBAM(b : int,t : int) : int{ return (b<<Bshift)|t;} Terase : adt { x : int; id : int; offset : int; bamoffset : int; nbam : int; bam : array of byte; bamx : int; nfree : int; nused : int; ndead : int; nbad : int; nerase : int;}; Ftl : adt { base : int; # base of flash region size : int; # size of flash region segsize : int; # size of flash segment (erase unit) eshift : int; # log2(erase-unit-size) bshift : int; # log2(bsize) bsize : int; nunit : int; # number of segments (erase units) unit : array of ref Terase; lastx : int; # index in unit of last allocation xfer : int; # index in unit of current transfer unit (-1 if none) nfree : int; # total free space in blocks nblock : int; # total space in blocks rwlimit : int; # user-visible block limit (`formatted size') vbm : array of int; # virtual block map fstart : int; # address of first block of data in a segment trace : int; # (debugging) trace of read/write actions detach : int; # free Ftl on last close # scavenging variables needspace : int; hasproc : int;}; # Ftl.detach Detached : con 1; # detach on close Deferred : con 2; # scavenger must free it ftls : ref Ftl; ftlstat(sz : int){ print("16r%x:16r%x:16r%x\n", ftls.rwlimit*Bsize, sz, flashsize); print("%d:%d:%d in 512b blocks\n", ftls.rwlimit, sz>>Bshift, flashsize>>Bshift);} ftlread(buf : array of byte, n : int, offset : int) : int{ ftl : ref Ftl; e : ref Terase; nb : int; a : int; pb : int; mapb : int; if(n <= 0 || n%Bsize || offset%Bsize) { fprint(stderr, "ftl: bad read\n"); exit; } ftl = ftls; nb = n/Bsize; offset /= Bsize; if(offset >= ftl.rwlimit) return 0; if(offset+nb > ftl.rwlimit) nb = ftl.rwlimit - offset; a = 0; for(n = 0; n < nb; n++){ (mapb, e, pb) = mapblk(ftl, offset+n); if(mapb) getflash(ftl, buf[a:], e.offset + pb*Bsize, Bsize); else memset(buf[a:], 0, Bsize); a += Bsize; } return a;} ftlwrite(buf : array of byte, n : int, offset : int) : int{ ns, nb : int; a : int; e, oe : ref Terase; ob, v : int; ftl : ref Ftl; mapb : int; if(n <= 0) return 0; ftl = ftls; if(n <= 0 || n%Bsize || offset%Bsize) { fprint(stderr, "ftl: bad write\n"); exit; } nb = n/Bsize; offset /= Bsize; if(offset >= ftl.rwlimit) return 0; if(offset+nb > ftl.rwlimit) nb = ftl.rwlimit - offset; a = 0; for(n = 0; n < nb; n++){ ns = 0; while((v = allocblk(ftl)) == 0) if(!scavenge(ftl) || ++ns > 3){ fprint(stderr, "ftl: flash memory full\n"); } (mapb, oe, ob) = mapblk(ftl, offset+n); if(!mapb) oe = nil; e = ftl.unit[v>>16]; v &= 16rffff; putflash(ftl, e.offset + v*Bsize, buf[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++; } a += Bsize; } return a;} mkftl(fname : string, base : int, size : int, eshift : int, op : string) : ref Ftl{ i, j, nov, segblocks : int; limit : int; e : ref Terase; ftl := ref Ftl; ftl.lastx = 0; ftl.detach = 0; ftl.needspace = 0; ftl.hasproc = 0; ftl.trace = 0; limit = flashsize; if(size == Nolimit) size = limit-base; if(base >= limit || size > limit || base+size > limit || eshift < 8 || (1<<eshift) > size) { fprint(stderr, "bad flash space parameters"); exit; } if(FTLDEBUG || ftl.trace || trace) print("%s flash %s #%x:#%x limit #%x\n", op, fname, base, size, limit); ftl.base = base; ftl.size = size; ftl.bshift = Bshift; ftl.bsize = Bsize; ftl.eshift = eshift; ftl.segsize = 1<<eshift; ftl.nunit = size>>eshift; nov = ((ftl.segsize/Bsize)*4 + BAMoffset + Bsize - 1)/Bsize; # number of overhead blocks per segment (header, and BAM itself) ftl.fstart = nov; segblocks = ftl.segsize/Bsize - nov; ftl.nblock = ftl.nunit*segblocks; if(ftl.nblock >= 16r10000) ftl.nblock = 16r10000; ftl.vbm = array[ftl.nblock] of int; ftl.unit = array[ftl.nunit] of ref Terase; if(ftl.vbm == nil || ftl.unit == nil) { fprint(stderr, "out of mem"); exit; } for(i=0; i<ftl.nblock; i++) ftl.vbm[i] = 0; if(op == "format"){ for(i=0; i<ftl.nunit-1; i++) eraseinit(ftl, i*ftl.segsize, i, 1); eraseinit(ftl, i*ftl.segsize, XferID, 1); } ftl.xfer = -1; for(i=0; i<ftl.nunit; i++){ e = eraseload(ftl, i, i*ftl.segsize); if(e == nil){ fprint(stderr, "ftl: logical segment %d: bad format\n", i); continue; } if(e.id == XferBusy){ e.nerase++; eraseinit(ftl, e.offset, XferID, e.nerase); e.id = XferID; } for(j=0; j<ftl.nunit; j++) if(ftl.unit[j] != nil && ftl.unit[j].id == e.id){ fprint(stderr, "ftl: duplicate erase unit #%x\n", e.id); erasefree(e); e = nil; break; } if(e != nil){ ftl.unit[e.x] = e; if(e.id == XferID) ftl.xfer = e.x; if (FTLDEBUG || ftl.trace || trace) fprint(stderr, "ftl: unit %d:#%x used %d free %d dead %d bad %d nerase %d\n", e.x, e.id, e.nused, e.nfree, e.ndead, e.nbad, e.nerase); } } if(ftl.xfer < 0 && ftl.nunit <= 0 || ftl.xfer >= 0 && ftl.nunit <= 1) { fprint(stderr, "ftl: no valid flash data units"); exit; } if(ftl.xfer < 0) fprint(stderr, "ftl: no transfer unit: device is WORM\n"); else ftl.nblock -= segblocks; # discount transfer segment if(ftl.nblock >= 1000) ftl.rwlimit = ftl.nblock-100; # TO DO: variable reserve else ftl.rwlimit = ftl.nblock*USABLEPCT/100; return ftl;} ftlfree(ftl : ref Ftl){ if(ftl != nil){ ftl.unit = nil; ftl.vbm = nil; ftl = nil; }} ## this simple greedy algorithm weighted by nerase does seem to lead# to even wear of erase units (cf. the eNVy file system)# bestcopy(ftl : ref Ftl) : ref Terase{ e, be : ref Terase; i : int; be = nil; for(i=0; i<ftl.nunit; i++) if((e = ftl.unit[i]) != nil && e.id != XferID && e.id != XferBusy && e.ndead+e.nbad && (be == nil || e.nerase <= be.nerase && e.ndead >= be.ndead)) be = e; return be;} copyunit(ftl : ref Ftl, from : ref Terase, too : ref Terase) : int{ i, nb : int; id := array[2] of byte; bam : array of byte; buf : array of byte; v, bno : int; if(FTLDEBUG || ftl.trace || trace) print("ftl: copying %d (#%x) to #%x\n", from.id, from.offset, too.offset); too.nbam = 0; too.bam = nil; bam = nil; buf = array[Bsize] of byte; if(buf == nil) return 0; PUT2(id, XferBusy); putflash(ftl, too.offset+O_ID, id, 2); # make new BAM nb = from.nbam*4; bam = array[nb] of byte; memmove(bam, from.bam, nb); too.nused = 0; too.nbad = 0; too.nfree = 0; too.ndead = 0; for(i = 0; i < from.nbam; i++) bv := GET4(bam[4*i:]); case(bv){ Bwriting or Bdeleted or Bfree => PUT4(bam[4*i:], Bfree); too.nfree++; break; * => case(bv&BlockType){ DataBlock or ReplacePage => v = bv; bno = BNO(v & ~BlockType); if(i < ftl.fstart || bno >= ftl.nblock){ print("ftl: unit %d:#%x bad bam[%d]=#%x\n", from.x, from.id, i, v); too.nfree++; PUT4(bam[4*i:], Bfree); break; } getflash(ftl, buf, from.offset+i*Bsize, Bsize); putflash(ftl, too.offset+i*Bsize, buf, Bsize); too.nused++; break; ControlBlock => too.nused++; break; * => # case BadBlock: # it isn't necessarily bad in this unit too.nfree++; PUT4(bam[4*i:], Bfree); break; } } # for(i=0; i<from.nbam; i++){ # v = GET4(bam[4*i:]); # if(v != Bfree && ftl.trace > 1) # print("to[%d]=#%x\n", i, v); # PUT4(bam[4*i:], v); # } putflash(ftl, too.bamoffset, bam, nb); # BUG: PUT4 ? IS IT ? # for(i=0; i<from.nbam; i++){ # v = GET4(bam[4*i:]); # PUT4(bam[4*i:], v); # } too.id = from.id; PUT2(id, too.id); putflash(ftl, too.offset+O_ID, id, 2); too.nbam = from.nbam; too.bam = bam; ftl.nfree += too.nfree - from.nfree; buf = nil; return 1;} mustscavenge(a : ref Ftl) : int{ return a.needspace || a.detach == Deferred;} donescavenge(a : ref Ftl) : int{ return a.needspace == 0;} scavengeproc(arg : ref Ftl){ ftl : ref Ftl; i : int; e, ne : ref Terase; ftl = arg; if(mustscavenge(ftl)){ if(ftl.detach == Deferred){ ftlfree(ftl); fprint(stderr, "scavenge out of memory\n"); exit; } if(FTLDEBUG || ftl.trace || trace) print("ftl: scavenge %d\n", ftl.nfree); e = bestcopy(ftl); if(e == nil || ftl.xfer < 0 || (ne = ftl.unit[ftl.xfer]) == nil || ne.id != XferID || e == ne) ; else if(copyunit(ftl, e, ne)){ i = ne.x; ne.x = e.x; e.x = i; ftl.unit[ne.x] = ne; ftl.unit[e.x] = e; ftl.xfer = e.x; e.id = XferID; e.nbam = 0; e.bam = nil; e.bamx = 0; e.nerase++; eraseinit(ftl, e.offset, XferID, e.nerase); } if(FTLDEBUG || ftl.trace || trace) print("ftl: end scavenge %d\n", ftl.nfree); ftl.needspace = 0; }} scavenge(ftl : ref Ftl) : int{ if(ftl.xfer < 0 || bestcopy(ftl) == nil) return 0; # you worm! if(!ftl.hasproc){ ftl.hasproc = 1; } ftl.needspace = 1; Lines 1–500 of 911. Load more
# # 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@caldo.demon.co.uk) # 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 (byteles@vitanuova.com) # # C H Forsyth and Vita Nuova Limited expressly allow Lucent Technologies # to use this driver freely for any Inferno-related purposes whatever, # including commercial applications. # # TO DO: # check error handling details for get/put flash # bad block handling # reserved space in formatted size # possibly block size as parameter # fetch parameters from header on init # # Adapted to a ftl formatter for Inferno 2000 by J R Firth, Vita Nuova Limited # usage : ftl flashsize secsize inputfile outputfile # outputfile will then be a ftl image of inputfile # nb assumes the base address is zero # # Converted to limbo for Inferno 2000 by JR Firth, Vita Nuova Holdings Limited # implement Ftlimage; include "sys.m"; include "draw.m"; sys : Sys; OREAD, OWRITE, FD, open, create, read, write, print, fprint : import sys; Ftlimage : module { init : fn(nil : ref Draw->Context, argv : list of string); }; stderr : ref FD; flashsize, secsize : int; flashm : array of byte; trace : int = 0; Eshift : con 18; # 2^18=256k; log2(eraseunit) Flashseg : con 1<<Eshift; Bshift : con 9; # 2^9=512 Bsize : con 1<<Bshift; BAMoffset : con 16r100; Nolimit : con ~0; USABLEPCT : con 95; # release only this % to client FTLDEBUG : con 0; # erase unit header (defined by FTL specification) # offsets into Merase O_LINKTUPLE : con 0; O_ORGTUPLE : con 5; O_NXFER : con 15; O_NERASE : con 16; O_ID : con 20; O_BSHIFT : con 22; O_ESHIFT : con 23; O_PSTART : con 24; O_NUNITS : con 26; O_PSIZE : con 28; O_VBMBASE : con 32; O_NVBM : con 36; O_FLAGS : con 38; O_CODE : con 39; O_SERIAL : con 40; O_ALTOFFSET : con 44; O_BAMOFFSET : con 48; O_RSV2 : con 52; ERASEHDRLEN : con 64; # special unit IDs XferID : con 16rffff; XferBusy : con 16r7fff; # special BAM addresses Bfree : con -1; #16rffffffff Bwriting : con -2; #16rfffffffe Bdeleted : con 0; # block types TypeShift : con 7; BlockType : con (1<<TypeShift)-1; ControlBlock : con 16r30; DataBlock : con 16r40; ReplacePage : con 16r60; BadBlock : con 16r70; BNO(va : int) : int { return va>>Bshift; } MKBAM(b : int,t : int) : int { return (b<<Bshift)|t; } Terase : adt { x : int; id : int; offset : int; bamoffset : int; nbam : int; bam : array of byte; bamx : int; nfree : int; nused : int; ndead : int; nbad : int; nerase : int; }; Ftl : adt { base : int; # base of flash region size : int; # size of flash region segsize : int; # size of flash segment (erase unit) eshift : int; # log2(erase-unit-size) bshift : int; # log2(bsize) bsize : int; nunit : int; # number of segments (erase units) unit : array of ref Terase; lastx : int; # index in unit of last allocation xfer : int; # index in unit of current transfer unit (-1 if none) nfree : int; # total free space in blocks nblock : int; # total space in blocks rwlimit : int; # user-visible block limit (`formatted size') vbm : array of int; # virtual block map fstart : int; # address of first block of data in a segment trace : int; # (debugging) trace of read/write actions detach : int; # free Ftl on last close # scavenging variables needspace : int; hasproc : int; }; # Ftl.detach Detached : con 1; # detach on close Deferred : con 2; # scavenger must free it ftls : ref Ftl; ftlstat(sz : int) { print("16r%x:16r%x:16r%x\n", ftls.rwlimit*Bsize, sz, flashsize); print("%d:%d:%d in 512b blocks\n", ftls.rwlimit, sz>>Bshift, flashsize>>Bshift); } ftlread(buf : array of byte, n : int, offset : int) : int { ftl : ref Ftl; e : ref Terase; nb : int; a : int; pb : int; mapb : int; if(n <= 0 || n%Bsize || offset%Bsize) { fprint(stderr, "ftl: bad read\n"); exit; } ftl = ftls; nb = n/Bsize; offset /= Bsize; if(offset >= ftl.rwlimit) return 0; if(offset+nb > ftl.rwlimit) nb = ftl.rwlimit - offset; a = 0; for(n = 0; n < nb; n++){ (mapb, e, pb) = mapblk(ftl, offset+n); if(mapb) getflash(ftl, buf[a:], e.offset + pb*Bsize, Bsize); else memset(buf[a:], 0, Bsize); a += Bsize; } return a; } ftlwrite(buf : array of byte, n : int, offset : int) : int { ns, nb : int; a : int; e, oe : ref Terase; ob, v : int; ftl : ref Ftl; mapb : int; if(n <= 0) return 0; ftl = ftls; if(n <= 0 || n%Bsize || offset%Bsize) { fprint(stderr, "ftl: bad write\n"); exit; } nb = n/Bsize; offset /= Bsize; if(offset >= ftl.rwlimit) return 0; if(offset+nb > ftl.rwlimit) nb = ftl.rwlimit - offset; a = 0; for(n = 0; n < nb; n++){ ns = 0; while((v = allocblk(ftl)) == 0) if(!scavenge(ftl) || ++ns > 3){ fprint(stderr, "ftl: flash memory full\n"); } (mapb, oe, ob) = mapblk(ftl, offset+n); if(!mapb) oe = nil; e = ftl.unit[v>>16]; v &= 16rffff; putflash(ftl, e.offset + v*Bsize, buf[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++; } a += Bsize; } return a; } mkftl(fname : string, base : int, size : int, eshift : int, op : string) : ref Ftl { i, j, nov, segblocks : int; limit : int; e : ref Terase; ftl := ref Ftl; ftl.lastx = 0; ftl.detach = 0; ftl.needspace = 0; ftl.hasproc = 0; ftl.trace = 0; limit = flashsize; if(size == Nolimit) size = limit-base; if(base >= limit || size > limit || base+size > limit || eshift < 8 || (1<<eshift) > size) { fprint(stderr, "bad flash space parameters"); exit; } if(FTLDEBUG || ftl.trace || trace) print("%s flash %s #%x:#%x limit #%x\n", op, fname, base, size, limit); ftl.base = base; ftl.size = size; ftl.bshift = Bshift; ftl.bsize = Bsize; ftl.eshift = eshift; ftl.segsize = 1<<eshift; ftl.nunit = size>>eshift; nov = ((ftl.segsize/Bsize)*4 + BAMoffset + Bsize - 1)/Bsize; # number of overhead blocks per segment (header, and BAM itself) ftl.fstart = nov; segblocks = ftl.segsize/Bsize - nov; ftl.nblock = ftl.nunit*segblocks; if(ftl.nblock >= 16r10000) ftl.nblock = 16r10000; ftl.vbm = array[ftl.nblock] of int; ftl.unit = array[ftl.nunit] of ref Terase; if(ftl.vbm == nil || ftl.unit == nil) { fprint(stderr, "out of mem"); exit; } for(i=0; i<ftl.nblock; i++) ftl.vbm[i] = 0; if(op == "format"){ for(i=0; i<ftl.nunit-1; i++) eraseinit(ftl, i*ftl.segsize, i, 1); eraseinit(ftl, i*ftl.segsize, XferID, 1); } ftl.xfer = -1; for(i=0; i<ftl.nunit; i++){ e = eraseload(ftl, i, i*ftl.segsize); if(e == nil){ fprint(stderr, "ftl: logical segment %d: bad format\n", i); continue; } if(e.id == XferBusy){ e.nerase++; eraseinit(ftl, e.offset, XferID, e.nerase); e.id = XferID; } for(j=0; j<ftl.nunit; j++) if(ftl.unit[j] != nil && ftl.unit[j].id == e.id){ fprint(stderr, "ftl: duplicate erase unit #%x\n", e.id); erasefree(e); e = nil; break; } if(e != nil){ ftl.unit[e.x] = e; if(e.id == XferID) ftl.xfer = e.x; if (FTLDEBUG || ftl.trace || trace) fprint(stderr, "ftl: unit %d:#%x used %d free %d dead %d bad %d nerase %d\n", e.x, e.id, e.nused, e.nfree, e.ndead, e.nbad, e.nerase); } } if(ftl.xfer < 0 && ftl.nunit <= 0 || ftl.xfer >= 0 && ftl.nunit <= 1) { fprint(stderr, "ftl: no valid flash data units"); exit; } if(ftl.xfer < 0) fprint(stderr, "ftl: no transfer unit: device is WORM\n"); else ftl.nblock -= segblocks; # discount transfer segment if(ftl.nblock >= 1000) ftl.rwlimit = ftl.nblock-100; # TO DO: variable reserve else ftl.rwlimit = ftl.nblock*USABLEPCT/100; return ftl; } ftlfree(ftl : ref Ftl) { if(ftl != nil){ ftl.unit = nil; ftl.vbm = nil; ftl = nil; } } # # this simple greedy algorithm weighted by nerase does seem to lead # to even wear of erase units (cf. the eNVy file system) # bestcopy(ftl : ref Ftl) : ref Terase { e, be : ref Terase; i : int; be = nil; for(i=0; i<ftl.nunit; i++) if((e = ftl.unit[i]) != nil && e.id != XferID && e.id != XferBusy && e.ndead+e.nbad && (be == nil || e.nerase <= be.nerase && e.ndead >= be.ndead)) be = e; return be; } copyunit(ftl : ref Ftl, from : ref Terase, too : ref Terase) : int { i, nb : int; id := array[2] of byte; bam : array of byte; buf : array of byte; v, bno : int; if(FTLDEBUG || ftl.trace || trace) print("ftl: copying %d (#%x) to #%x\n", from.id, from.offset, too.offset); too.nbam = 0; too.bam = nil; bam = nil; buf = array[Bsize] of byte; if(buf == nil) return 0; PUT2(id, XferBusy); putflash(ftl, too.offset+O_ID, id, 2); # make new BAM nb = from.nbam*4; bam = array[nb] of byte; memmove(bam, from.bam, nb); too.nused = 0; too.nbad = 0; too.nfree = 0; too.ndead = 0; for(i = 0; i < from.nbam; i++) bv := GET4(bam[4*i:]); case(bv){ Bwriting or Bdeleted or Bfree => PUT4(bam[4*i:], Bfree); too.nfree++; break; * => case(bv&BlockType){ DataBlock or ReplacePage => v = bv; bno = BNO(v & ~BlockType); if(i < ftl.fstart || bno >= ftl.nblock){ print("ftl: unit %d:#%x bad bam[%d]=#%x\n", from.x, from.id, i, v); too.nfree++; PUT4(bam[4*i:], Bfree); break; } getflash(ftl, buf, from.offset+i*Bsize, Bsize); putflash(ftl, too.offset+i*Bsize, buf, Bsize); too.nused++; break; ControlBlock => too.nused++; break; * => # case BadBlock: # it isn't necessarily bad in this unit too.nfree++; PUT4(bam[4*i:], Bfree); break; } } # for(i=0; i<from.nbam; i++){ # v = GET4(bam[4*i:]); # if(v != Bfree && ftl.trace > 1) # print("to[%d]=#%x\n", i, v); # PUT4(bam[4*i:], v); # } putflash(ftl, too.bamoffset, bam, nb); # BUG: PUT4 ? IS IT ? # for(i=0; i<from.nbam; i++){ # v = GET4(bam[4*i:]); # PUT4(bam[4*i:], v); # } too.id = from.id; PUT2(id, too.id); putflash(ftl, too.offset+O_ID, id, 2); too.nbam = from.nbam; too.bam = bam; ftl.nfree += too.nfree - from.nfree; buf = nil; return 1; } mustscavenge(a : ref Ftl) : int { return a.needspace || a.detach == Deferred; } donescavenge(a : ref Ftl) : int { return a.needspace == 0; } scavengeproc(arg : ref Ftl) { ftl : ref Ftl; i : int; e, ne : ref Terase; ftl = arg; if(mustscavenge(ftl)){ if(ftl.detach == Deferred){ ftlfree(ftl); fprint(stderr, "scavenge out of memory\n"); exit; } if(FTLDEBUG || ftl.trace || trace) print("ftl: scavenge %d\n", ftl.nfree); e = bestcopy(ftl); if(e == nil || ftl.xfer < 0 || (ne = ftl.unit[ftl.xfer]) == nil || ne.id != XferID || e == ne) ; else if(copyunit(ftl, e, ne)){ i = ne.x; ne.x = e.x; e.x = i; ftl.unit[ne.x] = ne; ftl.unit[e.x] = e; ftl.xfer = e.x; e.id = XferID; e.nbam = 0; e.bam = nil; e.bamx = 0; e.nerase++; eraseinit(ftl, e.offset, XferID, e.nerase); } if(FTLDEBUG || ftl.trace || trace) print("ftl: end scavenge %d\n", ftl.nfree); ftl.needspace = 0; } } scavenge(ftl : ref Ftl) : int { if(ftl.xfer < 0 || bestcopy(ftl) == nil) return 0; # you worm! if(!ftl.hasproc){ ftl.hasproc = 1; } ftl.needspace = 1;