ref: e8ed35d914bdf4445a4e915ca361f6eb26bbd611
dir: /partitioner/main.c/
#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;
}