shim_dcache.c 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453
  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 (memcmp(name, "memsize", 8) == 0)
  41. return dcache_stats.memsize;
  42. if (memcmp(name, "nentries", 9) == 0)
  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. INIT_LIST_HEAD(&dent->alias);
  70. return dent;
  71. }
  72. DEFINE_PROFILE_CATAGORY(dcache, );
  73. DEFINE_PROFILE_INTERVAL(total_init_dcache, dcache);
  74. DEFINE_PROFILE_CATAGORY(init_dcache, dcache);
  75. DEFINE_PROFILE_INTERVAL(dcache_init_memory, init_dcache);
  76. DEFINE_PROFILE_INTERVAL(dcache_init_hash_table, init_dcache);
  77. DEFINE_PROFILE_INTERVAL(dcache_init_lock, init_dcache);
  78. DEFINE_PROFILE_INTERVAL(dcache_init_root_entry, init_dcache);
  79. int init_dcache (void)
  80. {
  81. #ifdef PROFILE
  82. unsigned long begin_time = GET_PROFILE_INTERVAL();
  83. BEGIN_PROFILE_INTERVAL_SET(begin_time);
  84. #endif
  85. dentry_mgr = create_mem_mgr(init_align_up(DCACHE_MGR_ALLOC));
  86. SAVE_PROFILE_INTERVAL(dcache_init_memory);
  87. for (int i = 0 ; i < DCACHE_HASH_SIZE ; i++)
  88. INIT_HLIST_HEAD(&dcache_htable[i]);
  89. SAVE_PROFILE_INTERVAL(dcache_init_hash_table);
  90. create_lock(dcache_lock);
  91. SAVE_PROFILE_INTERVAL(dcache_init_lock);
  92. dentry_root = alloc_dentry();
  93. qstrsetstr(&dentry_root->rel_path, "", 0);
  94. qstrsetstr(&dentry_root->name, "", 0);
  95. get_dentry(dentry_root);
  96. SAVE_PROFILE_INTERVAL(dcache_init_root_entry);
  97. SAVE_PROFILE_INTERVAL_SINCE(total_init_dcache, begin_time);
  98. return 0;
  99. }
  100. int reinit_dcache (void)
  101. {
  102. create_lock(dcache_lock);
  103. return 0;
  104. }
  105. /* remove from the hash table, so that a lookup will fail. */
  106. void __del_dcache (struct shim_dentry * dent)
  107. {
  108. if (!(dent->state & DENTRY_HASHED))
  109. return;
  110. dent->state &= ~DENTRY_HASHED;
  111. hlist_del_init(&dent->hlist);
  112. #ifdef DEBUG_DCACHE
  113. debug("del dcache %p(%s/%s) (mount = %p)\n",
  114. dent, dent->fs ? qstrgetstr(&dent->fs->path) : "",
  115. qstrgetstr(&dent->rel_path), dent->fs);
  116. #endif
  117. }
  118. static int __internal_put_dentry (struct shim_dentry * dent);
  119. void del_dcache (struct shim_dentry * dent)
  120. {
  121. lock(dcache_lock);
  122. __del_dcache(dent);
  123. unlock(dcache_lock);
  124. }
  125. static inline void __dput_dentry (struct shim_dentry * dent)
  126. {
  127. while (1) {
  128. /* if the dentry is never in the hash table, we are happy to
  129. drop it */
  130. if (!(dent->state & DENTRY_HASHED))
  131. goto kill;
  132. /* move the node to unused list unless it is persistent */
  133. if (!(dent->state & DENTRY_PERSIST)) {
  134. dent->state |= DENTRY_RECENTLY;
  135. list_del(&dent->list);
  136. INIT_LIST_HEAD(&dent->list);
  137. list_add(&dent->list, &unused);
  138. }
  139. /* we don't delete the dentry from dcache because it might
  140. be acquired and used again, unless it gets recycled due
  141. to memory pressure */
  142. break;
  143. kill: {
  144. if (dent->fs && dent->fs->d_ops && dent->fs->d_ops->dput)
  145. dent->fs->d_ops->dput(dent);
  146. struct shim_dentry * parent = dent->parent;
  147. if (!parent)
  148. break;
  149. list_del(&dent->siblings); /* remove from parent's list of children */
  150. dent->parent = NULL;
  151. dent = parent;
  152. if (__internal_put_dentry(dent))
  153. break;
  154. }
  155. }
  156. }
  157. static int __internal_put_dentry (struct shim_dentry * dent)
  158. {
  159. int count = REF_DEC(dent->ref_count);
  160. #ifdef DEBUG_REF
  161. debug("put dentry %p(%s/%s) (ref_count = %d)\n", dent,
  162. dent->fs ?
  163. qstrgetstr(&dent->fs->path) : "",
  164. qstrgetstr(&dent->rel_path), count);
  165. #endif
  166. return count;
  167. }
  168. void get_dentry (struct shim_dentry * dent)
  169. {
  170. #ifdef DEBUG_REF
  171. int count = REF_INC(dent->ref_count);
  172. debug("get dentry %p(%s/%s) (ref_count = %d)\n", dent,
  173. dent->fs ?
  174. qstrgetstr(&dent->fs->path) : "",
  175. qstrgetstr(&dent->rel_path), count);
  176. #else
  177. REF_INC(dent->ref_count);
  178. #endif
  179. }
  180. void put_dentry (struct shim_dentry * dent)
  181. {
  182. if (__internal_put_dentry(dent))
  183. return;
  184. if (locked(dcache_lock)) {
  185. __dput_dentry(dent);
  186. } else {
  187. lock(dcache_lock);
  188. __dput_dentry(dent);
  189. unlock(dcache_lock);
  190. }
  191. }
  192. struct shim_dentry * get_new_dentry (struct shim_dentry * parent,
  193. const char * name, int namelen)
  194. {
  195. struct shim_dentry * dent = alloc_dentry();
  196. if (!dent)
  197. return NULL;
  198. REF_SET(dent->ref_count, 0);
  199. qstrsetstr(&dent->name, name, namelen);
  200. if (!parent) {
  201. qstrsetstr(&dent->rel_path, name, namelen);
  202. return dent;
  203. }
  204. if (!qstrempty(&parent->rel_path)) {
  205. const char * strs[] = { qstrgetstr(&parent->rel_path), "/", name };
  206. size_t lens[] = { parent->rel_path.len, 1, namelen };
  207. qstrsetstrs(&dent->rel_path, 3, strs, lens);
  208. } else
  209. qstrsetstr(&dent->rel_path, name, namelen);
  210. return dent;
  211. }
  212. void __set_parent_dentry (struct shim_dentry * child,
  213. struct shim_dentry * parent)
  214. {
  215. if (child->parent == parent)
  216. return;
  217. assert(!child->parent);
  218. get_dentry(parent);
  219. list_add_tail(&child->siblings, &parent->children);
  220. child->parent = parent;
  221. parent->nchildren++;
  222. }
  223. void __unset_parent_dentry (struct shim_dentry * child,
  224. struct shim_dentry * parent)
  225. {
  226. if (child->parent != parent)
  227. return;
  228. assert(child->parent);
  229. child->parent = NULL;
  230. list_del_init(&child->siblings);
  231. parent->nchildren--;
  232. put_dentry(parent);
  233. }
  234. static inline
  235. HASHTYPE hash_dentry (struct shim_dentry * start, const char * path, int len)
  236. {
  237. return rehash_path(start ? start->rel_path.hash : 0,
  238. path, len, NULL);
  239. }
  240. void __add_dcache (struct shim_dentry * dent, HASHTYPE * hashptr)
  241. {
  242. struct hlist_head * head;
  243. if (hashptr) {
  244. dent->rel_path.hash = *hashptr;
  245. goto add_hash;
  246. }
  247. if (!dent->parent) {
  248. dent->rel_path.hash = dent->fs ? dent->fs->path.hash : 0;
  249. goto add_hash;
  250. }
  251. dent->rel_path.hash = hash_dentry(dent->parent, dentry_get_name(dent),
  252. dent->name.len);
  253. add_hash:
  254. head = &dcache_htable[DCACHE_HASH(dent->rel_path.hash)];
  255. hlist_add_head(&dent->hlist, head);
  256. dent->state |= DENTRY_HASHED;
  257. #ifdef DEBUG_DCACHE
  258. debug("add dcache %p(%s/%s) (mount = %p)\n",
  259. dent, dent->fs ? qstrgetstr(&dent->fs->path) : "",
  260. qstrgetstr(&dent->rel_path), dent->fs);
  261. #endif
  262. }
  263. void add_dcache (struct shim_dentry * dent, HASHTYPE * hashptr)
  264. {
  265. lock(dcache_lock);
  266. __add_dcache(dent, hashptr);
  267. unlock(dcache_lock);
  268. }
  269. struct shim_dentry *
  270. __lookup_dcache (struct shim_dentry * start, const char * name, int namelen,
  271. const char * path, int pathlen, HASHTYPE * hashptr)
  272. {
  273. HASHTYPE hash = hash_dentry(start, name, namelen);
  274. struct shim_dentry * dent, * found = NULL;
  275. struct hlist_node * node;
  276. struct hlist_head * head = &dcache_htable[DCACHE_HASH(hash)];
  277. /* walk through all the nodes in the hash bucket, find the droids we're
  278. looking for */
  279. hlist_for_each_entry(dent, node, head, hlist) {
  280. if ((dent->state & DENTRY_MOUNTPOINT) ||
  281. dent->rel_path.hash != hash)
  282. continue;
  283. /* first we compare the filename */
  284. const char * filename = get_file_name(name, namelen);
  285. if (memcmp(dentry_get_name(dent), filename, name + namelen - filename))
  286. continue;
  287. if (filename == name) {
  288. struct shim_dentry * d = dent;
  289. while (d && !d->parent && d->fs)
  290. d = d->fs->mount_point;
  291. if (d && d->parent && d->parent != start)
  292. continue;
  293. }
  294. if (path && pathlen && filename != path) {
  295. const char * fullpath;
  296. int fullpathlen;
  297. fullpath = dentry_get_path(dent, true, &fullpathlen);
  298. if (pathlen > fullpathlen)
  299. continue;
  300. fullpath += fullpathlen - pathlen;
  301. if (fullpath[-1] != '/')
  302. continue;
  303. if (memcmp(fullpath, path, pathlen))
  304. continue;
  305. }
  306. get_dentry(dent);
  307. found = dent;
  308. break;
  309. }
  310. if (hashptr)
  311. *hashptr = hash;
  312. return found;
  313. }
  314. /* after lookup_dcache, the dentry is popped to prevent recycling */
  315. struct shim_dentry *
  316. lookup_dcache (struct shim_dentry * start, const char * name, int namelen,
  317. const char * path, int pathlen, HASHTYPE * hashptr)
  318. {
  319. lock(dcache_lock);
  320. struct shim_dentry * dent = __lookup_dcache(start, name, namelen, path,
  321. pathlen, hashptr);
  322. unlock(dcache_lock);
  323. return dent;
  324. }
  325. int __del_dentry_tree (struct shim_dentry * root)
  326. {
  327. struct shim_dentry * this_parent = root;
  328. struct list_head * next;
  329. repeat:
  330. next = this_parent->children.next;
  331. resume:
  332. while (next != &this_parent->children) {
  333. struct list_head * tmp = next;
  334. struct shim_dentry * d = list_entry(tmp, struct shim_dentry,
  335. siblings);
  336. next = tmp->next;
  337. if (d->state & DENTRY_MOUNTPOINT) {
  338. this_parent = d->mounted->root;
  339. goto repeat;
  340. }
  341. if (!list_empty(&d->children)) {
  342. this_parent = d;
  343. goto repeat;
  344. }
  345. __unset_parent_dentry(d, this_parent);
  346. __del_dcache(d);
  347. }
  348. if (this_parent != root) {
  349. struct shim_dentry * child = this_parent;
  350. if (!this_parent->parent) {
  351. this_parent = this_parent->fs->mount_point;
  352. __del_dcache(child);
  353. child = this_parent;
  354. }
  355. this_parent = this_parent->parent;
  356. next = child->siblings.next;
  357. __del_dcache(child);
  358. __unset_parent_dentry(child, this_parent);
  359. goto resume;
  360. }
  361. return 0;
  362. }