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