arena.c 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649
  1. #include "stdinc.h"
  2. #include "dat.h"
  3. #include "fns.h"
  4. typedef struct ASum ASum;
  5. struct ASum
  6. {
  7. Arena *arena;
  8. ASum *next;
  9. };
  10. static void sealArena(Arena *arena);
  11. static int okArena(Arena *arena);
  12. static int loadArena(Arena *arena);
  13. static CIBlock *getCIB(Arena *arena, int clump, int writing, CIBlock *rock);
  14. static void putCIB(Arena *arena, CIBlock *cib);
  15. static void doASum(void *);
  16. static VtLock *sumLock;
  17. static VtRendez *sumWait;
  18. static ASum *sumq;
  19. int
  20. initArenaSum(void)
  21. {
  22. sumLock = vtLockAlloc();
  23. sumWait = vtRendezAlloc(sumLock);
  24. if(vtThread(doASum, nil) < 0){
  25. setErr(EOk, "can't start arena checksum slave: %R");
  26. return 0;
  27. }
  28. return 1;
  29. }
  30. /*
  31. * make an Arena, and initialize it based upon the disk header and trailer.
  32. */
  33. Arena*
  34. initArena(Part *part, u64int base, u64int size, u32int blockSize)
  35. {
  36. Arena *arena;
  37. arena = MKZ(Arena);
  38. arena->part = part;
  39. arena->blockSize = blockSize;
  40. arena->clumpMax = arena->blockSize / ClumpInfoSize;
  41. arena->base = base + blockSize;
  42. arena->size = size - 2 * blockSize;
  43. arena->lock = vtLockAlloc();
  44. if(!loadArena(arena)){
  45. setErr(ECorrupt, "arena header or trailer corrupted");
  46. freeArena(arena);
  47. return nil;
  48. }
  49. if(!okArena(arena)){
  50. freeArena(arena);
  51. return nil;
  52. }
  53. if(arena->sealed && scoreEq(zeroScore, arena->score))
  54. backSumArena(arena);
  55. return arena;
  56. }
  57. void
  58. freeArena(Arena *arena)
  59. {
  60. if(arena == nil)
  61. return;
  62. if(arena->cib.data != nil){
  63. putDBlock(arena->cib.data);
  64. arena->cib.data = nil;
  65. }
  66. vtLockFree(arena->lock);
  67. free(arena);
  68. }
  69. Arena*
  70. newArena(Part *part, char *name, u64int base, u64int size, u32int blockSize)
  71. {
  72. Arena *arena;
  73. if(!nameOk(name)){
  74. setErr(EOk, "illegal arena name", name);
  75. return nil;
  76. }
  77. arena = MKZ(Arena);
  78. arena->part = part;
  79. arena->version = ArenaVersion;
  80. arena->blockSize = blockSize;
  81. arena->clumpMax = arena->blockSize / ClumpInfoSize;
  82. arena->base = base + blockSize;
  83. arena->size = size - 2 * blockSize;
  84. arena->lock = vtLockAlloc();
  85. nameCp(arena->name, name);
  86. if(!wbArena(arena) || !wbArenaHead(arena)){
  87. freeArena(arena);
  88. return nil;
  89. }
  90. return arena;
  91. }
  92. int
  93. readClumpInfo(Arena *arena, int clump, ClumpInfo *ci)
  94. {
  95. CIBlock *cib, r;
  96. cib = getCIB(arena, clump, 0, &r);
  97. if(cib == nil)
  98. return 0;
  99. unpackClumpInfo(ci, &cib->data->data[cib->offset]);
  100. putCIB(arena, cib);
  101. return 1;
  102. }
  103. int
  104. readClumpInfos(Arena *arena, int clump, ClumpInfo *cis, int n)
  105. {
  106. CIBlock *cib, r;
  107. int i;
  108. for(i = 0; i < n; i++){
  109. cib = getCIB(arena, clump + i, 0, &r);
  110. if(cib == nil)
  111. break;
  112. unpackClumpInfo(&cis[i], &cib->data->data[cib->offset]);
  113. putCIB(arena, cib);
  114. }
  115. return i;
  116. }
  117. /*
  118. * write directory information for one clump
  119. * must be called the arena locked
  120. */
  121. int
  122. writeClumpInfo(Arena *arena, int clump, ClumpInfo *ci)
  123. {
  124. CIBlock *cib, r;
  125. cib = getCIB(arena, clump, 1, &r);
  126. if(cib == nil)
  127. return 0;
  128. packClumpInfo(ci, &cib->data->data[cib->offset]);
  129. putCIB(arena, cib);
  130. return 1;
  131. }
  132. u64int
  133. arenaDirSize(Arena *arena, u32int clumps)
  134. {
  135. return ((clumps / arena->clumpMax) + 1) * arena->blockSize;
  136. }
  137. /*
  138. * read a clump of data
  139. * n is a hint of the size of the data, not including the header
  140. * make sure it won't run off the end, then return the number of bytes actually read
  141. */
  142. u32int
  143. readArena(Arena *arena, u64int aa, u8int *buf, long n)
  144. {
  145. DBlock *b;
  146. u64int a;
  147. u32int blockSize, off, m;
  148. long nn;
  149. if(n == 0)
  150. return 0;
  151. vtLock(arena->lock);
  152. a = arena->size - arenaDirSize(arena, arena->clumps);
  153. vtUnlock(arena->lock);
  154. if(aa >= a){
  155. setErr(EOk, "reading beyond arena clump storage: clumps=%d aa=%lld a=%lld -1 clumps=%lld\n", arena->clumps, aa, a, arena->size - arenaDirSize(arena, arena->clumps - 1));
  156. return 0;
  157. }
  158. if(aa + n > a)
  159. n = a - aa;
  160. blockSize = arena->blockSize;
  161. a = arena->base + aa;
  162. off = a & (blockSize - 1);
  163. a -= off;
  164. nn = 0;
  165. for(;;){
  166. b = getDBlock(arena->part, a, 1);
  167. if(b == nil)
  168. return 0;
  169. m = blockSize - off;
  170. if(m > n - nn)
  171. m = n - nn;
  172. memmove(&buf[nn], &b->data[off], m);
  173. putDBlock(b);
  174. nn += m;
  175. if(nn == n)
  176. break;
  177. off = 0;
  178. a += blockSize;
  179. }
  180. return n;
  181. }
  182. /*
  183. * write some data to the clump section at a given offset
  184. * used to fix up corrupted arenas.
  185. */
  186. u32int
  187. writeArena(Arena *arena, u64int aa, u8int *clbuf, u32int n)
  188. {
  189. DBlock *b;
  190. u64int a;
  191. u32int blockSize, off, m;
  192. long nn;
  193. int ok;
  194. if(n == 0)
  195. return 0;
  196. vtLock(arena->lock);
  197. a = arena->size - arenaDirSize(arena, arena->clumps);
  198. if(aa >= a || aa + n > a){
  199. vtUnlock(arena->lock);
  200. setErr(EOk, "writing beyond arena clump storage");
  201. return 0;
  202. }
  203. blockSize = arena->blockSize;
  204. a = arena->base + aa;
  205. off = a & (blockSize - 1);
  206. a -= off;
  207. nn = 0;
  208. for(;;){
  209. b = getDBlock(arena->part, a, off != 0 || off + n < blockSize);
  210. if(b == nil){
  211. vtUnlock(arena->lock);
  212. return 0;
  213. }
  214. m = blockSize - off;
  215. if(m > n - nn)
  216. m = n - nn;
  217. memmove(&b->data[off], &clbuf[nn], m);
  218. ok = writePart(arena->part, a, b->data, blockSize);
  219. putDBlock(b);
  220. if(!ok){
  221. vtUnlock(arena->lock);
  222. return 0;
  223. }
  224. nn += m;
  225. if(nn == n)
  226. break;
  227. off = 0;
  228. a += blockSize;
  229. }
  230. vtUnlock(arena->lock);
  231. return n;
  232. }
  233. /*
  234. * allocate space for the clump and write it,
  235. * updating the arena directory
  236. ZZZ question: should this distinguish between an arena
  237. filling up and real errors writing the clump?
  238. */
  239. u64int
  240. writeAClump(Arena *arena, Clump *c, u8int *clbuf)
  241. {
  242. DBlock *b;
  243. u64int a, aa;
  244. u32int clump, n, nn, m, off, blockSize;
  245. int ok;
  246. n = c->info.size + ClumpSize;
  247. vtLock(arena->lock);
  248. aa = arena->used;
  249. if(arena->sealed
  250. || aa + n + U32Size + arenaDirSize(arena, arena->clumps + 1) > arena->size){
  251. if(!arena->sealed)
  252. sealArena(arena);
  253. vtUnlock(arena->lock);
  254. return TWID64;
  255. }
  256. if(!packClump(c, &clbuf[0])){
  257. vtUnlock(arena->lock);
  258. return TWID64;
  259. }
  260. /*
  261. * write the data out one block at a time
  262. */
  263. blockSize = arena->blockSize;
  264. a = arena->base + aa;
  265. off = a & (blockSize - 1);
  266. a -= off;
  267. nn = 0;
  268. for(;;){
  269. b = getDBlock(arena->part, a, off != 0);
  270. if(b == nil){
  271. vtUnlock(arena->lock);
  272. return TWID64;
  273. }
  274. m = blockSize - off;
  275. if(m > n - nn)
  276. m = n - nn;
  277. memmove(&b->data[off], &clbuf[nn], m);
  278. ok = writePart(arena->part, a, b->data, blockSize);
  279. putDBlock(b);
  280. if(!ok){
  281. vtUnlock(arena->lock);
  282. return TWID64;
  283. }
  284. nn += m;
  285. if(nn == n)
  286. break;
  287. off = 0;
  288. a += blockSize;
  289. }
  290. arena->used += c->info.size + ClumpSize;
  291. arena->uncsize += c->info.uncsize;
  292. if(c->info.size < c->info.uncsize)
  293. arena->cclumps++;
  294. clump = arena->clumps++;
  295. if(arena->clumps == 0)
  296. fatal("clumps wrapped\n");
  297. arena->wtime = now();
  298. if(arena->ctime == 0)
  299. arena->ctime = arena->wtime;
  300. writeClumpInfo(arena, clump, &c->info);
  301. //ZZZ make this an enum param
  302. if((clump & 0x1ff) == 0x1ff){
  303. flushCIBlocks(arena);
  304. wbArena(arena);
  305. }
  306. vtUnlock(arena->lock);
  307. return aa;
  308. }
  309. /*
  310. * once sealed, an arena never has any data added to it.
  311. * it should only be changed to fix errors.
  312. * this also syncs the clump directory.
  313. */
  314. static void
  315. sealArena(Arena *arena)
  316. {
  317. flushCIBlocks(arena);
  318. arena->sealed = 1;
  319. wbArena(arena);
  320. backSumArena(arena);
  321. }
  322. void
  323. backSumArena(Arena *arena)
  324. {
  325. ASum *as;
  326. if(sumLock == nil)
  327. return;
  328. as = MK(ASum);
  329. if(as == nil)
  330. return;
  331. vtLock(sumLock);
  332. as->arena = arena;
  333. as->next = sumq;
  334. sumq = as;
  335. vtWakeup(sumWait);
  336. vtUnlock(sumLock);
  337. }
  338. static void
  339. doASum(void *unused)
  340. {
  341. ASum *as;
  342. Arena *arena;
  343. if(unused){;}
  344. for(;;){
  345. vtLock(sumLock);
  346. while(sumq == nil)
  347. vtSleep(sumWait);
  348. as = sumq;
  349. sumq = as->next;
  350. vtUnlock(sumLock);
  351. arena = as->arena;
  352. free(as);
  353. sumArena(arena);
  354. }
  355. }
  356. void
  357. sumArena(Arena *arena)
  358. {
  359. ZBlock *b;
  360. VtSha1 *s;
  361. u64int a, e;
  362. u32int bs;
  363. u8int score[VtScoreSize];
  364. bs = MaxIoSize;
  365. if(bs < arena->blockSize)
  366. bs = arena->blockSize;
  367. s = vtSha1Alloc();
  368. if(s == nil){
  369. logErr(EOk, "sumArena can't initialize sha1 state");
  370. return;
  371. }
  372. /*
  373. * read & sum all blocks except the last one
  374. */
  375. vtSha1Init(s);
  376. b = allocZBlock(bs, 0);
  377. e = arena->base + arena->size;
  378. for(a = arena->base - arena->blockSize; a + arena->blockSize <= e; a += bs){
  379. if(a + bs > e)
  380. bs = arena->blockSize;
  381. if(!readPart(arena->part, a, b->data, bs))
  382. goto ReadErr;
  383. vtSha1Update(s, b->data, bs);
  384. }
  385. /*
  386. * the last one is special, since it may already have the checksum included
  387. */
  388. bs = arena->blockSize;
  389. if(!readPart(arena->part, e, b->data, bs)){
  390. ReadErr:
  391. logErr(EOk, "sumArena can't sum %s, read at %lld failed: %r", arena->name, a);
  392. freeZBlock(b);
  393. vtSha1Free(s);
  394. return;
  395. }
  396. vtSha1Update(s, b->data, bs - VtScoreSize);
  397. vtSha1Update(s, zeroScore, VtScoreSize);
  398. vtSha1Final(s, score);
  399. vtSha1Free(s);
  400. /*
  401. * check for no checksum or the same
  402. */
  403. if(!scoreEq(score, &b->data[bs - VtScoreSize])){
  404. if(!scoreEq(zeroScore, &b->data[bs - VtScoreSize]))
  405. logErr(EOk, "overwriting mismatched checksums for arena=%s, found=%V calculated=%V",
  406. arena->name, &b->data[bs - VtScoreSize], score);
  407. scoreCp(&b->data[bs - VtScoreSize], score);
  408. if(!writePart(arena->part, e, b->data, bs))
  409. logErr(EOk, "sumArena can't write sum for %s: %r", arena->name);
  410. }
  411. freeZBlock(b);
  412. vtLock(arena->lock);
  413. scoreCp(arena->score, score);
  414. vtUnlock(arena->lock);
  415. }
  416. /*
  417. * write the arena trailer block to the partition
  418. */
  419. int
  420. wbArena(Arena *arena)
  421. {
  422. ZBlock *b;
  423. int ok;
  424. b = allocZBlock(arena->blockSize, 1);
  425. if(b == nil){
  426. logErr(EAdmin, "can't write arena trailer: %R");
  427. ///ZZZ add error message?
  428. return 0;
  429. }
  430. ok = okArena(arena) && packArena(arena, b->data)
  431. && writePart(arena->part, arena->base + arena->size, b->data, arena->blockSize);
  432. freeZBlock(b);
  433. return ok;
  434. }
  435. int
  436. wbArenaHead(Arena *arena)
  437. {
  438. ZBlock *b;
  439. ArenaHead head;
  440. int ok;
  441. nameCp(head.name, arena->name);
  442. head.version = arena->version;
  443. head.size = arena->size + 2 * arena->blockSize;
  444. head.blockSize = arena->blockSize;
  445. b = allocZBlock(arena->blockSize, 1);
  446. if(b == nil){
  447. logErr(EAdmin, "can't write arena header: %R");
  448. ///ZZZ add error message?
  449. return 0;
  450. }
  451. ok = packArenaHead(&head, b->data)
  452. && writePart(arena->part, arena->base - arena->blockSize, b->data, arena->blockSize);
  453. freeZBlock(b);
  454. return ok;
  455. }
  456. /*
  457. * read the arena header and trailer blocks from disk
  458. */
  459. static int
  460. loadArena(Arena *arena)
  461. {
  462. ArenaHead head;
  463. ZBlock *b;
  464. b = allocZBlock(arena->blockSize, 0);
  465. if(b == nil)
  466. return 0;
  467. if(!readPart(arena->part, arena->base + arena->size, b->data, arena->blockSize)){
  468. freeZBlock(b);
  469. return 0;
  470. }
  471. if(!unpackArena(arena, b->data)){
  472. freeZBlock(b);
  473. return 0;
  474. }
  475. if(arena->version != ArenaVersion){
  476. setErr(EAdmin, "unknown arena version %d", arena->version);
  477. freeZBlock(b);
  478. return 0;
  479. }
  480. scoreCp(arena->score, &b->data[arena->blockSize - VtScoreSize]);
  481. if(!readPart(arena->part, arena->base - arena->blockSize, b->data, arena->blockSize)){
  482. logErr(EAdmin, "can't read arena header: %R");
  483. freeZBlock(b);
  484. return 1;
  485. }
  486. if(!unpackArenaHead(&head, b->data))
  487. logErr(ECorrupt, "corrupted arena header: %R");
  488. else if(!nameEq(arena->name, head.name)
  489. || arena->version != head.version
  490. || arena->blockSize != head.blockSize
  491. || arena->size + 2 * arena->blockSize != head.size)
  492. logErr(ECorrupt, "arena header inconsistent with arena data");
  493. freeZBlock(b);
  494. return 1;
  495. }
  496. static int
  497. okArena(Arena *arena)
  498. {
  499. u64int dsize;
  500. int ok;
  501. ok = 1;
  502. dsize = arenaDirSize(arena, arena->clumps);
  503. if(arena->used + dsize > arena->size){
  504. setErr(ECorrupt, "arena used > size");
  505. ok = 0;
  506. }
  507. if(arena->cclumps > arena->clumps)
  508. logErr(ECorrupt, "arena has more compressed clumps than total clumps");
  509. if(arena->uncsize + arena->clumps * ClumpSize + arena->blockSize < arena->used)
  510. logErr(ECorrupt, "arena uncompressed size inconsistent with used space %lld %d %lld", arena->uncsize, arena->clumps, arena->used);
  511. if(arena->ctime > arena->wtime)
  512. logErr(ECorrupt, "arena creation time after last write time");
  513. return ok;
  514. }
  515. static CIBlock*
  516. getCIB(Arena *arena, int clump, int writing, CIBlock *rock)
  517. {
  518. CIBlock *cib;
  519. u32int block, off;
  520. if(clump >= arena->clumps){
  521. setErr(EOk, "clump directory access out of range");
  522. return nil;
  523. }
  524. block = clump / arena->clumpMax;
  525. off = (clump - block * arena->clumpMax) * ClumpInfoSize;
  526. if(arena->cib.block == block
  527. && arena->cib.data != nil){
  528. arena->cib.offset = off;
  529. return &arena->cib;
  530. }
  531. if(writing){
  532. flushCIBlocks(arena);
  533. cib = &arena->cib;
  534. }else
  535. cib = rock;
  536. vtLock(stats.lock);
  537. stats.ciReads++;
  538. vtUnlock(stats.lock);
  539. cib->block = block;
  540. cib->offset = off;
  541. cib->data = getDBlock(arena->part, arena->base + arena->size - (block + 1) * arena->blockSize, arena->blockSize);
  542. if(cib->data == nil)
  543. return nil;
  544. return cib;
  545. }
  546. static void
  547. putCIB(Arena *arena, CIBlock *cib)
  548. {
  549. if(cib != &arena->cib){
  550. putDBlock(cib->data);
  551. cib->data = nil;
  552. }
  553. }
  554. /*
  555. * must be called with arena locked
  556. */
  557. int
  558. flushCIBlocks(Arena *arena)
  559. {
  560. int ok;
  561. if(arena->cib.data == nil)
  562. return 1;
  563. vtLock(stats.lock);
  564. stats.ciWrites++;
  565. vtUnlock(stats.lock);
  566. ok = writePart(arena->part, arena->base + arena->size - (arena->cib.block + 1) * arena->blockSize, arena->cib.data->data, arena->blockSize);
  567. if(!ok)
  568. setErr(EAdmin, "failed writing arena directory block");
  569. putDBlock(arena->cib.data);
  570. arena->cib.data = nil;
  571. return ok;
  572. }