hmcdrv_cache.c 6.6 KB

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