hmcdrv_cache.c 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252
  1. /*
  2. * SE/HMC Drive (Read) Cache Functions
  3. *
  4. * Copyright IBM Corp. 2013
  5. * Author(s): Ralf Hoppe (rhoppe@de.ibm.com)
  6. *
  7. */
  8. #define KMSG_COMPONENT "hmcdrv"
  9. #define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
  10. #include <linux/kernel.h>
  11. #include <linux/mm.h>
  12. #include <linux/jiffies.h>
  13. #include "hmcdrv_ftp.h"
  14. #include "hmcdrv_cache.h"
  15. #define HMCDRV_CACHE_TIMEOUT 30 /* aging timeout in seconds */
  16. /**
  17. * struct hmcdrv_cache_entry - file cache (only used on read/dir)
  18. * @id: FTP command ID
  19. * @content: kernel-space buffer, 4k aligned
  20. * @len: size of @content cache (0 if caching disabled)
  21. * @ofs: start of content within file (-1 if no cached content)
  22. * @fname: file name
  23. * @fsize: file size
  24. * @timeout: cache timeout in jiffies
  25. *
  26. * Notice that the first three members (id, fname, fsize) are cached on all
  27. * read/dir requests. But content is cached only under some preconditions.
  28. * Uncached content is signalled by a negative value of @ofs.
  29. */
  30. struct hmcdrv_cache_entry {
  31. enum hmcdrv_ftp_cmdid id;
  32. char fname[HMCDRV_FTP_FIDENT_MAX];
  33. size_t fsize;
  34. loff_t ofs;
  35. unsigned long timeout;
  36. void *content;
  37. size_t len;
  38. };
  39. static int hmcdrv_cache_order; /* cache allocated page order */
  40. static struct hmcdrv_cache_entry hmcdrv_cache_file = {
  41. .fsize = SIZE_MAX,
  42. .ofs = -1,
  43. .len = 0,
  44. .fname = {'\0'}
  45. };
  46. /**
  47. * hmcdrv_cache_get() - looks for file data/content in read cache
  48. * @ftp: pointer to FTP command specification
  49. *
  50. * Return: number of bytes read from cache or a negative number if nothing
  51. * in content cache (for the file/cmd specified in @ftp)
  52. */
  53. static ssize_t hmcdrv_cache_get(const struct hmcdrv_ftp_cmdspec *ftp)
  54. {
  55. loff_t pos; /* position in cache (signed) */
  56. ssize_t len;
  57. if ((ftp->id != hmcdrv_cache_file.id) ||
  58. strcmp(hmcdrv_cache_file.fname, ftp->fname))
  59. return -1;
  60. if (ftp->ofs >= hmcdrv_cache_file.fsize) /* EOF ? */
  61. return 0;
  62. if ((hmcdrv_cache_file.ofs < 0) || /* has content? */
  63. time_after(jiffies, hmcdrv_cache_file.timeout))
  64. return -1;
  65. /* there seems to be cached content - calculate the maximum number
  66. * of bytes that can be returned (regarding file size and offset)
  67. */
  68. len = hmcdrv_cache_file.fsize - ftp->ofs;
  69. if (len > ftp->len)
  70. len = ftp->len;
  71. /* check if the requested chunk falls into our cache (which starts
  72. * at offset 'hmcdrv_cache_file.ofs' in the file of interest)
  73. */
  74. pos = ftp->ofs - hmcdrv_cache_file.ofs;
  75. if ((pos >= 0) &&
  76. ((pos + len) <= hmcdrv_cache_file.len)) {
  77. memcpy(ftp->buf,
  78. hmcdrv_cache_file.content + pos,
  79. len);
  80. pr_debug("using cached content of '%s', returning %zd/%zd bytes\n",
  81. hmcdrv_cache_file.fname, len,
  82. hmcdrv_cache_file.fsize);
  83. return len;
  84. }
  85. return -1;
  86. }
  87. /**
  88. * hmcdrv_cache_do() - do a HMC drive CD/DVD transfer with cache update
  89. * @ftp: pointer to FTP command specification
  90. * @func: FTP transfer function to be used
  91. *
  92. * Return: number of bytes read/written or a (negative) error code
  93. */
  94. static ssize_t hmcdrv_cache_do(const struct hmcdrv_ftp_cmdspec *ftp,
  95. hmcdrv_cache_ftpfunc func)
  96. {
  97. ssize_t len;
  98. /* only cache content if the read/dir cache really exists
  99. * (hmcdrv_cache_file.len > 0), is large enough to handle the
  100. * request (hmcdrv_cache_file.len >= ftp->len) and there is a need
  101. * to do so (ftp->len > 0)
  102. */
  103. if ((ftp->len > 0) && (hmcdrv_cache_file.len >= ftp->len)) {
  104. /* because the cache is not located at ftp->buf, we have to
  105. * assemble a new HMC drive FTP cmd specification (pointing
  106. * to our cache, and using the increased size)
  107. */
  108. struct hmcdrv_ftp_cmdspec cftp = *ftp; /* make a copy */
  109. cftp.buf = hmcdrv_cache_file.content; /* and update */
  110. cftp.len = hmcdrv_cache_file.len; /* buffer data */
  111. len = func(&cftp, &hmcdrv_cache_file.fsize); /* now do */
  112. if (len > 0) {
  113. pr_debug("caching %zd bytes content for '%s'\n",
  114. len, ftp->fname);
  115. if (len > ftp->len)
  116. len = ftp->len;
  117. hmcdrv_cache_file.ofs = ftp->ofs;
  118. hmcdrv_cache_file.timeout = jiffies +
  119. HMCDRV_CACHE_TIMEOUT * HZ;
  120. memcpy(ftp->buf, hmcdrv_cache_file.content, len);
  121. }
  122. } else {
  123. len = func(ftp, &hmcdrv_cache_file.fsize);
  124. hmcdrv_cache_file.ofs = -1; /* invalidate content */
  125. }
  126. if (len > 0) {
  127. /* cache some file info (FTP command, file name and file
  128. * size) unconditionally
  129. */
  130. strlcpy(hmcdrv_cache_file.fname, ftp->fname,
  131. HMCDRV_FTP_FIDENT_MAX);
  132. hmcdrv_cache_file.id = ftp->id;
  133. pr_debug("caching cmd %d, file size %zu for '%s'\n",
  134. ftp->id, hmcdrv_cache_file.fsize, ftp->fname);
  135. }
  136. return len;
  137. }
  138. /**
  139. * hmcdrv_cache_cmd() - perform a cached HMC drive CD/DVD transfer
  140. * @ftp: pointer to FTP command specification
  141. * @func: FTP transfer function to be used
  142. *
  143. * Attention: Notice that this function is not reentrant - so the caller
  144. * must ensure exclusive execution.
  145. *
  146. * Return: number of bytes read/written or a (negative) error code
  147. */
  148. ssize_t hmcdrv_cache_cmd(const struct hmcdrv_ftp_cmdspec *ftp,
  149. hmcdrv_cache_ftpfunc func)
  150. {
  151. ssize_t len;
  152. if ((ftp->id == HMCDRV_FTP_DIR) || /* read cache */
  153. (ftp->id == HMCDRV_FTP_NLIST) ||
  154. (ftp->id == HMCDRV_FTP_GET)) {
  155. len = hmcdrv_cache_get(ftp);
  156. if (len >= 0) /* got it from cache ? */
  157. return len; /* yes */
  158. len = hmcdrv_cache_do(ftp, func);
  159. if (len >= 0)
  160. return len;
  161. } else {
  162. len = func(ftp, NULL); /* simply do original command */
  163. }
  164. /* invalidate the (read) cache in case there was a write operation
  165. * or an error on read/dir
  166. */
  167. hmcdrv_cache_file.id = HMCDRV_FTP_NOOP;
  168. hmcdrv_cache_file.fsize = LLONG_MAX;
  169. hmcdrv_cache_file.ofs = -1;
  170. return len;
  171. }
  172. /**
  173. * hmcdrv_cache_startup() - startup of HMC drive cache
  174. * @cachesize: cache size
  175. *
  176. * Return: 0 on success, else a (negative) error code
  177. */
  178. int hmcdrv_cache_startup(size_t cachesize)
  179. {
  180. if (cachesize > 0) { /* perform caching ? */
  181. hmcdrv_cache_order = get_order(cachesize);
  182. hmcdrv_cache_file.content =
  183. (void *) __get_free_pages(GFP_KERNEL | GFP_DMA,
  184. hmcdrv_cache_order);
  185. if (!hmcdrv_cache_file.content) {
  186. pr_err("Allocating the requested cache size of %zu bytes failed\n",
  187. cachesize);
  188. return -ENOMEM;
  189. }
  190. pr_debug("content cache enabled, size is %zu bytes\n",
  191. cachesize);
  192. }
  193. hmcdrv_cache_file.len = cachesize;
  194. return 0;
  195. }
  196. /**
  197. * hmcdrv_cache_shutdown() - shutdown of HMC drive cache
  198. */
  199. void hmcdrv_cache_shutdown(void)
  200. {
  201. if (hmcdrv_cache_file.content) {
  202. free_pages((unsigned long) hmcdrv_cache_file.content,
  203. hmcdrv_cache_order);
  204. hmcdrv_cache_file.content = NULL;
  205. }
  206. hmcdrv_cache_file.id = HMCDRV_FTP_NOOP;
  207. hmcdrv_cache_file.fsize = LLONG_MAX;
  208. hmcdrv_cache_file.ofs = -1;
  209. hmcdrv_cache_file.len = 0; /* no cache */
  210. }