Commit 8fd4b67e authored by Mike Hibler's avatar Mike Hibler

Changes:

Support for jumbo packets.  Setting WITH_JUMBO on the make command line
will change the image block size to 8192 bytes and reduces the number of
block per chunk to 256 (to maintain the 1MB chunk size for compat with old
images).  The default is still 1024.

Added the notion of a "dubious" chunk buffer in the client.  If an incoming
chunk buffer is marked as CHUNK_DUBIOUS, then its contents can be evicted and
the buffer reused for a more promising chunk.  This is a crude replacement
mechanism that is currently only used in one place: if we miss part of a
chunk and the server switches to sending a new chunk for which we have no
free buffer, we switch to collecting the new chunk.  The reasoning is that
it will take a while for the server to switch back to completing the former
chunk, during which time it may send one or more complete chunks that we
could more fruitfully use (decompress and write out).

Changed the meaning of the "done" field for a chunk.  It used to mean either
that we have completely processed the chunk or that we are currently collecting
it.  It took additional work (scanning all chunk buffers) to differentiate
these cases, so I make it explicit.

Allow the client and server to dynamically determine the maximum socket
buffer size.

Fix a couple more on-the-wire data structure size/alignment issues that
showed up on a 64-bit OS.

A few minor speedups to the bitmap handling code.  Think: "rearranging deck
chairs on the Titanic" here.  We need more serious algorithmic changes
to scale all this code going forward.

Add some more TRACE events and refine what is already there.

Added some hacks to allow frisbee client/server to run on the same machine.
We had made it remarkably hard to do this.  But then again, why would you
want to!  Look for SAME_HOST_HACK in the makefile.
parent f0885af0
......@@ -66,6 +66,24 @@ SERVEROBJS = server.o $(SHAREDOBJS)
CFLAGS = -O2 -g -Wall -fno-builtin-log $(LDSTATIC) $(PTHREADCFLAGS) -DSTATS
LDFLAGS = $(LDSTATIC)
#
# Define this to break chunks into 8192 byte rather than 1024 byte blocks
#
ifeq ($(WITH_JUMBO),1)
CFLAGS += -DJUMBO
endif
#
# Define this to run the client and server on the same physical machine
# over the loopback interface. You will also have to setup an alias on
# the loopback interface so that client and server can use different IPs:
#
# ifconfig lo0 alias 127.0.0.2 netmask 255.255.255.255
#
# and just use unicast and not multi/broadcast.
#
#CFLAGS += -DSAME_HOST_HACK
#
# Define this if your implementation of cond_vars works well
# ("works well" in this case means doesn't hang forever or burn up
......@@ -74,7 +92,7 @@ LDFLAGS = $(LDSTATIC)
#CFLAGS += -DCONDVARS_WORK
# Define this to a non-zero value to enable recording of trace data
#CFLAGS += -DNEVENTS=50000
#CFLAGS += -DNEVENTS=500000
# Turn on client event handling
#CFLAGS += -DDOEVENTS
......
This diff is collapsed.
......@@ -13,22 +13,29 @@
#include "log.h"
/*
* Ethernet MTU (1514) - eth header (14) - min UDP/IP (28) - BLOCK msg
* Ethernet MTU (1514 or 9000) - eth header (14) - min UDP/IP (28) - BLOCK msg
* header (24).
*/
#ifdef JUMBO
#define MAXBLOCKSIZE 8934
#else
#define MAXBLOCKSIZE 1448
#endif
/*
* Images are broken into chunks which are the standalone unit of decompression
* Chunks are broken into blocks which are the unit of transmission
*/
#ifdef JUMBO
#define CHUNKSIZE 128
#define BLOCKSIZE 8192
#else
#define CHUNKSIZE 1024
#define BLOCKSIZE 1024
#endif
/*
* Make sure we can fit a block in a single ethernet MTU.
* This limits the maximum block size to 1448 with the current protocol
* headers on Ethernet.
*/
#if BLOCKSIZE > MAXBLOCKSIZE
#error "Invalid block size"
......@@ -66,9 +73,11 @@
/*
* Socket buffer size, used for both send and receive in client and
* server right now.
* server right now. If DYN_SOCKBUFSIZE is set, we find the larger
* socketbuffer size possible that is less than or equal to SOCKBUFSIZE.
*/
#define SOCKBUFSIZE (200 * 1024)
#define SOCKBUFSIZE (512 * 1024)
#define DYN_SOCKBUFSIZE 1
/*
* The number of read-ahead chunks that the client will request
......@@ -202,7 +211,7 @@ typedef struct {
} __attribute__((__packed__)) v1;
uint32_t limit[256];
} u;
} ClientStats_t;
} __attribute__((__packed__)) ClientStats_t;
typedef struct {
char map[CHUNKSIZE/CHAR_BIT];
......@@ -289,6 +298,7 @@ typedef struct {
/*
* Protos.
*/
int GetSockbufSize(void);
int ClientNetInit(void);
int ServerNetInit(void);
int ServerNetMCKeepAlive(void);
......
/*
* EMULAB-COPYRIGHT
* Copyright (c) 2000-2006 University of Utah and the Flux Group.
* Copyright (c) 2000-2009 University of Utah and the Flux Group.
* All rights reserved.
*/
......@@ -39,9 +39,47 @@ static int sock = -1;
struct in_addr myipaddr;
static int nobufdelay = -1;
int broadcast = 0;
static int isclient = 0;
static int sndportnum; /* kept in network order */
int
GetSockbufSize(void)
{
static int sbsize = 0;
if (sbsize == 0) {
#if DYN_SOCKBUFSIZE > 0
int sock;
if ((sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0)
pfatal("Could not allocate a socket");
for (sbsize = SOCKBUFSIZE; sbsize > 0; sbsize -= (16*1024)) {
int i = sbsize;
if (setsockopt(sock, SOL_SOCKET, SO_SNDBUF,
&i, sizeof(i)) >= 0)
break;
}
if (sbsize < 0) {
int i = 0;
unsigned int ilen = sizeof(i);
if (getsockopt(sock, SOL_SOCKET, SO_SNDBUF,
&i, &ilen) < 0)
i = SOCKBUFSIZE;
sbsize = i;
}
close(sock);
#else
sbsize = SOCKBUFSIZE;
#endif
log("Maximum socket buffer size of %d bytes", sbsize);
}
return sbsize;
}
static void
CommonInit(void)
CommonInit(int dobind)
{
struct sockaddr_in name;
struct timeval timeout;
......@@ -52,40 +90,44 @@ CommonInit(void)
if ((sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0)
pfatal("Could not allocate a socket");
i = SOCKBUFSIZE;
i = GetSockbufSize();
if (setsockopt(sock, SOL_SOCKET, SO_SNDBUF, &i, sizeof(i)) < 0)
pwarning("Could not increase send socket buffer size to %d",
SOCKBUFSIZE);
pwarning("Could not increase send socket buffer size to %d", i);
i = SOCKBUFSIZE;
i = GetSockbufSize();
if (setsockopt(sock, SOL_SOCKET, SO_RCVBUF, &i, sizeof(i)) < 0)
pwarning("Could not increase recv socket buffer size to %d",
SOCKBUFSIZE);
name.sin_family = AF_INET;
name.sin_port = htons(portnum);
name.sin_addr.s_addr = htonl(INADDR_ANY);
i = MAXBINDATTEMPTS;
while (i) {
if (bind(sock, (struct sockaddr *)&name, sizeof(name)) == 0)
break;
/*
* Note that we exit with a magic value.
* This is for server wrapper-scripts so that they can
* differentiate this case and try again with a different port.
*/
if (--i == 0) {
error("Could not bind to port %d!\n", portnum);
exit(EADDRINUSE);
pwarning("Could not increase recv socket buffer size to %d", i);
if (dobind) {
name.sin_family = AF_INET;
name.sin_port = htons(portnum);
name.sin_addr.s_addr = htonl(INADDR_ANY);
i = MAXBINDATTEMPTS;
while (i) {
if (bind(sock, (struct sockaddr *)&name, sizeof(name)) == 0)
break;
/*
* Note that we exit with a magic value.
* This is for server wrapper-scripts so that they can
* differentiate this case and try again with a
* different port.
*/
if (--i == 0) {
error("Could not bind to port %d!\n", portnum);
exit(EADDRINUSE);
}
pwarning("Bind to port %d failed. Will try %d more times!",
portnum, i);
sleep(5);
}
pwarning("Bind to port %d failed. Will try %d more times!",
portnum, i);
sleep(5);
log("Bound to port %d", portnum);
} else {
log("NOT binding to port %d", portnum);
}
log("Bound to port %d", portnum);
sndportnum = htons(portnum);
/*
* At present, we use a multicast address in both directions.
......@@ -172,7 +214,12 @@ CommonInit(void)
int
ClientNetInit(void)
{
CommonInit();
#ifndef SAME_HOST_HACK
CommonInit(1);
#else
CommonInit(0);
#endif
isclient = 1;
return 1;
}
......@@ -186,7 +233,8 @@ ClientNetID(void)
int
ServerNetInit(void)
{
CommonInit();
CommonInit(1);
isclient = 0;
return 1;
}
......@@ -259,12 +307,33 @@ PacketReceive(Packet_t *p)
mlen, p->hdr.datalen);
return 1;
}
#ifdef SAME_HOST_HACK
/*
* If using a host alias for the client, a message may get
* the wrong IP, so rig the IP check to make it always work.
*/
if (p->hdr.srcip != from.sin_addr.s_addr)
from.sin_addr.s_addr = p->hdr.srcip;
/*
* Also, we aren't binding to a port on the client side, so the
* first message to the server will contain the actual port we
* will use from now on.
*/
if (!isclient && sndportnum == htons(portnum) &&
sndportnum != from.sin_port)
sndportnum = from.sin_port;
#endif
if (p->hdr.srcip != from.sin_addr.s_addr) {
log("Bad message source (%x != %x)",
ntohl(from.sin_addr.s_addr), ntohl(p->hdr.srcip));
return 1;
}
if (sndportnum != from.sin_port) {
log("Bad message port (%d != %d)",
ntohs(from.sin_port), ntohs(sndportnum));
return 1;
}
return 0;
}
......@@ -285,7 +354,7 @@ PacketSend(Packet_t *p, int *resends)
p->hdr.srcip = myipaddr.s_addr;
to.sin_family = AF_INET;
to.sin_port = htons(portnum);
to.sin_port = sndportnum;
to.sin_addr.s_addr = mcastaddr.s_addr;
delays = 0;
......@@ -322,7 +391,7 @@ PacketReply(Packet_t *p)
len = sizeof(p->hdr) + p->hdr.datalen;
to.sin_family = AF_INET;
to.sin_port = htons(portnum);
to.sin_port = sndportnum;
to.sin_addr.s_addr = p->hdr.srcip;
p->hdr.srcip = myipaddr.s_addr;
......
/*
* EMULAB-COPYRIGHT
* Copyright (c) 2000-2005 University of Utah and the Flux Group.
* Copyright (c) 2000-2009 University of Utah and the Flux Group.
* All rights reserved.
*/
......@@ -81,10 +81,12 @@ struct {
unsigned long requests;
unsigned long joinrep;
unsigned long blockssent;
unsigned long dupsent;
unsigned long filereads;
unsigned long long filebytes;
unsigned long long fileusecs;
unsigned long filemaxusec;
unsigned long partialreq;
unsigned long dupsent;
unsigned long qmerges;
unsigned long badpackets;
unsigned long blockslost;
......@@ -125,6 +127,7 @@ static int WorkQSize = 0;
static int WorkChunk, WorkBlock, WorkCount;
#ifdef STATS
static int WorkQMax = 0;
static unsigned long WorkQMaxBlocks = 0;
#endif
/*
......@@ -134,8 +137,7 @@ static int WorkQMax = 0;
* request can be dropped if there already exists a Q item, since the client
* is going to see that piece eventually.
*
* We use a spinlock to guard the work queue, which incidentally will protect
* malloc/free.
* We use a mutex to guard the work queue.
*
* XXX - Clients make requests for chunk/block pieces they are
* missing. For now, map that into an entire chunk and add it to the
......@@ -166,6 +168,9 @@ WorkQueueEnqueue(int chunk, BlockMap_t *map, int count)
{
WQelem_t *wqel;
int elt, blocks;
#ifdef STATS
unsigned long qblocks = 0;
#endif
if (count == 0)
return 0;
......@@ -203,6 +208,17 @@ WorkQueueEnqueue(int chunk, BlockMap_t *map, int count)
*/
if (wqel->nblocks == CHUNKSIZE)
blocks = 0;
/*
* Or if incoming request is an entire chunk
* just copy that map.
*/
else if (count == CHUNKSIZE) {
wqel->blockmap = *map;
blocks = CHUNKSIZE - wqel->nblocks;
}
/*
* Otherwise do the full merge
*/
else
blocks = BlockMapMerge(map, &wqel->blockmap);
EVENT(1, EV_WORKMERGE, mcastaddr,
......@@ -212,6 +228,9 @@ WorkQueueEnqueue(int chunk, BlockMap_t *map, int count)
pthread_mutex_unlock(&WorkQLock);
return 0;
}
#ifdef STATS
qblocks += wqel->nblocks;
#endif
elt--;
}
......@@ -227,6 +246,8 @@ WorkQueueEnqueue(int chunk, BlockMap_t *map, int count)
#ifdef STATS
if (WorkQSize > WorkQMax)
WorkQMax = WorkQSize;
if (qblocks > WorkQMaxBlocks)
WorkQMaxBlocks = qblocks;
#endif
pthread_mutex_unlock(&WorkQLock);
......@@ -642,8 +663,10 @@ PlayFrisbee(void)
fatal("could not allocate read buffer");
while (1) {
if (killme)
return;
if (killme) {
log("Interrupted!");
break;
}
/*
* Look for a WorkQ item to process. When there is nothing
......@@ -707,7 +730,7 @@ PlayFrisbee(void)
int readbytes;
int resends;
int thisburst = 0;
#ifdef NEVENTS
#if defined(NEVENTS) || defined(STATS)
struct timeval rstamp;
gettimeofday(&rstamp, 0);
#endif
......@@ -727,6 +750,19 @@ PlayFrisbee(void)
pfatal("Reading File");
fatal("EOF on file");
}
#ifdef STATS
{
struct timeval now;
int us;
gettimeofday(&now, 0);
timersub(&now, &rstamp, &now);
us = now.tv_sec * 1000000 + now.tv_usec;
assert(us >= 0);
Stats.fileusecs += us;
if (us > Stats.filemaxusec)
Stats.filemaxusec = us;
}
#endif
DOSTAT(filereads++);
DOSTAT(filebytes += cc);
EVENT(2, EV_READFILE, mcastaddr,
......@@ -937,7 +973,7 @@ main(int argc, char **argv)
if (tracing) {
TraceStop();
TraceDump();
TraceDump(1);
}
subtime(&LastReq, &LastReq, &FirstReq);
......@@ -970,17 +1006,24 @@ main(int argc, char **argv)
Stats.dupsent);
log(" client re-req: %d",
Stats.clientlost);
log(" 1k blocks sent: %d (%d repeated)",
log(" %dk blocks sent: %d (%d repeated)",
(BLOCKSIZE/1024),
Stats.blockssent, Stats.blockssent ?
(Stats.blockssent-FileInfo.blocks) : 0);
log(" file reads: %d (%qu bytes, %qu repeated)",
Stats.filereads, Stats.filebytes, Stats.filebytes ?
(Stats.filebytes - FileInfo.blocks * BLOCKSIZE) : 0);
log(" file read time: %d.%03d sec (%llu us/op, %d us max)",
(int)(Stats.fileusecs / 1000000),
(int)((Stats.fileusecs % 1000000) / 1000),
Stats.fileusecs / (Stats.filereads ? Stats.filereads : 1),
Stats.filemaxusec);
log(" net idle/blocked: %d/%d", Stats.goesidle, nonetbufs);
log(" send intvl/missed: %d/%d",
Stats.intervals, Stats.missed);
log(" spurious wakeups: %d", Stats.wakeups);
log(" max workq size: %d", WorkQMax);
log(" max workq size: %d elts, %lu blocks",
WorkQMax, WorkQMaxBlocks);
}
#endif
......@@ -1230,6 +1273,11 @@ compute_sendrate(void)
if (dynburst)
maxburstsize = burstsize;
if (burstsize * sizeof(Packet_t) > GetSockbufSize()) {
warning("Burst size exceeds socket buffer size, "
"may drop packets");
}
log("Maximum send bandwidth %.3f Mbits/sec (%d blocks/sec)",
bandwidth / 1000000.0, bandwidth / wireblocksize);
if (debug)
......
/*
* EMULAB-COPYRIGHT
* Copyright (c) 2002, 2003, 2004 University of Utah and the Flux Group.
* Copyright (c) 2002-2009 University of Utah and the Flux Group.
* All rights reserved.
*/
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <string.h>
#include <netinet/in.h>
#include <arpa/inet.h>
......@@ -97,7 +98,7 @@ TraceStop(void)
}
void
TraceDump(void)
TraceDump(int serverrel)
{
struct event *ptr;
int done = 0;
......@@ -111,14 +112,34 @@ TraceDump(void)
if (!done) {
done = 1;
fprintf(fd, "%d of %d events, "
"start: %ld.%03ld:\n",
"start: %ld.%03ld, level: %d:\n",
evcount > NEVENTS ? NEVENTS : evcount,
evcount, (long)startt.tv_sec,
startt.tv_usec/1000);
startt.tv_usec/1000, oevlogging);
/*
* Make all event stamps relative to the
* first event received. This is specifically
* for the case where the client and server
* are running on the same machine and there
* is only a single client. This makes the
* server timeline relative to the client's
* because the server's first event is the
* JOIN from the client which occurs at 0.000
* in the client's timeline.
*
* XXX we add 1us in order to make it possible
* to merge the timelines and preserve
* causality.
*/
if (serverrel) {
struct timeval ms = { 0, 120 };
timersub(&ptr->tstamp, &ms, &startt);
}
}
timersub(&ptr->tstamp, &startt, &stamp);
fprintf(fd, " +%03ld.%03ld: ",
(long)stamp.tv_sec, stamp.tv_usec/1000);
fprintf(fd, " +%03ld.%06ld: ",
(long)stamp.tv_sec, stamp.tv_usec);
fprintf(fd, "%c: ", evisclient ? 'C' : 'S');
switch (ptr->event) {
case EV_JOINREQ:
fprintf(fd, "%s: JOIN request, ID=%lx\n",
......@@ -248,12 +269,32 @@ TraceDump(void)
break;
}
case EV_CLINOROOM:
fprintf(fd, "%s: block %lu[%lu], no room, "
"dropped %lu blocks of previous\n",
fprintf(fd, "%s: block %lu[%lu], no room "
"(full=%lu filling=%lu)\n",
inet_ntoa(ptr->srcip),
ptr->args[0], ptr->args[1],
ptr->args[2], ptr->args[3]);
break;
case EV_CLIFOUNDROOM:
fprintf(fd, "%s: block %lu[%lu], marked dubious"
" (missed %lu blocks)\n",
inet_ntoa(ptr->srcip),
ptr->args[0], ptr->args[1],
ptr->args[2]);
break;
case EV_CLIREUSE:
fprintf(fd, "%s: block %lu[%lu], displaces "
"dubious chunk %lu from chunk buffer\n",
inet_ntoa(ptr->srcip),
ptr->args[0], ptr->args[1],
ptr->args[2]);
break;
case EV_CLIDUBPROMO:
fprintf(fd, "%s: block %lu[%lu], no longer "
"dubious\n",
inet_ntoa(ptr->srcip),
ptr->args[0], ptr->args[1]);
break;
case EV_CLIDUPCHUNK:
fprintf(fd, "%s: block %lu[%lu], dup chunk\n",
inet_ntoa(ptr->srcip),
......@@ -318,8 +359,10 @@ TraceDump(void)
inet_ntoa(ptr->srcip), ptr->args[0]);
break;
case EV_CLIJOINREP:
fprintf(fd, "%s: got JOIN reply, blocks=%lu\n",
inet_ntoa(ptr->srcip), ptr->args[0]);
fprintf(fd, "%s: got JOIN reply, blocks=%lu, "
"blocksize=%lu\n",
inet_ntoa(ptr->srcip), ptr->args[0],
ptr->args[1]);
break;
case EV_CLILEAVE:
{
......@@ -327,7 +370,7 @@ TraceDump(void)
bytes = (unsigned long long)ptr->args[2] << 32;
bytes |= ptr->args[3];
fprintf(fd, "%s: send LEAVE, ID=%lx, "
"time=%lu, bytes=%qu\n",
"time=%lu, bytes=%llu\n",
inet_ntoa(ptr->srcip),
ptr->args[0], ptr->args[1], bytes);
break;
......@@ -351,14 +394,32 @@ TraceDump(void)
ptr->args[0], ptr->args[1],
ptr->args[2], ptr->args[3]);
break;
case EV_CLIDCSTAT:
{
unsigned long long bytes;
bytes = (unsigned long long)ptr->args[0] << 32;
bytes |= ptr->args[1];
fprintf(fd, "%s: decompressed %llu bytes total\n",
inet_ntoa(ptr->srcip), bytes);
break;
}
case EV_CLIDCIDLE:
fprintf(fd, "%s: decompressor IDLE\n",
inet_ntoa(ptr->srcip));
break;
case EV_CLIWRSTATUS:
fprintf(fd, "%s: writer %s\n",
fprintf(fd, "%s: writer %s",
inet_ntoa(ptr->srcip),
ptr->args[0] ? "IDLE" : "STARTED");
if (ptr->args[0]) {
unsigned long long bytes;
bytes = (unsigned long long)ptr->args[1] << 32;
bytes |= ptr->args[2];
fprintf(fd, ", %llu bytes written",
bytes);
}
fprintf(fd, "\n");
break;
case EV_CLIGOTPKT:
stamp.tv_sec = ptr->args[0];
......
/*
* EMULAB-COPYRIGHT
* Copyright (c) 2002, 2003, 2004 University of Utah and the Flux Group.
* Copyright (c) 2002-2009 University of Utah and the Flux Group.
* All rights reserved.
*/
......@@ -93,15 +93,19 @@ if (evlogging >= (l)) { \
#define EV_LONGBURST 39
#define EV_DUPCHUNK 40
#define EV_CLIWRSTATUS 41
#define EV_CLIFOUNDROOM 42
#define EV_CLIREUSE 43
#define EV_CLIDUBPROMO 44
#define EV_CLIDCSTAT 45
#define EV_MAX 41
#define EV_MAX 45
extern void ClientTraceInit(char *file);
extern void ClientTraceReinit(char *file);
extern void ServerTraceInit(char *file);
extern void TraceStart(int level);
extern void TraceStop(void);
extern void TraceDump(void);
extern void TraceDump(int mkrel);
#else
#define EVENT(l, e, ip, a1, a2, a3, a4)
#define CLEVENT(l, e, a1, a2, a3, a4)
......@@ -110,5 +114,5 @@ extern void TraceDump(void);
#define ServerTraceInit(file)
#define TraceStart(level)
#define TraceStop()
#define TraceDump()
#define TraceDump(mkrel)
#endif
<
/*
* EMULAB-COPYRIGHT
* Copyright (c) 2000-2007 University of Utah and the Flux Group.
* Copyright (c) 2000-2009 University of Utah and the Flux Group.
* All rights reserved.
*/
......@@ -63,7 +63,7 @@ sleeptime(unsigned int usecs, char *str, int doround)
if (clockres_us == 0) {
#ifndef linux
struct clockinfo ci;
int cisize = sizeof(ci);
size_t cisize = sizeof(ci);
ci.hz = 0;
if (sysctlbyname("kern.clockrate", &ci, &cisize, 0, 0) == 0 &&
......@@ -179,11 +179,17 @@ BlockMapAdd(BlockMap_t *blockmap, int block, int count)
i = block / CHAR_BIT;
off = block % CHAR_BIT;
while (count--) {
blockmap->map[i] |= (1 << off);
if (++off == CHAR_BIT) {
i++;
off = 0;
while (count > 0) {
if (off == 0 && count >= CHAR_BIT) {
blockmap->map[i++] = ~0;
count -= CHAR_BIT;
} else {
blockmap->map[i] |= (1 << off);
if (++off == CHAR_BIT) {
i++;
off = 0;
}
count--;
}
}
}
......@@ -300,13 +306,17 @@ BlockMapInvert(BlockMap_t *oldmap, BlockMap_t *newmap)
newmap->map[i] = ~(oldmap->map[i]);
}
/*
* Merge any blocks from request frommap into tomap.
*/
int
BlockMapMerge(BlockMap_t *frommap, BlockMap_t *tomap)
{
int i, bit, mask, did = 0;
for (i = 0; i < sizeof(frommap->map); i++) {
if (tomap->map[i] == ~0 || frommap->map[i] == tomap->map[i])
if (frommap->map[i] == 0 || tomap->map[i] == ~0 ||
frommap->map[i] == tomap->map[i])
continue;
for (bit = 0; bit < CHAR_BIT; bit++) {
mask = 1 << bit;
......@@ -321,31 +331,46 @@ BlockMapMerge(BlockMap_t *frommap, BlockMap_t *tomap)
return did;
}
static void
bmfirstfunc(int chunk, int block, int count, void *arg)
{
int *firstp = arg;
if (*firstp == -1)
*firstp = block;
}
/*
* Return the block number of the first block allocated in the map.
* Returns CHUNKSIZE if no block is set.
*/
int
BlockMapFirst(BlockMap_t *blockmap)
{
int first = -1;
int block, i;
assert(sizeof(blockmap->map) * CHAR_BIT == CHUNKSIZE);
/*
* Skip empty space at the front quickly
*/
for (i = 0; i < sizeof(blockmap->map); i++)
if (blockmap->map[i] != 0)
break;