xref: /linux/drivers/s390/char/hmcdrv_cache.c (revision 4f2c0a4acffbec01079c28f839422e64ddeff004)
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