1b2441318SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
28f933b10SRalf Hoppe /*
38f933b10SRalf Hoppe * SE/HMC Drive (Read) Cache Functions
48f933b10SRalf Hoppe *
58f933b10SRalf Hoppe * Copyright IBM Corp. 2013
68f933b10SRalf Hoppe * Author(s): Ralf Hoppe (rhoppe@de.ibm.com)
78f933b10SRalf Hoppe *
88f933b10SRalf Hoppe */
98f933b10SRalf Hoppe
108f933b10SRalf Hoppe #define KMSG_COMPONENT "hmcdrv"
118f933b10SRalf Hoppe #define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
128f933b10SRalf Hoppe
138f933b10SRalf Hoppe #include <linux/kernel.h>
148f933b10SRalf Hoppe #include <linux/mm.h>
158f933b10SRalf Hoppe #include <linux/jiffies.h>
168f933b10SRalf Hoppe
178f933b10SRalf Hoppe #include "hmcdrv_ftp.h"
188f933b10SRalf Hoppe #include "hmcdrv_cache.h"
198f933b10SRalf Hoppe
208f933b10SRalf Hoppe #define HMCDRV_CACHE_TIMEOUT 30 /* aging timeout in seconds */
218f933b10SRalf Hoppe
228f933b10SRalf Hoppe /**
238f933b10SRalf Hoppe * struct hmcdrv_cache_entry - file cache (only used on read/dir)
248f933b10SRalf Hoppe * @id: FTP command ID
258f933b10SRalf Hoppe * @content: kernel-space buffer, 4k aligned
268f933b10SRalf Hoppe * @len: size of @content cache (0 if caching disabled)
278f933b10SRalf Hoppe * @ofs: start of content within file (-1 if no cached content)
288f933b10SRalf Hoppe * @fname: file name
298f933b10SRalf Hoppe * @fsize: file size
308f933b10SRalf Hoppe * @timeout: cache timeout in jiffies
318f933b10SRalf Hoppe *
328f933b10SRalf Hoppe * Notice that the first three members (id, fname, fsize) are cached on all
338f933b10SRalf Hoppe * read/dir requests. But content is cached only under some preconditions.
348f933b10SRalf Hoppe * Uncached content is signalled by a negative value of @ofs.
358f933b10SRalf Hoppe */
368f933b10SRalf Hoppe struct hmcdrv_cache_entry {
378f933b10SRalf Hoppe enum hmcdrv_ftp_cmdid id;
388f933b10SRalf Hoppe char fname[HMCDRV_FTP_FIDENT_MAX];
398f933b10SRalf Hoppe size_t fsize;
408f933b10SRalf Hoppe loff_t ofs;
418f933b10SRalf Hoppe unsigned long timeout;
428f933b10SRalf Hoppe void *content;
438f933b10SRalf Hoppe size_t len;
448f933b10SRalf Hoppe };
458f933b10SRalf Hoppe
468f933b10SRalf Hoppe static int hmcdrv_cache_order; /* cache allocated page order */
478f933b10SRalf Hoppe
488f933b10SRalf Hoppe static struct hmcdrv_cache_entry hmcdrv_cache_file = {
498f933b10SRalf Hoppe .fsize = SIZE_MAX,
508f933b10SRalf Hoppe .ofs = -1,
518f933b10SRalf Hoppe .len = 0,
528f933b10SRalf Hoppe .fname = {'\0'}
538f933b10SRalf Hoppe };
548f933b10SRalf Hoppe
558f933b10SRalf Hoppe /**
568f933b10SRalf Hoppe * hmcdrv_cache_get() - looks for file data/content in read cache
578f933b10SRalf Hoppe * @ftp: pointer to FTP command specification
588f933b10SRalf Hoppe *
598f933b10SRalf Hoppe * Return: number of bytes read from cache or a negative number if nothing
608f933b10SRalf Hoppe * in content cache (for the file/cmd specified in @ftp)
618f933b10SRalf Hoppe */
hmcdrv_cache_get(const struct hmcdrv_ftp_cmdspec * ftp)628f933b10SRalf Hoppe static ssize_t hmcdrv_cache_get(const struct hmcdrv_ftp_cmdspec *ftp)
638f933b10SRalf Hoppe {
648f933b10SRalf Hoppe loff_t pos; /* position in cache (signed) */
658f933b10SRalf Hoppe ssize_t len;
668f933b10SRalf Hoppe
678f933b10SRalf Hoppe if ((ftp->id != hmcdrv_cache_file.id) ||
688f933b10SRalf Hoppe strcmp(hmcdrv_cache_file.fname, ftp->fname))
698f933b10SRalf Hoppe return -1;
708f933b10SRalf Hoppe
718f933b10SRalf Hoppe if (ftp->ofs >= hmcdrv_cache_file.fsize) /* EOF ? */
728f933b10SRalf Hoppe return 0;
738f933b10SRalf Hoppe
748f933b10SRalf Hoppe if ((hmcdrv_cache_file.ofs < 0) || /* has content? */
758f933b10SRalf Hoppe time_after(jiffies, hmcdrv_cache_file.timeout))
768f933b10SRalf Hoppe return -1;
778f933b10SRalf Hoppe
788f933b10SRalf Hoppe /* there seems to be cached content - calculate the maximum number
798f933b10SRalf Hoppe * of bytes that can be returned (regarding file size and offset)
808f933b10SRalf Hoppe */
818f933b10SRalf Hoppe len = hmcdrv_cache_file.fsize - ftp->ofs;
828f933b10SRalf Hoppe
838f933b10SRalf Hoppe if (len > ftp->len)
848f933b10SRalf Hoppe len = ftp->len;
858f933b10SRalf Hoppe
868f933b10SRalf Hoppe /* check if the requested chunk falls into our cache (which starts
878f933b10SRalf Hoppe * at offset 'hmcdrv_cache_file.ofs' in the file of interest)
888f933b10SRalf Hoppe */
898f933b10SRalf Hoppe pos = ftp->ofs - hmcdrv_cache_file.ofs;
908f933b10SRalf Hoppe
918f933b10SRalf Hoppe if ((pos >= 0) &&
928f933b10SRalf Hoppe ((pos + len) <= hmcdrv_cache_file.len)) {
938f933b10SRalf Hoppe
948f933b10SRalf Hoppe memcpy(ftp->buf,
958f933b10SRalf Hoppe hmcdrv_cache_file.content + pos,
968f933b10SRalf Hoppe len);
978f933b10SRalf Hoppe pr_debug("using cached content of '%s', returning %zd/%zd bytes\n",
988f933b10SRalf Hoppe hmcdrv_cache_file.fname, len,
998f933b10SRalf Hoppe hmcdrv_cache_file.fsize);
1008f933b10SRalf Hoppe
1018f933b10SRalf Hoppe return len;
1028f933b10SRalf Hoppe }
1038f933b10SRalf Hoppe
1048f933b10SRalf Hoppe return -1;
1058f933b10SRalf Hoppe }
1068f933b10SRalf Hoppe
1078f933b10SRalf Hoppe /**
1088f933b10SRalf Hoppe * hmcdrv_cache_do() - do a HMC drive CD/DVD transfer with cache update
1098f933b10SRalf Hoppe * @ftp: pointer to FTP command specification
1108f933b10SRalf Hoppe * @func: FTP transfer function to be used
1118f933b10SRalf Hoppe *
1128f933b10SRalf Hoppe * Return: number of bytes read/written or a (negative) error code
1138f933b10SRalf Hoppe */
hmcdrv_cache_do(const struct hmcdrv_ftp_cmdspec * ftp,hmcdrv_cache_ftpfunc func)1148f933b10SRalf Hoppe static ssize_t hmcdrv_cache_do(const struct hmcdrv_ftp_cmdspec *ftp,
1158f933b10SRalf Hoppe hmcdrv_cache_ftpfunc func)
1168f933b10SRalf Hoppe {
1178f933b10SRalf Hoppe ssize_t len;
1188f933b10SRalf Hoppe
1198f933b10SRalf Hoppe /* only cache content if the read/dir cache really exists
1208f933b10SRalf Hoppe * (hmcdrv_cache_file.len > 0), is large enough to handle the
1218f933b10SRalf Hoppe * request (hmcdrv_cache_file.len >= ftp->len) and there is a need
1228f933b10SRalf Hoppe * to do so (ftp->len > 0)
1238f933b10SRalf Hoppe */
1248f933b10SRalf Hoppe if ((ftp->len > 0) && (hmcdrv_cache_file.len >= ftp->len)) {
1258f933b10SRalf Hoppe
1268f933b10SRalf Hoppe /* because the cache is not located at ftp->buf, we have to
1278f933b10SRalf Hoppe * assemble a new HMC drive FTP cmd specification (pointing
1288f933b10SRalf Hoppe * to our cache, and using the increased size)
1298f933b10SRalf Hoppe */
1308f933b10SRalf Hoppe struct hmcdrv_ftp_cmdspec cftp = *ftp; /* make a copy */
1318f933b10SRalf Hoppe cftp.buf = hmcdrv_cache_file.content; /* and update */
1328f933b10SRalf Hoppe cftp.len = hmcdrv_cache_file.len; /* buffer data */
1338f933b10SRalf Hoppe
1348f933b10SRalf Hoppe len = func(&cftp, &hmcdrv_cache_file.fsize); /* now do */
1358f933b10SRalf Hoppe
1368f933b10SRalf Hoppe if (len > 0) {
1378f933b10SRalf Hoppe pr_debug("caching %zd bytes content for '%s'\n",
1388f933b10SRalf Hoppe len, ftp->fname);
1398f933b10SRalf Hoppe
1408f933b10SRalf Hoppe if (len > ftp->len)
1418f933b10SRalf Hoppe len = ftp->len;
1428f933b10SRalf Hoppe
1438f933b10SRalf Hoppe hmcdrv_cache_file.ofs = ftp->ofs;
1448f933b10SRalf Hoppe hmcdrv_cache_file.timeout = jiffies +
1458f933b10SRalf Hoppe HMCDRV_CACHE_TIMEOUT * HZ;
1468f933b10SRalf Hoppe memcpy(ftp->buf, hmcdrv_cache_file.content, len);
1478f933b10SRalf Hoppe }
1488f933b10SRalf Hoppe } else {
1498f933b10SRalf Hoppe len = func(ftp, &hmcdrv_cache_file.fsize);
1508f933b10SRalf Hoppe hmcdrv_cache_file.ofs = -1; /* invalidate content */
1518f933b10SRalf Hoppe }
1528f933b10SRalf Hoppe
1538f933b10SRalf Hoppe if (len > 0) {
1548f933b10SRalf Hoppe /* cache some file info (FTP command, file name and file
1558f933b10SRalf Hoppe * size) unconditionally
1568f933b10SRalf Hoppe */
157*820109fbSWolfram Sang strscpy(hmcdrv_cache_file.fname, ftp->fname,
1588f933b10SRalf Hoppe HMCDRV_FTP_FIDENT_MAX);
1598f933b10SRalf Hoppe hmcdrv_cache_file.id = ftp->id;
1608f933b10SRalf Hoppe pr_debug("caching cmd %d, file size %zu for '%s'\n",
1618f933b10SRalf Hoppe ftp->id, hmcdrv_cache_file.fsize, ftp->fname);
1628f933b10SRalf Hoppe }
1638f933b10SRalf Hoppe
1648f933b10SRalf Hoppe return len;
1658f933b10SRalf Hoppe }
1668f933b10SRalf Hoppe
1678f933b10SRalf Hoppe /**
1688f933b10SRalf Hoppe * hmcdrv_cache_cmd() - perform a cached HMC drive CD/DVD transfer
1698f933b10SRalf Hoppe * @ftp: pointer to FTP command specification
1708f933b10SRalf Hoppe * @func: FTP transfer function to be used
1718f933b10SRalf Hoppe *
1728f933b10SRalf Hoppe * Attention: Notice that this function is not reentrant - so the caller
1738f933b10SRalf Hoppe * must ensure exclusive execution.
1748f933b10SRalf Hoppe *
1758f933b10SRalf Hoppe * Return: number of bytes read/written or a (negative) error code
1768f933b10SRalf Hoppe */
hmcdrv_cache_cmd(const struct hmcdrv_ftp_cmdspec * ftp,hmcdrv_cache_ftpfunc func)1778f933b10SRalf Hoppe ssize_t hmcdrv_cache_cmd(const struct hmcdrv_ftp_cmdspec *ftp,
1788f933b10SRalf Hoppe hmcdrv_cache_ftpfunc func)
1798f933b10SRalf Hoppe {
1808f933b10SRalf Hoppe ssize_t len;
1818f933b10SRalf Hoppe
1828f933b10SRalf Hoppe if ((ftp->id == HMCDRV_FTP_DIR) || /* read cache */
1838f933b10SRalf Hoppe (ftp->id == HMCDRV_FTP_NLIST) ||
1848f933b10SRalf Hoppe (ftp->id == HMCDRV_FTP_GET)) {
1858f933b10SRalf Hoppe
1868f933b10SRalf Hoppe len = hmcdrv_cache_get(ftp);
1878f933b10SRalf Hoppe
1888f933b10SRalf Hoppe if (len >= 0) /* got it from cache ? */
1898f933b10SRalf Hoppe return len; /* yes */
1908f933b10SRalf Hoppe
1918f933b10SRalf Hoppe len = hmcdrv_cache_do(ftp, func);
1928f933b10SRalf Hoppe
1938f933b10SRalf Hoppe if (len >= 0)
1948f933b10SRalf Hoppe return len;
1958f933b10SRalf Hoppe
1968f933b10SRalf Hoppe } else {
1978f933b10SRalf Hoppe len = func(ftp, NULL); /* simply do original command */
1988f933b10SRalf Hoppe }
1998f933b10SRalf Hoppe
2008f933b10SRalf Hoppe /* invalidate the (read) cache in case there was a write operation
2018f933b10SRalf Hoppe * or an error on read/dir
2028f933b10SRalf Hoppe */
2038f933b10SRalf Hoppe hmcdrv_cache_file.id = HMCDRV_FTP_NOOP;
2048f933b10SRalf Hoppe hmcdrv_cache_file.fsize = LLONG_MAX;
2058f933b10SRalf Hoppe hmcdrv_cache_file.ofs = -1;
2068f933b10SRalf Hoppe
2078f933b10SRalf Hoppe return len;
2088f933b10SRalf Hoppe }
2098f933b10SRalf Hoppe
2108f933b10SRalf Hoppe /**
2118f933b10SRalf Hoppe * hmcdrv_cache_startup() - startup of HMC drive cache
2128f933b10SRalf Hoppe * @cachesize: cache size
2138f933b10SRalf Hoppe *
2148f933b10SRalf Hoppe * Return: 0 on success, else a (negative) error code
2158f933b10SRalf Hoppe */
hmcdrv_cache_startup(size_t cachesize)2168f933b10SRalf Hoppe int hmcdrv_cache_startup(size_t cachesize)
2178f933b10SRalf Hoppe {
2188f933b10SRalf Hoppe if (cachesize > 0) { /* perform caching ? */
2198f933b10SRalf Hoppe hmcdrv_cache_order = get_order(cachesize);
2208f933b10SRalf Hoppe hmcdrv_cache_file.content =
2218f933b10SRalf Hoppe (void *) __get_free_pages(GFP_KERNEL | GFP_DMA,
2228f933b10SRalf Hoppe hmcdrv_cache_order);
2238f933b10SRalf Hoppe
2248f933b10SRalf Hoppe if (!hmcdrv_cache_file.content) {
2258f933b10SRalf Hoppe pr_err("Allocating the requested cache size of %zu bytes failed\n",
2268f933b10SRalf Hoppe cachesize);
2278f933b10SRalf Hoppe return -ENOMEM;
2288f933b10SRalf Hoppe }
2298f933b10SRalf Hoppe
2308f933b10SRalf Hoppe pr_debug("content cache enabled, size is %zu bytes\n",
2318f933b10SRalf Hoppe cachesize);
2328f933b10SRalf Hoppe }
2338f933b10SRalf Hoppe
2348f933b10SRalf Hoppe hmcdrv_cache_file.len = cachesize;
2358f933b10SRalf Hoppe return 0;
2368f933b10SRalf Hoppe }
2378f933b10SRalf Hoppe
2388f933b10SRalf Hoppe /**
2398f933b10SRalf Hoppe * hmcdrv_cache_shutdown() - shutdown of HMC drive cache
2408f933b10SRalf Hoppe */
hmcdrv_cache_shutdown(void)2418f933b10SRalf Hoppe void hmcdrv_cache_shutdown(void)
2428f933b10SRalf Hoppe {
2438f933b10SRalf Hoppe if (hmcdrv_cache_file.content) {
2448f933b10SRalf Hoppe free_pages((unsigned long) hmcdrv_cache_file.content,
2458f933b10SRalf Hoppe hmcdrv_cache_order);
2468f933b10SRalf Hoppe hmcdrv_cache_file.content = NULL;
2478f933b10SRalf Hoppe }
2488f933b10SRalf Hoppe
2498f933b10SRalf Hoppe hmcdrv_cache_file.id = HMCDRV_FTP_NOOP;
2508f933b10SRalf Hoppe hmcdrv_cache_file.fsize = LLONG_MAX;
2518f933b10SRalf Hoppe hmcdrv_cache_file.ofs = -1;
2528f933b10SRalf Hoppe hmcdrv_cache_file.len = 0; /* no cache */
2538f933b10SRalf Hoppe }
254