shim_dcache.c 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535
  1. /* -*- mode:c; c-file-style:"k&r"; c-basic-offset: 4; tab-width:4; indent-tabs-mode:nil; mode:auto-fill; fill-column:78; -*- */
  2. /* vim: set ts=4 sw=4 et tw=78 fo=cqt wm=0: */
  3. /* Copyright (C) 2014 OSCAR lab, Stony Brook University
  4. This file is part of Graphene Library OS.
  5. Graphene Library OS is free software: you can redistribute it and/or
  6. modify it under the terms of the GNU General Public License
  7. as published by the Free Software Foundation, either version 3 of the
  8. License, or (at your option) any later version.
  9. Graphene Library OS is distributed in the hope that it will be useful,
  10. but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. GNU General Public License for more details.
  13. You should have received a copy of the GNU General Public License
  14. along with this program. If not, see <http://www.gnu.org/licenses/>. */
  15. /*
  16. * shim_dcache.c
  17. *
  18. * This file contains codes for maintaining directory cache in library OS.
  19. * The source codes are imported from Linux kernel, but simplified according
  20. * to the characteristic of library OS.
  21. */
  22. #include <shim_types.h>
  23. #include <shim_internal.h>
  24. #include <shim_handle.h>
  25. #include <shim_fs.h>
  26. #include <shim_checkpoint.h>
  27. #include <list.h>
  28. /* As best I can tell, dentries are added to this list and then never touched
  29. * again */
  30. /* Links to shim_dentry->list */
  31. static LISTP_TYPE(shim_dentry) dentry_list = LISTP_INIT;
  32. /* Attaches to shim_dentry->hlist */
  33. static LISTP_TYPE(shim_dentry) dcache_htable[DCACHE_HASH_SIZE] = { LISTP_INIT };
  34. LOCKTYPE dcache_lock;
  35. struct shim_dcache_stats {
  36. long memsize;
  37. long nentries;
  38. };
  39. static struct shim_dcache_stats dcache_stats;
  40. long get_dcache_stats (const char * name)
  41. {
  42. if (strcmp_static(name, "memsize"))
  43. return dcache_stats.memsize;
  44. if (strcmp_static(name, "nentries"))
  45. return dcache_stats.nentries;
  46. return 0;
  47. }
  48. #define DCACHE_MGR_ALLOC 64
  49. #define PAGE_SIZE allocsize
  50. #define OBJ_TYPE struct shim_dentry
  51. #include <memmgr.h>
  52. static MEM_MGR dentry_mgr = NULL;
  53. struct shim_dentry * dentry_root = NULL;
  54. //#define DEBUG_DCACHE
  55. //#define DEBUG_REF
  56. static struct shim_dentry * alloc_dentry (void)
  57. {
  58. struct shim_dentry * dent =
  59. get_mem_obj_from_mgr_enlarge(dentry_mgr,
  60. size_align_up(DCACHE_MGR_ALLOC));
  61. if (!dent)
  62. return NULL;
  63. dcache_stats.memsize += sizeof(struct shim_dentry);
  64. dcache_stats.nentries++;
  65. memset(dent, 0, sizeof(struct shim_dentry));
  66. dent->mode = NO_MODE;
  67. INIT_LIST_HEAD(dent, hlist);
  68. INIT_LIST_HEAD(dent, list);
  69. INIT_LISTP(&dent->children);
  70. INIT_LIST_HEAD(dent, siblings);
  71. if (locked(dcache_lock)) {
  72. listp_add(dent, &dentry_list, list);
  73. } else {
  74. lock(dcache_lock);
  75. listp_add(dent, &dentry_list, list);
  76. unlock(dcache_lock);
  77. }
  78. return dent;
  79. }
  80. DEFINE_PROFILE_CATAGORY(dcache, );
  81. DEFINE_PROFILE_INTERVAL(total_init_dcache, dcache);
  82. DEFINE_PROFILE_CATAGORY(within_init_dcache, dcache);
  83. DEFINE_PROFILE_INTERVAL(dcache_init_memory, within_init_dcache);
  84. DEFINE_PROFILE_INTERVAL(dcache_init_lock, within_init_dcache);
  85. DEFINE_PROFILE_INTERVAL(dcache_init_root_entry, within_init_dcache);
  86. int init_dcache (void)
  87. {
  88. #ifdef PROFILE
  89. unsigned long begin_time = GET_PROFILE_INTERVAL();
  90. BEGIN_PROFILE_INTERVAL_SET(begin_time);
  91. #endif
  92. dentry_mgr = create_mem_mgr(init_align_up(DCACHE_MGR_ALLOC));
  93. SAVE_PROFILE_INTERVAL(dcache_init_memory);
  94. create_lock(dcache_lock);
  95. SAVE_PROFILE_INTERVAL(dcache_init_lock);
  96. dentry_root = alloc_dentry();
  97. qstrsetstr(&dentry_root->rel_path, "", 0);
  98. qstrsetstr(&dentry_root->name, "", 0);
  99. get_dentry(dentry_root);
  100. SAVE_PROFILE_INTERVAL(dcache_init_root_entry);
  101. SAVE_PROFILE_INTERVAL_SINCE(total_init_dcache, begin_time);
  102. return 0;
  103. }
  104. int reinit_dcache (void)
  105. {
  106. create_lock(dcache_lock);
  107. return 0;
  108. }
  109. /* remove from the hash table, so that a lookup will fail. */
  110. static void __del_dcache (struct shim_dentry * dent)
  111. {
  112. if (!(dent->state & DENTRY_HASHED))
  113. return;
  114. /* DEP 5/15/17: I believe that, if a dentry is in the DENTRY_HASHED state,
  115. * that the hash field is always valid. */
  116. LISTP_TYPE(shim_dentry) * head = &dcache_htable[DCACHE_HASH(dent->rel_path.hash)];
  117. dent->state &= ~DENTRY_HASHED;
  118. listp_del_init(dent, head, hlist);
  119. #ifdef DEBUG_DCACHE
  120. debug("del dcache %p(%s/%s) (mount = %p)\n",
  121. dent, dent->fs ? qstrgetstr(&dent->fs->path) : "",
  122. qstrgetstr(&dent->rel_path), dent->fs);
  123. #endif
  124. }
  125. static int __internal_put_dentry (struct shim_dentry * dent);
  126. /* Release a dentry whose ref count has gone to zero. */
  127. static inline void __dput_dentry (struct shim_dentry * dent)
  128. {
  129. while (1) {
  130. /* if the dentry is never in the hash table, we are happy to
  131. drop it */
  132. if (!(dent->state & DENTRY_HASHED))
  133. goto kill;
  134. /* move the node to unused list unless it is persistent */
  135. if (!(dent->state & DENTRY_PERSIST)) {
  136. dent->state |= DENTRY_RECENTLY;
  137. /* DEP: The only reference I see to this field is being placed on
  138. * the unused list. It seems that sometimes the dent is not
  139. * on a list; just hack this for now; this code will be reworked
  140. * soon anyway.
  141. */
  142. /* Chia-Che 10/17/17: Originally we place all the freed
  143. * dentries on an unused list, now we just maintain a long list
  144. * of all dentries. */
  145. }
  146. /* we don't delete the dentry from dcache because it might
  147. be acquired and used again, unless it gets recycled due
  148. to memory pressure */
  149. break;
  150. kill: {
  151. if (dent->fs && dent->fs->d_ops && dent->fs->d_ops->dput)
  152. dent->fs->d_ops->dput(dent);
  153. struct shim_dentry * parent = dent->parent;
  154. if (!parent)
  155. break;
  156. listp_del(dent, &parent->children, siblings); /* remove from parent's list of children */
  157. listp_del(dent, &dentry_list, list);
  158. /* we can free this dentry */
  159. free_mem_obj_to_mgr(dentry_mgr, dent);
  160. dent = parent;
  161. if (__internal_put_dentry(dent))
  162. break;
  163. }
  164. }
  165. }
  166. static int __internal_put_dentry (struct shim_dentry * dent)
  167. {
  168. int count = REF_DEC(dent->ref_count);
  169. #ifdef DEBUG_REF
  170. debug("put dentry %p(%s/%s) (ref_count = %d)\n", dent,
  171. dent->fs ?
  172. qstrgetstr(&dent->fs->path) : "",
  173. qstrgetstr(&dent->rel_path), count);
  174. #endif
  175. return count;
  176. }
  177. void get_dentry (struct shim_dentry * dent)
  178. {
  179. #ifdef DEBUG_REF
  180. int count = REF_INC(dent->ref_count);
  181. debug("get dentry %p(%s/%s) (ref_count = %d)\n", dent,
  182. dent->fs ?
  183. qstrgetstr(&dent->fs->path) : "",
  184. qstrgetstr(&dent->rel_path), count);
  185. #else
  186. REF_INC(dent->ref_count);
  187. #endif
  188. }
  189. void put_dentry (struct shim_dentry * dent)
  190. {
  191. if (__internal_put_dentry(dent))
  192. return;
  193. if (locked(dcache_lock)) {
  194. __dput_dentry(dent);
  195. } else {
  196. lock(dcache_lock);
  197. __dput_dentry(dent);
  198. unlock(dcache_lock);
  199. }
  200. }
  201. struct shim_dentry * get_new_dentry (struct shim_dentry * parent,
  202. const char * name, int namelen)
  203. {
  204. struct shim_dentry * dent = alloc_dentry();
  205. if (!dent)
  206. return NULL;
  207. REF_SET(dent->ref_count, 0);
  208. qstrsetstr(&dent->name, name, namelen);
  209. if (!parent) {
  210. qstrsetstr(&dent->rel_path, name, namelen);
  211. return dent;
  212. }
  213. if (!qstrempty(&parent->rel_path)) {
  214. const char * strs[] = { qstrgetstr(&parent->rel_path), "/", name };
  215. size_t lens[] = { parent->rel_path.len, 1, namelen };
  216. qstrsetstrs(&dent->rel_path, 3, strs, lens);
  217. } else
  218. qstrsetstr(&dent->rel_path, name, namelen);
  219. return dent;
  220. }
  221. void __set_parent_dentry (struct shim_dentry * child,
  222. struct shim_dentry * parent)
  223. {
  224. if (child->parent == parent)
  225. return;
  226. assert(!child->parent);
  227. get_dentry(parent);
  228. listp_add_tail(child, &parent->children, siblings);
  229. child->parent = parent;
  230. parent->nchildren++;
  231. }
  232. void __unset_parent_dentry (struct shim_dentry * child,
  233. struct shim_dentry * parent)
  234. {
  235. if (child->parent != parent)
  236. return;
  237. assert(child->parent);
  238. listp_del_init(child, &parent->children, siblings);
  239. child->parent = NULL;
  240. parent->nchildren--;
  241. put_dentry(parent);
  242. }
  243. static inline
  244. HASHTYPE hash_dentry (struct shim_dentry * start, const char * path, int len)
  245. {
  246. return rehash_path(start ? start->rel_path.hash : 0,
  247. path, len, NULL);
  248. }
  249. void __add_dcache (struct shim_dentry * dent, HASHTYPE * hashptr)
  250. {
  251. LISTP_TYPE(shim_dentry) * head;
  252. if (hashptr) {
  253. dent->rel_path.hash = *hashptr;
  254. goto add_hash;
  255. }
  256. if (!dent->parent) {
  257. dent->rel_path.hash = dent->fs ? dent->fs->path.hash : 0;
  258. goto add_hash;
  259. }
  260. dent->rel_path.hash = hash_dentry(dent->parent, dentry_get_name(dent),
  261. dent->name.len);
  262. add_hash:
  263. head = &dcache_htable[DCACHE_HASH(dent->rel_path.hash)];
  264. listp_add(dent, head, hlist);
  265. dent->state |= DENTRY_HASHED;
  266. #ifdef DEBUG_DCACHE
  267. debug("add dcache %p(%s/%s) (mount = %p)\n",
  268. dent, dent->fs ? qstrgetstr(&dent->fs->path) : "",
  269. qstrgetstr(&dent->rel_path), dent->fs);
  270. #endif
  271. }
  272. void add_dcache (struct shim_dentry * dent, HASHTYPE * hashptr)
  273. {
  274. lock(dcache_lock);
  275. __add_dcache(dent, hashptr);
  276. unlock(dcache_lock);
  277. }
  278. struct shim_dentry *
  279. __lookup_dcache (struct shim_dentry * start, const char * name, int namelen,
  280. const char * path, int pathlen, HASHTYPE * hashptr)
  281. {
  282. HASHTYPE hash = hash_dentry(start, name, namelen);
  283. struct shim_dentry * dent, * found = NULL;
  284. LISTP_TYPE(shim_dentry) * head = &dcache_htable[DCACHE_HASH(hash)];
  285. /* walk through all the nodes in the hash bucket, find the droids we're
  286. looking for */
  287. listp_for_each_entry(dent, head, hlist) {
  288. if ((dent->state & DENTRY_MOUNTPOINT) ||
  289. dent->rel_path.hash != hash)
  290. continue;
  291. /* first we compare the filename */
  292. const char * filename = get_file_name(name, namelen);
  293. if (memcmp(dentry_get_name(dent), filename, name + namelen - filename))
  294. continue;
  295. if (filename == name) {
  296. struct shim_dentry * d = dent;
  297. while (d && !d->parent && d->fs)
  298. d = d->fs->mount_point;
  299. if (d && d->parent && d->parent != start)
  300. continue;
  301. }
  302. if (path && pathlen && filename != path) {
  303. const char * fullpath;
  304. int fullpathlen;
  305. fullpath = dentry_get_path(dent, true, &fullpathlen);
  306. if (pathlen > fullpathlen)
  307. continue;
  308. fullpath += fullpathlen - pathlen;
  309. if (fullpath[-1] != '/')
  310. continue;
  311. if (memcmp(fullpath, path, pathlen))
  312. continue;
  313. }
  314. get_dentry(dent);
  315. found = dent;
  316. break;
  317. }
  318. if (hashptr)
  319. *hashptr = hash;
  320. return found;
  321. }
  322. /* after lookup_dcache, the dentry is popped to prevent recycling */
  323. struct shim_dentry *
  324. lookup_dcache (struct shim_dentry * start, const char * name, int namelen,
  325. const char * path, int pathlen, HASHTYPE * hashptr)
  326. {
  327. lock(dcache_lock);
  328. struct shim_dentry * dent = __lookup_dcache(start, name, namelen, path,
  329. pathlen, hashptr);
  330. unlock(dcache_lock);
  331. return dent;
  332. }
  333. int __del_dentry_tree (struct shim_dentry * root)
  334. {
  335. struct shim_dentry * this_parent = root;
  336. struct shim_dentry * next;
  337. repeat:
  338. next = this_parent->children.first;
  339. resume:
  340. while (next != this_parent->children.first) {
  341. struct shim_dentry *d, * tmp;
  342. d = tmp = next;
  343. next = tmp->siblings.next;
  344. if (d->state & DENTRY_MOUNTPOINT) {
  345. this_parent = d->mounted->root;
  346. goto repeat;
  347. }
  348. if (!listp_empty(&d->children)) {
  349. this_parent = d;
  350. goto repeat;
  351. }
  352. __unset_parent_dentry(d, this_parent);
  353. __del_dcache(d);
  354. }
  355. if (this_parent != root) {
  356. struct shim_dentry * child = this_parent;
  357. if (!this_parent->parent) {
  358. this_parent = this_parent->fs->mount_point;
  359. __del_dcache(child);
  360. child = this_parent;
  361. }
  362. this_parent = this_parent->parent;
  363. next = child->siblings.next;
  364. __del_dcache(child);
  365. __unset_parent_dentry(child, this_parent);
  366. goto resume;
  367. }
  368. return 0;
  369. }
  370. BEGIN_CP_FUNC(dentry)
  371. {
  372. assert(size == sizeof(struct shim_dentry));
  373. struct shim_dentry * dent = (struct shim_dentry *) obj;
  374. struct shim_dentry * new_dent = NULL;
  375. ptr_t off = GET_FROM_CP_MAP(obj);
  376. if (!off) {
  377. off = ADD_CP_OFFSET(sizeof(struct shim_dentry));
  378. ADD_TO_CP_MAP(obj, off);
  379. new_dent = (struct shim_dentry *) (base + off);
  380. lock(dent->lock);
  381. *new_dent = *dent;
  382. INIT_LIST_HEAD(new_dent, hlist);
  383. INIT_LIST_HEAD(new_dent, list);
  384. INIT_LISTP(&new_dent->children);
  385. INIT_LIST_HEAD(new_dent, siblings);
  386. new_dent->data = NULL;
  387. clear_lock(new_dent->lock);
  388. REF_SET(new_dent->ref_count, 0);
  389. DO_CP_IN_MEMBER(qstr, new_dent, rel_path);
  390. DO_CP_IN_MEMBER(qstr, new_dent, name);
  391. if (dent->fs)
  392. DO_CP_MEMBER(mount, dent, new_dent, fs);
  393. if (dent->parent)
  394. DO_CP_MEMBER(dentry, dent, new_dent, parent);
  395. if (dent->mounted)
  396. DO_CP_MEMBER(mount, dent, new_dent, mounted);
  397. unlock(dent->lock);
  398. ADD_CP_FUNC_ENTRY(off);
  399. } else {
  400. new_dent = (struct shim_dentry *) (base + off);
  401. }
  402. if (objp)
  403. *objp = (void *) new_dent;
  404. }
  405. END_CP_FUNC(dentry)
  406. BEGIN_RS_FUNC(dentry)
  407. {
  408. struct shim_dentry * dent = (void *) (base + GET_CP_FUNC_ENTRY());
  409. BEGIN_PROFILE_INTERVAL();
  410. CP_REBASE(dent->hlist);
  411. CP_REBASE(dent->list);
  412. CP_REBASE(dent->children);
  413. CP_REBASE(dent->siblings);
  414. CP_REBASE(dent->fs);
  415. CP_REBASE(dent->parent);
  416. CP_REBASE(dent->mounted);
  417. create_lock(dent->lock);
  418. if (dent->parent)
  419. __set_parent_dentry(dent, dent->parent);
  420. LISTP_TYPE(shim_dentry) * head = &dcache_htable[DCACHE_HASH(dent->rel_path.hash)];
  421. listp_add(dent, head, hlist);
  422. dent->state |= DENTRY_HASHED;
  423. DEBUG_RS("hash=%08x,path=%s,fs=%s", dent->rel_path.hash,
  424. dentry_get_path(dent, true, NULL),
  425. dent->fs ? qstrgetstr(&dent->fs->path) : NULL);
  426. }
  427. END_RS_FUNC(dentry)