shithub: neoventi

ref: 53f749d02ad4780eab161b856d49b84f54909bc1
dir: /partitioner/main.c/

View raw version
#include "platform.h"
#include "partitioner.h"
#include <stdio.h>

static const char *types[] = { "unknown", "bad", "disk", "partition", "file", };
static const char *uses[] = { "undecided","none","arena","index","bloom","index and bloom", "fossil" };

const char *usage = "%s [disk1] [disk2] [...diskn]";

int ispartofdisk(Region *disk, Region *maybepart);

static struct {
	int usebloom;
	size_t indexsize, arenasize;
	size_t regioncount;
	Region *regions;
	size_t fossilcount;
	size_t fossilsize;
	int *fds;
} state = {
	.usebloom = 0,
};

static void
parseargs(int argc, char **argv)
{
	size_t offset = 1;
	state.fossilcount = 0;
	state.fossilsize = 10*(size_t)1024*1024*1024;
	state.regioncount = argc - 1;
	state.regions = calloc(sizeof(Region), state.regioncount);
	for(int i = 1; i < argc; i += 1){
		if(streql(argv[i], "-f")){
			if(state.fossilcount == 5)
				sysfatal("Cannot give -f multiple times");
			state.fossilcount = 5;
			offset = 2;
			continue;
		}
		state.regions[i - offset].path = argv[i];
	}
	state.regioncount = argc - offset;
}

static void
roundup(size_t *val, size_t base)
{
	size_t abovebase = *val % base;
	if(abovebase == 0)
		return;
	/* The number is *not* a multiple of the base. Removing the modulus reduces to the
	 * prior multiple; adding the base then moves it to the next one. */
	*val = *val - abovebase + base;
}

/* If directory containing 'ctl', disk. Otherwise, raw file [or partition, they're treated identically] */
static void
opendisks(void)
{
	printf("Probing disk presence...\n");
	for(size_t i = 0; i < state.regioncount; i += 1){
		state.regions[i].fd = open(state.regions[i].path, OREAD | __O_DIRECT);
		state.regions[i].size = fdsize(state.regions[i].fd);
	}
}

static void
removeregion(int(*check)(size_t index), char *hint)
{
	size_t newcount = 0;
	size_t i;
	Region *newregions = calloc(sizeof(Region), state.regioncount);
	if(newregions == NULL)
		sysfatal("Insufficient memory");
	for(i = 0; i < state.regioncount; i += 1)
		if(!check(i)){
			newregions[newcount] = state.regions[i];
			newcount += 1;
		} else{
			printf("Eliding %s, which is %s\n", state.regions[i].path, hint);
		}
	state.regioncount = newcount;
	free(state.regions);
	state.regions = newregions;
}

static int
istoosmall(size_t index)
{
	return state.regions[index].size < MinSize || state.regions[index].size == (size_t)-1;
}

static int
isclosed(size_t index)
{
	return state.regions[index].fd < 0;
}

static int
isunused(size_t index)
{
	return state.regions[index].type == bad;
}

static int
isduplicate(size_t index)
{
	for(size_t i = 0; i < state.regioncount; i += 1){
		if(i == index)
			continue;
		if(streql(state.regions[index].path, state.regions[i].path))
			return 1;
	}
	return 0;
}

static int
issubregion(size_t index)
{
	for(size_t i = 0; i < state.regioncount; i += 1)
		if(ispartofdisk(&state.regions[i], &state.regions[index]))
			return 1;
	return 0;
}

static size_t
totalsize(void)
{
	size_t size = 0;
	for(size_t i = 0; i < state.regioncount; i += 1)
		size += state.regions[i].size;
	return size;
}

static void
dump(size_t *indices)
{
	size_t j = 0;
	printf("\tTotal size: %.2fMiB\n", (double)totalsize() / 1024 / 1024);
	for(size_t i = 0; i < state.regioncount; i += 1){
		if(indices == NULL || indices[j] == i){
			printf("\t\tRegion %s", state.regions[i].path);
			if(state.regions[i].offset > 0)
				printf(":%luMiB", state.regions[i].offset/1024/1024);
			printf(", uuid %s, size %.2f MiB, %uMiB/s seqread, %uus latency, %s, %d redundancy, %s, %s\n", state.regions[i].uuid, (double)state.regions[i].size/1024/1024, state.regions[i].seqspeed, state.regions[i].randlatency, types[state.regions[i].type], state.regions[i].redundancy, uses[state.regions[i].use], state.regions[i].why != NULL ? state.regions[i].why : "CAUSE UNKNOWN");
			j += 1;
		}
	}
}

static size_t
roffset(Region *r, int random, size_t alignment, size_t size)
{
	size_t offset;
	if(random)
		offset = rand() % r->size;
	else
		offset = lseek(r->fd, size, SEEK_CUR) - size;
	roundup(&offset, alignment);
	return offset;
}

static void
proberegionspeed(Region *r, int random)
{
	long count = 0, ocount;
	char *buf = aligned_alloc(512, 0x2000);
	size_t offset;
	double t;
	if(buf == NULL)
		/* Continue without determining speed */
		return;
	delta();
	for(t = 0; t < 0.1; t += delta()){
		offset = roffset(r, random, random ? 512 : 4096, random ? 512 : 4096);
		ocount = pread(r->fd, buf, random ? 512 : 4096, offset);
		if(ocount < 0 && count == 0){
			fprintf(stderr, "Unable to probe %s read speed of '%s'\n", random ? "random" : "sequential", r->path);
			break;
		}
		count += ocount;
	}
	if(random)
		r->randlatency = (t * 1000 * 1000) / (count / 512);
	else
		r->seqspeed = count / (t * 1000 * 1000);
	free(buf);
}

static void
proberegion(Region *r)
{
	printf("\tProbing '%s'...\n", r->path);
	proberegiontype(r);
	proberegionspeed(r, 0);
	proberegionspeed(r, 1);
	proberegionredundancy(r);
}

static void
proberegions(void)
{
	if(state.regioncount == 0)
		sysfatal("No usable disks provided!");
	printf("Probing disks...\n");
	for(size_t i = 0; i < state.regioncount; i += 1)
		proberegion(&state.regions[i]);
}

static void
reduceregion(size_t r, size_t size)
{
	/* If the region happens to be exactly the size intended, don't split it */
	if(state.regions[r].size == size)
		return;
	Region *newregions = calloc(sizeof(Region), state.regioncount + 1);
	if(newregions == NULL)
		sysfatal("Insufficient memory");
	memcpy(newregions, state.regions, sizeof(Region) * state.regioncount);
	free(state.regions);
	state.regions = newregions;
	state.regions[state.regioncount] = state.regions[r];
	state.regions[r].size = size;
	state.regions[state.regioncount].offset += size;
	state.regions[state.regioncount].size -= size;
	state.regions[state.regioncount].use = undecided;
	state.regioncount += 1;
}

static void
decidebloom(void)
{
	state.usebloom = totalsize() > ((size_t)32 * 1024 * 1024 * 1024);
	printf("Bloom filter: %s...\n", state.usebloom ? "enabled" : "disabled");
}

static void
decidesizes(void)
{
	size_t total = totalsize() - (state.usebloom ? 512 * 1024 * 1024 : 0);
	state.fossilsize = totalsize() / 100;
	if(state.fossilcount > 0 && state.fossilsize < 512 * 1024 * 1024)
		sysfatal("Not enough space for state.fossilcount!");
	total -= state.fossilcount * state.fossilsize;
	state.indexsize = total * 15 / 105;
	roundup(&state.indexsize, 512*1024*1024);
	state.arenasize = total - state.indexsize;
}

/* if minsize == -1, finds the largest unused region
 * Otherwise, picks a random unused region >= minsize */
static size_t
findunusedregion(size_t minsize)
{
	size_t largest = -1, largestsize = 0;
	for(size_t i = 0; i < state.regioncount; i += 1){
		if(state.regions[i].use != undecided)
			continue;
		if(minsize != (size_t)-1 && state.regions[i].size >= minsize)
			return i;
		if(minsize == (size_t)-1 && state.regions[i].size > largestsize){
			largest = i;
			largestsize = state.regions[i].size;
		}
	}
	return largest;
}

static void
picklargest(size_t minsize, int use)
{
	size_t i = findunusedregion(minsize);
	if(i == (size_t)-1){
		dump(NULL);
		sysfatal("no region big enough for %s partition, needed size %ldMiB", uses[use], minsize/1024/1024);
	}
	reduceregion(i, minsize);
	state.regions[i].why = "it was the first region found which was sufficiently large";
	state.regions[i].use = use;
}

/* As picklargest, but will split among regions as needed */
static void
scatterpicks(size_t minsize, int use)
{
	size_t i = findunusedregion(minsize);
	if(i == (size_t)-1){
		dump(NULL);
		sysfatal("no region big enough for %s partition, needed size %ldMiB", uses[use], minsize/1024/1024);
	}
	reduceregion(i, minsize);
	state.regions[i].why = "it was the first region found which was sufficiently large";
	state.regions[i].use = use;
}

static void
plan(void)
{
	printf("Planning...\n");
	decidebloom();
	decidesizes();
	scatterpicks(state.arenasize, arena);
	scatterpicks(state.indexsize, index);
	if(state.usebloom)
		picklargest(512*1024*1024, bloom);
	for(size_t f = 0; f < state.fossilcount; f += 1)
		picklargest(state.fossilsize, fossil);
}

static void
partition(void)
{
	printf("Partitioning...\n");
	sysfatal("TODO");
}

static void
format(void)
{
	printf("Formatting...\n");
	sysfatal("TODO");
}

static void
execute(void)
{
	partition();
	format();
}

static void
verify(void)
{
	char buf[3];
	printf("Plan:\n");
	dump(NULL);
	printf("Proceed? ");
	fflush(stdout);
	if(fgets(buf, 3, stdin) == NULL || (buf[0] != 'y' && buf[0] != 'Y'))
		sysfatal("Aborting!");
}

int
main(int argc, char **argv)
{
	parseargs(argc, argv);
	opendisks();
	/* don't probe bad regions; however, without probing, some duplicates cannot be detected */
	/* so, first remove bad regions; then probe; then do another check for duplicates */
	removeregion(isclosed, "not a valid disk");
	removeregion(isunused, "useless");
	removeregion(isduplicate, "duplicates");
	removeregion(istoosmall, "too small");
	proberegions();
	removeregion(issubregion, "a subregion of another, known region");
	plan();
	verify();
	execute();
	return 0;
}