shim_dcache.c 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522
  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 <linux_list.h>
  28. static LIST_HEAD(dcache_list);
  29. static LIST_HEAD(unused);
  30. static LIST_HEAD(persistent);
  31. static struct hlist_head dcache_htable[DCACHE_HASH_SIZE] = { HLIST_HEAD_INIT };
  32. LOCKTYPE dcache_lock;
  33. struct shim_dcache_stats {
  34. long memsize;
  35. long nentries;
  36. };
  37. static struct shim_dcache_stats dcache_stats;
  38. long get_dcache_stats (const char * name)
  39. {
  40. if (strcmp_static(name, "memsize"))
  41. return dcache_stats.memsize;
  42. if (strcmp_static(name, "nentries"))
  43. return dcache_stats.nentries;
  44. return 0;
  45. }
  46. #define DCACHE_MGR_ALLOC 64
  47. #define PAGE_SIZE allocsize
  48. #define OBJ_TYPE struct shim_dentry
  49. #include <memmgr.h>
  50. static MEM_MGR dentry_mgr = NULL;
  51. struct shim_dentry * dentry_root = NULL;
  52. //#define DEBUG_DCACHE
  53. //#define DEBUG_REF
  54. static struct shim_dentry * alloc_dentry (void)
  55. {
  56. struct shim_dentry * dent =
  57. get_mem_obj_from_mgr_enlarge(dentry_mgr,
  58. size_align_up(DCACHE_MGR_ALLOC));
  59. if (!dent)
  60. return NULL;
  61. dcache_stats.memsize += sizeof(struct shim_dentry);
  62. dcache_stats.nentries++;
  63. memset(dent, 0, sizeof(struct shim_dentry));
  64. dent->mode = NO_MODE;
  65. INIT_HLIST_NODE(&dent->hlist);
  66. INIT_LIST_HEAD(&dent->list);
  67. INIT_LIST_HEAD(&dent->children);
  68. INIT_LIST_HEAD(&dent->siblings);
  69. return dent;
  70. }
  71. DEFINE_PROFILE_CATAGORY(dcache, );
  72. DEFINE_PROFILE_INTERVAL(total_init_dcache, dcache);
  73. DEFINE_PROFILE_CATAGORY(within_init_dcache, dcache);
  74. DEFINE_PROFILE_INTERVAL(dcache_init_memory, within_init_dcache);
  75. DEFINE_PROFILE_INTERVAL(dcache_init_lock, within_init_dcache);
  76. DEFINE_PROFILE_INTERVAL(dcache_init_root_entry, within_init_dcache);
  77. int init_dcache (void)
  78. {
  79. #ifdef PROFILE
  80. unsigned long begin_time = GET_PROFILE_INTERVAL();
  81. BEGIN_PROFILE_INTERVAL_SET(begin_time);
  82. #endif
  83. dentry_mgr = create_mem_mgr(init_align_up(DCACHE_MGR_ALLOC));
  84. SAVE_PROFILE_INTERVAL(dcache_init_memory);
  85. create_lock(dcache_lock);
  86. SAVE_PROFILE_INTERVAL(dcache_init_lock);
  87. dentry_root = alloc_dentry();
  88. qstrsetstr(&dentry_root->rel_path, "", 0);
  89. qstrsetstr(&dentry_root->name, "", 0);
  90. get_dentry(dentry_root);
  91. SAVE_PROFILE_INTERVAL(dcache_init_root_entry);
  92. SAVE_PROFILE_INTERVAL_SINCE(total_init_dcache, begin_time);
  93. return 0;
  94. }
  95. int reinit_dcache (void)
  96. {
  97. create_lock(dcache_lock);
  98. return 0;
  99. }
  100. /* remove from the hash table, so that a lookup will fail. */
  101. void __del_dcache (struct shim_dentry * dent)
  102. {
  103. if (!(dent->state & DENTRY_HASHED))
  104. return;
  105. dent->state &= ~DENTRY_HASHED;
  106. hlist_del_init(&dent->hlist);
  107. #ifdef DEBUG_DCACHE
  108. debug("del dcache %p(%s/%s) (mount = %p)\n",
  109. dent, dent->fs ? qstrgetstr(&dent->fs->path) : "",
  110. qstrgetstr(&dent->rel_path), dent->fs);
  111. #endif
  112. }
  113. static int __internal_put_dentry (struct shim_dentry * dent);
  114. void del_dcache (struct shim_dentry * dent)
  115. {
  116. lock(dcache_lock);
  117. __del_dcache(dent);
  118. unlock(dcache_lock);
  119. }
  120. static inline void __dput_dentry (struct shim_dentry * dent)
  121. {
  122. while (1) {
  123. /* if the dentry is never in the hash table, we are happy to
  124. drop it */
  125. if (!(dent->state & DENTRY_HASHED))
  126. goto kill;
  127. /* move the node to unused list unless it is persistent */
  128. if (!(dent->state & DENTRY_PERSIST)) {
  129. dent->state |= DENTRY_RECENTLY;
  130. list_del(&dent->list);
  131. INIT_LIST_HEAD(&dent->list);
  132. list_add(&dent->list, &unused);
  133. }
  134. /* we don't delete the dentry from dcache because it might
  135. be acquired and used again, unless it gets recycled due
  136. to memory pressure */
  137. break;
  138. kill: {
  139. if (dent->fs && dent->fs->d_ops && dent->fs->d_ops->dput)
  140. dent->fs->d_ops->dput(dent);
  141. struct shim_dentry * parent = dent->parent;
  142. if (!parent)
  143. break;
  144. list_del(&dent->siblings); /* remove from parent's list of children */
  145. dent->parent = NULL;
  146. dent = parent;
  147. if (__internal_put_dentry(dent))
  148. break;
  149. }
  150. }
  151. }
  152. static int __internal_put_dentry (struct shim_dentry * dent)
  153. {
  154. int count = REF_DEC(dent->ref_count);
  155. #ifdef DEBUG_REF
  156. debug("put dentry %p(%s/%s) (ref_count = %d)\n", dent,
  157. dent->fs ?
  158. qstrgetstr(&dent->fs->path) : "",
  159. qstrgetstr(&dent->rel_path), count);
  160. #endif
  161. return count;
  162. }
  163. void get_dentry (struct shim_dentry * dent)
  164. {
  165. #ifdef DEBUG_REF
  166. int count = REF_INC(dent->ref_count);
  167. debug("get dentry %p(%s/%s) (ref_count = %d)\n", dent,
  168. dent->fs ?
  169. qstrgetstr(&dent->fs->path) : "",
  170. qstrgetstr(&dent->rel_path), count);
  171. #else
  172. REF_INC(dent->ref_count);
  173. #endif
  174. }
  175. void put_dentry (struct shim_dentry * dent)
  176. {
  177. if (__internal_put_dentry(dent))
  178. return;
  179. if (locked(dcache_lock)) {
  180. __dput_dentry(dent);
  181. } else {
  182. lock(dcache_lock);
  183. __dput_dentry(dent);
  184. unlock(dcache_lock);
  185. }
  186. }
  187. struct shim_dentry * get_new_dentry (struct shim_dentry * parent,
  188. const char * name, int namelen)
  189. {
  190. struct shim_dentry * dent = alloc_dentry();
  191. if (!dent)
  192. return NULL;
  193. REF_SET(dent->ref_count, 0);
  194. qstrsetstr(&dent->name, name, namelen);
  195. if (!parent) {
  196. qstrsetstr(&dent->rel_path, name, namelen);
  197. return dent;
  198. }
  199. if (!qstrempty(&parent->rel_path)) {
  200. const char * strs[] = { qstrgetstr(&parent->rel_path), "/", name };
  201. size_t lens[] = { parent->rel_path.len, 1, namelen };
  202. qstrsetstrs(&dent->rel_path, 3, strs, lens);
  203. } else
  204. qstrsetstr(&dent->rel_path, name, namelen);
  205. return dent;
  206. }
  207. void __set_parent_dentry (struct shim_dentry * child,
  208. struct shim_dentry * parent)
  209. {
  210. if (child->parent == parent)
  211. return;
  212. assert(!child->parent);
  213. get_dentry(parent);
  214. list_add_tail(&child->siblings, &parent->children);
  215. child->parent = parent;
  216. parent->nchildren++;
  217. }
  218. void __unset_parent_dentry (struct shim_dentry * child,
  219. struct shim_dentry * parent)
  220. {
  221. if (child->parent != parent)
  222. return;
  223. assert(child->parent);
  224. child->parent = NULL;
  225. list_del_init(&child->siblings);
  226. parent->nchildren--;
  227. put_dentry(parent);
  228. }
  229. static inline
  230. HASHTYPE hash_dentry (struct shim_dentry * start, const char * path, int len)
  231. {
  232. return rehash_path(start ? start->rel_path.hash : 0,
  233. path, len, NULL);
  234. }
  235. void __add_dcache (struct shim_dentry * dent, HASHTYPE * hashptr)
  236. {
  237. struct hlist_head * head;
  238. if (hashptr) {
  239. dent->rel_path.hash = *hashptr;
  240. goto add_hash;
  241. }
  242. if (!dent->parent) {
  243. dent->rel_path.hash = dent->fs ? dent->fs->path.hash : 0;
  244. goto add_hash;
  245. }
  246. dent->rel_path.hash = hash_dentry(dent->parent, dentry_get_name(dent),
  247. dent->name.len);
  248. add_hash:
  249. head = &dcache_htable[DCACHE_HASH(dent->rel_path.hash)];
  250. hlist_add_head(&dent->hlist, head);
  251. dent->state |= DENTRY_HASHED;
  252. #ifdef DEBUG_DCACHE
  253. debug("add dcache %p(%s/%s) (mount = %p)\n",
  254. dent, dent->fs ? qstrgetstr(&dent->fs->path) : "",
  255. qstrgetstr(&dent->rel_path), dent->fs);
  256. #endif
  257. }
  258. void add_dcache (struct shim_dentry * dent, HASHTYPE * hashptr)
  259. {
  260. lock(dcache_lock);
  261. __add_dcache(dent, hashptr);
  262. unlock(dcache_lock);
  263. }
  264. struct shim_dentry *
  265. __lookup_dcache (struct shim_dentry * start, const char * name, int namelen,
  266. const char * path, int pathlen, HASHTYPE * hashptr)
  267. {
  268. HASHTYPE hash = hash_dentry(start, name, namelen);
  269. struct shim_dentry * dent, * found = NULL;
  270. struct hlist_node * node;
  271. struct hlist_head * head = &dcache_htable[DCACHE_HASH(hash)];
  272. /* walk through all the nodes in the hash bucket, find the droids we're
  273. looking for */
  274. hlist_for_each_entry(dent, node, head, hlist) {
  275. if ((dent->state & DENTRY_MOUNTPOINT) ||
  276. dent->rel_path.hash != hash)
  277. continue;
  278. /* first we compare the filename */
  279. const char * filename = get_file_name(name, namelen);
  280. if (memcmp(dentry_get_name(dent), filename, name + namelen - filename))
  281. continue;
  282. if (filename == name) {
  283. struct shim_dentry * d = dent;
  284. while (d && !d->parent && d->fs)
  285. d = d->fs->mount_point;
  286. if (d && d->parent && d->parent != start)
  287. continue;
  288. }
  289. if (path && pathlen && filename != path) {
  290. const char * fullpath;
  291. int fullpathlen;
  292. fullpath = dentry_get_path(dent, true, &fullpathlen);
  293. if (pathlen > fullpathlen)
  294. continue;
  295. fullpath += fullpathlen - pathlen;
  296. if (fullpath[-1] != '/')
  297. continue;
  298. if (memcmp(fullpath, path, pathlen))
  299. continue;
  300. }
  301. get_dentry(dent);
  302. found = dent;
  303. break;
  304. }
  305. if (hashptr)
  306. *hashptr = hash;
  307. return found;
  308. }
  309. /* after lookup_dcache, the dentry is popped to prevent recycling */
  310. struct shim_dentry *
  311. lookup_dcache (struct shim_dentry * start, const char * name, int namelen,
  312. const char * path, int pathlen, HASHTYPE * hashptr)
  313. {
  314. lock(dcache_lock);
  315. struct shim_dentry * dent = __lookup_dcache(start, name, namelen, path,
  316. pathlen, hashptr);
  317. unlock(dcache_lock);
  318. return dent;
  319. }
  320. int __del_dentry_tree (struct shim_dentry * root)
  321. {
  322. struct shim_dentry * this_parent = root;
  323. struct list_head * next;
  324. repeat:
  325. next = this_parent->children.next;
  326. resume:
  327. while (next != &this_parent->children) {
  328. struct list_head * tmp = next;
  329. struct shim_dentry * d = list_entry(tmp, struct shim_dentry,
  330. siblings);
  331. next = tmp->next;
  332. if (d->state & DENTRY_MOUNTPOINT) {
  333. this_parent = d->mounted->root;
  334. goto repeat;
  335. }
  336. if (!list_empty(&d->children)) {
  337. this_parent = d;
  338. goto repeat;
  339. }
  340. __unset_parent_dentry(d, this_parent);
  341. __del_dcache(d);
  342. }
  343. if (this_parent != root) {
  344. struct shim_dentry * child = this_parent;
  345. if (!this_parent->parent) {
  346. this_parent = this_parent->fs->mount_point;
  347. __del_dcache(child);
  348. child = this_parent;
  349. }
  350. this_parent = this_parent->parent;
  351. next = child->siblings.next;
  352. __del_dcache(child);
  353. __unset_parent_dentry(child, this_parent);
  354. goto resume;
  355. }
  356. return 0;
  357. }
  358. BEGIN_CP_FUNC(dentry)
  359. {
  360. assert(size == sizeof(struct shim_dentry));
  361. struct shim_dentry * dent = (struct shim_dentry *) obj;
  362. struct shim_dentry * new_dent = NULL;
  363. ptr_t off = GET_FROM_CP_MAP(obj);
  364. if (!off) {
  365. off = ADD_CP_OFFSET(sizeof(struct shim_dentry));
  366. ADD_TO_CP_MAP(obj, off);
  367. new_dent = (struct shim_dentry *) (base + off);
  368. lock(dent->lock);
  369. *new_dent = *dent;
  370. INIT_HLIST_NODE(&new_dent->hlist);
  371. INIT_LIST_HEAD(&new_dent->list);
  372. INIT_LIST_HEAD(&new_dent->children);
  373. INIT_LIST_HEAD(&new_dent->siblings);
  374. new_dent->data = NULL;
  375. clear_lock(new_dent->lock);
  376. REF_SET(new_dent->ref_count, 0);
  377. DO_CP_IN_MEMBER(qstr, new_dent, rel_path);
  378. DO_CP_IN_MEMBER(qstr, new_dent, name);
  379. if (dent->fs)
  380. DO_CP_MEMBER(mount, dent, new_dent, fs);
  381. if (dent->parent)
  382. DO_CP_MEMBER(dentry, dent, new_dent, parent);
  383. if (dent->mounted)
  384. DO_CP_MEMBER(mount, dent, new_dent, mounted);
  385. unlock(dent->lock);
  386. ADD_CP_FUNC_ENTRY(off);
  387. } else {
  388. new_dent = (struct shim_dentry *) (base + off);
  389. }
  390. if (objp)
  391. *objp = (void *) new_dent;
  392. }
  393. END_CP_FUNC(dentry)
  394. BEGIN_RS_FUNC(dentry)
  395. {
  396. struct shim_dentry * dent = (void *) (base + GET_CP_FUNC_ENTRY());
  397. BEGIN_PROFILE_INTERVAL();
  398. CP_REBASE(dent->hlist);
  399. CP_REBASE(dent->list);
  400. CP_REBASE(dent->children);
  401. CP_REBASE(dent->siblings);
  402. CP_REBASE(dent->fs);
  403. CP_REBASE(dent->parent);
  404. CP_REBASE(dent->mounted);
  405. create_lock(dent->lock);
  406. if (dent->parent)
  407. __set_parent_dentry(dent, dent->parent);
  408. struct hlist_head * head = &dcache_htable[DCACHE_HASH(dent->rel_path.hash)];
  409. hlist_add_head(&dent->hlist, head);
  410. dent->state |= DENTRY_HASHED;
  411. DEBUG_RS("hash=%08x,path=%s,fs=%s", dent->rel_path.hash,
  412. dentry_get_path(dent, true, NULL),
  413. dent->fs ? qstrgetstr(&dent->fs->path) : NULL);
  414. }
  415. END_RS_FUNC(dentry)