1/*-
2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3 *
4 * Copyright (c) 2006 Ruslan Ermilov <ru@FreeBSD.org>
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 *    notice, this list of conditions and the following disclaimer in the
14 *    documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * SUCH DAMAGE.
27 */
28
29#include <sys/cdefs.h>
30__FBSDID("$FreeBSD$");
31
32#include <sys/param.h>
33#include <sys/systm.h>
34#include <sys/kernel.h>
35#include <sys/module.h>
36#include <sys/lock.h>
37#include <sys/mutex.h>
38#include <sys/bio.h>
39#include <sys/sysctl.h>
40#include <sys/malloc.h>
41#include <sys/queue.h>
42#include <sys/sbuf.h>
43#include <sys/time.h>
44#include <vm/uma.h>
45#include <geom/geom.h>
46#include <geom/geom_dbg.h>
47#include <geom/cache/g_cache.h>
48
49FEATURE(geom_cache, "GEOM cache module");
50
51static MALLOC_DEFINE(M_GCACHE, "gcache_data", "GEOM_CACHE Data");
52
53SYSCTL_DECL(_kern_geom);
54static SYSCTL_NODE(_kern_geom, OID_AUTO, cache, CTLFLAG_RW | CTLFLAG_MPSAFE, 0,
55    "GEOM_CACHE stuff");
56static u_int g_cache_debug = 0;
57SYSCTL_UINT(_kern_geom_cache, OID_AUTO, debug, CTLFLAG_RW, &g_cache_debug, 0,
58    "Debug level");
59static u_int g_cache_enable = 1;
60SYSCTL_UINT(_kern_geom_cache, OID_AUTO, enable, CTLFLAG_RW, &g_cache_enable, 0,
61    "");
62static u_int g_cache_timeout = 10;
63SYSCTL_UINT(_kern_geom_cache, OID_AUTO, timeout, CTLFLAG_RW, &g_cache_timeout,
64    0, "");
65static u_int g_cache_idletime = 5;
66SYSCTL_UINT(_kern_geom_cache, OID_AUTO, idletime, CTLFLAG_RW, &g_cache_idletime,
67    0, "");
68static u_int g_cache_used_lo = 5;
69static u_int g_cache_used_hi = 20;
70static int
71sysctl_handle_pct(SYSCTL_HANDLER_ARGS)
72{
73	u_int val = *(u_int *)arg1;
74	int error;
75
76	error = sysctl_handle_int(oidp, &val, 0, req);
77	if (error || !req->newptr)
78		return (error);
79	if (val > 100)
80		return (EINVAL);
81	if ((arg1 == &g_cache_used_lo && val > g_cache_used_hi) ||
82	    (arg1 == &g_cache_used_hi && g_cache_used_lo > val))
83		return (EINVAL);
84	*(u_int *)arg1 = val;
85	return (0);
86}
87SYSCTL_PROC(_kern_geom_cache, OID_AUTO, used_lo,
88    CTLTYPE_UINT | CTLFLAG_RW | CTLFLAG_NEEDGIANT, &g_cache_used_lo, 0,
89    sysctl_handle_pct, "IU",
90    "");
91SYSCTL_PROC(_kern_geom_cache, OID_AUTO, used_hi,
92    CTLTYPE_UINT | CTLFLAG_RW | CTLFLAG_NEEDGIANT, &g_cache_used_hi, 0,
93    sysctl_handle_pct, "IU",
94    "");
95
96static int g_cache_destroy(struct g_cache_softc *sc, boolean_t force);
97static g_ctl_destroy_geom_t g_cache_destroy_geom;
98
99static g_taste_t g_cache_taste;
100static g_ctl_req_t g_cache_config;
101static g_dumpconf_t g_cache_dumpconf;
102
103struct g_class g_cache_class = {
104	.name = G_CACHE_CLASS_NAME,
105	.version = G_VERSION,
106	.ctlreq = g_cache_config,
107	.taste = g_cache_taste,
108	.destroy_geom = g_cache_destroy_geom
109};
110
111#define	OFF2BNO(off, sc)	((off) >> (sc)->sc_bshift)
112#define	BNO2OFF(bno, sc)	((bno) << (sc)->sc_bshift)
113
114static struct g_cache_desc *
115g_cache_alloc(struct g_cache_softc *sc)
116{
117	struct g_cache_desc *dp;
118
119	mtx_assert(&sc->sc_mtx, MA_OWNED);
120
121	if (!TAILQ_EMPTY(&sc->sc_usedlist)) {
122		dp = TAILQ_FIRST(&sc->sc_usedlist);
123		TAILQ_REMOVE(&sc->sc_usedlist, dp, d_used);
124		sc->sc_nused--;
125		dp->d_flags = 0;
126		LIST_REMOVE(dp, d_next);
127		return (dp);
128	}
129	if (sc->sc_nent > sc->sc_maxent) {
130		sc->sc_cachefull++;
131		return (NULL);
132	}
133	dp = malloc(sizeof(*dp), M_GCACHE, M_NOWAIT | M_ZERO);
134	if (dp == NULL)
135		return (NULL);
136	dp->d_data = uma_zalloc(sc->sc_zone, M_NOWAIT);
137	if (dp->d_data == NULL) {
138		free(dp, M_GCACHE);
139		return (NULL);
140	}
141	sc->sc_nent++;
142	return (dp);
143}
144
145static void
146g_cache_free(struct g_cache_softc *sc, struct g_cache_desc *dp)
147{
148
149	mtx_assert(&sc->sc_mtx, MA_OWNED);
150
151	uma_zfree(sc->sc_zone, dp->d_data);
152	free(dp, M_GCACHE);
153	sc->sc_nent--;
154}
155
156static void
157g_cache_free_used(struct g_cache_softc *sc)
158{
159	struct g_cache_desc *dp;
160	u_int n;
161
162	mtx_assert(&sc->sc_mtx, MA_OWNED);
163
164	n = g_cache_used_lo * sc->sc_maxent / 100;
165	while (sc->sc_nused > n) {
166		KASSERT(!TAILQ_EMPTY(&sc->sc_usedlist), ("used list empty"));
167		dp = TAILQ_FIRST(&sc->sc_usedlist);
168		TAILQ_REMOVE(&sc->sc_usedlist, dp, d_used);
169		sc->sc_nused--;
170		LIST_REMOVE(dp, d_next);
171		g_cache_free(sc, dp);
172	}
173}
174
175static void
176g_cache_deliver(struct g_cache_softc *sc, struct bio *bp,
177    struct g_cache_desc *dp, int error)
178{
179	off_t off1, off, len;
180
181	mtx_assert(&sc->sc_mtx, MA_OWNED);
182	KASSERT(OFF2BNO(bp->bio_offset, sc) <= dp->d_bno, ("wrong entry"));
183	KASSERT(OFF2BNO(bp->bio_offset + bp->bio_length - 1, sc) >=
184	    dp->d_bno, ("wrong entry"));
185
186	off1 = BNO2OFF(dp->d_bno, sc);
187	off = MAX(bp->bio_offset, off1);
188	len = MIN(bp->bio_offset + bp->bio_length, off1 + sc->sc_bsize) - off;
189
190	if (bp->bio_error == 0)
191		bp->bio_error = error;
192	if (bp->bio_error == 0) {
193		bcopy(dp->d_data + (off - off1),
194		    bp->bio_data + (off - bp->bio_offset), len);
195	}
196	bp->bio_completed += len;
197	KASSERT(bp->bio_completed <= bp->bio_length, ("extra data"));
198	if (bp->bio_completed == bp->bio_length) {
199		if (bp->bio_error != 0)
200			bp->bio_completed = 0;
201		g_io_deliver(bp, bp->bio_error);
202	}
203
204	if (dp->d_flags & D_FLAG_USED) {
205		TAILQ_REMOVE(&sc->sc_usedlist, dp, d_used);
206		TAILQ_INSERT_TAIL(&sc->sc_usedlist, dp, d_used);
207	} else if (OFF2BNO(off + len, sc) > dp->d_bno) {
208		TAILQ_INSERT_TAIL(&sc->sc_usedlist, dp, d_used);
209		sc->sc_nused++;
210		dp->d_flags |= D_FLAG_USED;
211	}
212	dp->d_atime = time_uptime;
213}
214
215static void
216g_cache_done(struct bio *bp)
217{
218	struct g_cache_softc *sc;
219	struct g_cache_desc *dp;
220	struct bio *bp2, *tmpbp;
221
222	sc = bp->bio_from->geom->softc;
223	KASSERT(G_CACHE_DESC1(bp) == sc, ("corrupt bio_caller in g_cache_done()"));
224	dp = G_CACHE_DESC2(bp);
225	mtx_lock(&sc->sc_mtx);
226	bp2 = dp->d_biolist;
227	while (bp2 != NULL) {
228		KASSERT(G_CACHE_NEXT_BIO1(bp2) == sc, ("corrupt bio_driver in g_cache_done()"));
229		tmpbp = G_CACHE_NEXT_BIO2(bp2);
230		g_cache_deliver(sc, bp2, dp, bp->bio_error);
231		bp2 = tmpbp;
232	}
233	dp->d_biolist = NULL;
234	if (dp->d_flags & D_FLAG_INVALID) {
235		sc->sc_invalid--;
236		g_cache_free(sc, dp);
237	} else if (bp->bio_error) {
238		LIST_REMOVE(dp, d_next);
239		if (dp->d_flags & D_FLAG_USED) {
240			TAILQ_REMOVE(&sc->sc_usedlist, dp, d_used);
241			sc->sc_nused--;
242		}
243		g_cache_free(sc, dp);
244	}
245	mtx_unlock(&sc->sc_mtx);
246	g_destroy_bio(bp);
247}
248
249static struct g_cache_desc *
250g_cache_lookup(struct g_cache_softc *sc, off_t bno)
251{
252	struct g_cache_desc *dp;
253
254	mtx_assert(&sc->sc_mtx, MA_OWNED);
255
256	LIST_FOREACH(dp, &sc->sc_desclist[G_CACHE_BUCKET(bno)], d_next)
257		if (dp->d_bno == bno)
258			return (dp);
259	return (NULL);
260}
261
262static int
263g_cache_read(struct g_cache_softc *sc, struct bio *bp)
264{
265	struct bio *cbp;
266	struct g_cache_desc *dp;
267
268	mtx_lock(&sc->sc_mtx);
269	dp = g_cache_lookup(sc,
270	    OFF2BNO(bp->bio_offset + bp->bio_completed, sc));
271	if (dp != NULL) {
272		/* Add to waiters list or deliver. */
273		sc->sc_cachehits++;
274		if (dp->d_biolist != NULL) {
275			G_CACHE_NEXT_BIO1(bp) = sc;
276			G_CACHE_NEXT_BIO2(bp) = dp->d_biolist;
277			dp->d_biolist = bp;
278		} else
279			g_cache_deliver(sc, bp, dp, 0);
280		mtx_unlock(&sc->sc_mtx);
281		return (0);
282	}
283
284	/* Cache miss.  Allocate entry and schedule bio.  */
285	sc->sc_cachemisses++;
286	dp = g_cache_alloc(sc);
287	if (dp == NULL) {
288		mtx_unlock(&sc->sc_mtx);
289		return (ENOMEM);
290	}
291	cbp = g_clone_bio(bp);
292	if (cbp == NULL) {
293		g_cache_free(sc, dp);
294		mtx_unlock(&sc->sc_mtx);
295		return (ENOMEM);
296	}
297
298	dp->d_bno = OFF2BNO(bp->bio_offset + bp->bio_completed, sc);
299	G_CACHE_NEXT_BIO1(bp) = sc;
300	G_CACHE_NEXT_BIO2(bp) = NULL;
301	dp->d_biolist = bp;
302	LIST_INSERT_HEAD(&sc->sc_desclist[G_CACHE_BUCKET(dp->d_bno)],
303	    dp, d_next);
304	mtx_unlock(&sc->sc_mtx);
305
306	G_CACHE_DESC1(cbp) = sc;
307	G_CACHE_DESC2(cbp) = dp;
308	cbp->bio_done = g_cache_done;
309	cbp->bio_offset = BNO2OFF(dp->d_bno, sc);
310	cbp->bio_data = dp->d_data;
311	cbp->bio_length = sc->sc_bsize;
312	g_io_request(cbp, LIST_FIRST(&bp->bio_to->geom->consumer));
313	return (0);
314}
315
316static void
317g_cache_invalidate(struct g_cache_softc *sc, struct bio *bp)
318{
319	struct g_cache_desc *dp;
320	off_t bno, lim;
321
322	mtx_lock(&sc->sc_mtx);
323	bno = OFF2BNO(bp->bio_offset, sc);
324	lim = OFF2BNO(bp->bio_offset + bp->bio_length - 1, sc);
325	do {
326		if ((dp = g_cache_lookup(sc, bno)) != NULL) {
327			LIST_REMOVE(dp, d_next);
328			if (dp->d_flags & D_FLAG_USED) {
329				TAILQ_REMOVE(&sc->sc_usedlist, dp, d_used);
330				sc->sc_nused--;
331			}
332			if (dp->d_biolist == NULL)
333				g_cache_free(sc, dp);
334			else {
335				dp->d_flags = D_FLAG_INVALID;
336				sc->sc_invalid++;
337			}
338		}
339		bno++;
340	} while (bno <= lim);
341	mtx_unlock(&sc->sc_mtx);
342}
343
344static void
345g_cache_start(struct bio *bp)
346{
347	struct g_cache_softc *sc;
348	struct g_geom *gp;
349	struct g_cache_desc *dp;
350	struct bio *cbp;
351
352	gp = bp->bio_to->geom;
353	sc = gp->softc;
354	G_CACHE_LOGREQ(bp, "Request received.");
355	switch (bp->bio_cmd) {
356	case BIO_READ:
357		sc->sc_reads++;
358		sc->sc_readbytes += bp->bio_length;
359		if (!g_cache_enable)
360			break;
361		if (bp->bio_offset + bp->bio_length > sc->sc_tail)
362			break;
363		if (OFF2BNO(bp->bio_offset, sc) ==
364		    OFF2BNO(bp->bio_offset + bp->bio_length - 1, sc)) {
365			sc->sc_cachereads++;
366			sc->sc_cachereadbytes += bp->bio_length;
367			if (g_cache_read(sc, bp) == 0)
368				return;
369			sc->sc_cachereads--;
370			sc->sc_cachereadbytes -= bp->bio_length;
371			break;
372		} else if (OFF2BNO(bp->bio_offset, sc) + 1 ==
373		    OFF2BNO(bp->bio_offset + bp->bio_length - 1, sc)) {
374			mtx_lock(&sc->sc_mtx);
375			dp = g_cache_lookup(sc, OFF2BNO(bp->bio_offset, sc));
376			if (dp == NULL || dp->d_biolist != NULL) {
377				mtx_unlock(&sc->sc_mtx);
378				break;
379			}
380			sc->sc_cachereads++;
381			sc->sc_cachereadbytes += bp->bio_length;
382			g_cache_deliver(sc, bp, dp, 0);
383			mtx_unlock(&sc->sc_mtx);
384			if (g_cache_read(sc, bp) == 0)
385				return;
386			sc->sc_cachereads--;
387			sc->sc_cachereadbytes -= bp->bio_length;
388			break;
389		}
390		break;
391	case BIO_WRITE:
392		sc->sc_writes++;
393		sc->sc_wrotebytes += bp->bio_length;
394		g_cache_invalidate(sc, bp);
395		break;
396	}
397	cbp = g_clone_bio(bp);
398	if (cbp == NULL) {
399		g_io_deliver(bp, ENOMEM);
400		return;
401	}
402	cbp->bio_done = g_std_done;
403	G_CACHE_LOGREQ(cbp, "Sending request.");
404	g_io_request(cbp, LIST_FIRST(&gp->consumer));
405}
406
407static void
408g_cache_go(void *arg)
409{
410	struct g_cache_softc *sc = arg;
411	struct g_cache_desc *dp;
412	int i;
413
414	mtx_assert(&sc->sc_mtx, MA_OWNED);
415
416	/* Forcibly mark idle ready entries as used. */
417	for (i = 0; i < G_CACHE_BUCKETS; i++) {
418		LIST_FOREACH(dp, &sc->sc_desclist[i], d_next) {
419			if (dp->d_flags & D_FLAG_USED ||
420			    dp->d_biolist != NULL ||
421			    time_uptime - dp->d_atime < g_cache_idletime)
422				continue;
423			TAILQ_INSERT_TAIL(&sc->sc_usedlist, dp, d_used);
424			sc->sc_nused++;
425			dp->d_flags |= D_FLAG_USED;
426		}
427	}
428
429	/* Keep the number of used entries low. */
430	if (sc->sc_nused > g_cache_used_hi * sc->sc_maxent / 100)
431		g_cache_free_used(sc);
432
433	callout_reset(&sc->sc_callout, g_cache_timeout * hz, g_cache_go, sc);
434}
435
436static int
437g_cache_access(struct g_provider *pp, int dr, int dw, int de)
438{
439	struct g_geom *gp;
440	struct g_consumer *cp;
441	int error;
442
443	gp = pp->geom;
444	cp = LIST_FIRST(&gp->consumer);
445	error = g_access(cp, dr, dw, de);
446
447	return (error);
448}
449
450static void
451g_cache_orphan(struct g_consumer *cp)
452{
453
454	g_topology_assert();
455	g_cache_destroy(cp->geom->softc, 1);
456}
457
458static struct g_cache_softc *
459g_cache_find_device(struct g_class *mp, const char *name)
460{
461	struct g_geom *gp;
462
463	LIST_FOREACH(gp, &mp->geom, geom) {
464		if (strcmp(gp->name, name) == 0)
465			return (gp->softc);
466	}
467	return (NULL);
468}
469
470static struct g_geom *
471g_cache_create(struct g_class *mp, struct g_provider *pp,
472    const struct g_cache_metadata *md, u_int type)
473{
474	struct g_cache_softc *sc;
475	struct g_geom *gp;
476	struct g_provider *newpp;
477	struct g_consumer *cp;
478	u_int bshift;
479	int i;
480
481	g_topology_assert();
482
483	gp = NULL;
484	newpp = NULL;
485	cp = NULL;
486
487	G_CACHE_DEBUG(1, "Creating device %s.", md->md_name);
488
489	/* Cache size is minimum 100. */
490	if (md->md_size < 100) {
491		G_CACHE_DEBUG(0, "Invalid size for device %s.", md->md_name);
492		return (NULL);
493	}
494
495	/* Block size restrictions. */
496	bshift = ffs(md->md_bsize) - 1;
497	if (md->md_bsize == 0 || md->md_bsize > maxphys ||
498	    md->md_bsize != 1 << bshift ||
499	    (md->md_bsize % pp->sectorsize) != 0) {
500		G_CACHE_DEBUG(0, "Invalid blocksize for provider %s.", pp->name);
501		return (NULL);
502	}
503
504	/* Check for duplicate unit. */
505	if (g_cache_find_device(mp, (const char *)&md->md_name) != NULL) {
506		G_CACHE_DEBUG(0, "Provider %s already exists.", md->md_name);
507		return (NULL);
508	}
509
510	gp = g_new_geomf(mp, "%s", md->md_name);
511	sc = g_malloc(sizeof(*sc), M_WAITOK | M_ZERO);
512	sc->sc_type = type;
513	sc->sc_bshift = bshift;
514	sc->sc_bsize = 1 << bshift;
515	sc->sc_zone = uma_zcreate("gcache", sc->sc_bsize, NULL, NULL, NULL, NULL,
516	    UMA_ALIGN_PTR, 0);
517	mtx_init(&sc->sc_mtx, "GEOM CACHE mutex", NULL, MTX_DEF);
518	for (i = 0; i < G_CACHE_BUCKETS; i++)
519		LIST_INIT(&sc->sc_desclist[i]);
520	TAILQ_INIT(&sc->sc_usedlist);
521	sc->sc_maxent = md->md_size;
522	callout_init_mtx(&sc->sc_callout, &sc->sc_mtx, 0);
523	gp->softc = sc;
524	sc->sc_geom = gp;
525	gp->start = g_cache_start;
526	gp->orphan = g_cache_orphan;
527	gp->access = g_cache_access;
528	gp->dumpconf = g_cache_dumpconf;
529
530	newpp = g_new_providerf(gp, "cache/%s", gp->name);
531	newpp->sectorsize = pp->sectorsize;
532	newpp->mediasize = pp->mediasize;
533	if (type == G_CACHE_TYPE_AUTOMATIC)
534		newpp->mediasize -= pp->sectorsize;
535	sc->sc_tail = BNO2OFF(OFF2BNO(newpp->mediasize, sc), sc);
536
537	cp = g_new_consumer(gp);
538	if (g_attach(cp, pp) != 0) {
539		G_CACHE_DEBUG(0, "Cannot attach to provider %s.", pp->name);
540		g_destroy_consumer(cp);
541		g_destroy_provider(newpp);
542		mtx_destroy(&sc->sc_mtx);
543		g_free(sc);
544		g_destroy_geom(gp);
545		return (NULL);
546	}
547
548	g_error_provider(newpp, 0);
549	G_CACHE_DEBUG(0, "Device %s created.", gp->name);
550	callout_reset(&sc->sc_callout, g_cache_timeout * hz, g_cache_go, sc);
551	return (gp);
552}
553
554static int
555g_cache_destroy(struct g_cache_softc *sc, boolean_t force)
556{
557	struct g_geom *gp;
558	struct g_provider *pp;
559	struct g_cache_desc *dp, *dp2;
560	int i;
561
562	g_topology_assert();
563	if (sc == NULL)
564		return (ENXIO);
565	gp = sc->sc_geom;
566	pp = LIST_FIRST(&gp->provider);
567	if (pp != NULL && (pp->acr != 0 || pp->acw != 0 || pp->ace != 0)) {
568		if (force) {
569			G_CACHE_DEBUG(0, "Device %s is still open, so it "
570			    "can't be definitely removed.", pp->name);
571		} else {
572			G_CACHE_DEBUG(1, "Device %s is still open (r%dw%de%d).",
573			    pp->name, pp->acr, pp->acw, pp->ace);
574			return (EBUSY);
575		}
576	} else {
577		G_CACHE_DEBUG(0, "Device %s removed.", gp->name);
578	}
579	callout_drain(&sc->sc_callout);
580	mtx_lock(&sc->sc_mtx);
581	for (i = 0; i < G_CACHE_BUCKETS; i++) {
582		dp = LIST_FIRST(&sc->sc_desclist[i]);
583		while (dp != NULL) {
584			dp2 = LIST_NEXT(dp, d_next);
585			g_cache_free(sc, dp);
586			dp = dp2;
587		}
588	}
589	mtx_unlock(&sc->sc_mtx);
590	mtx_destroy(&sc->sc_mtx);
591	uma_zdestroy(sc->sc_zone);
592	g_free(sc);
593	gp->softc = NULL;
594	g_wither_geom(gp, ENXIO);
595
596	return (0);
597}
598
599static int
600g_cache_destroy_geom(struct gctl_req *req, struct g_class *mp, struct g_geom *gp)
601{
602
603	return (g_cache_destroy(gp->softc, 0));
604}
605
606static int
607g_cache_read_metadata(struct g_consumer *cp, struct g_cache_metadata *md)
608{
609	struct g_provider *pp;
610	u_char *buf;
611	int error;
612
613	g_topology_assert();
614
615	error = g_access(cp, 1, 0, 0);
616	if (error != 0)
617		return (error);
618	pp = cp->provider;
619	g_topology_unlock();
620	buf = g_read_data(cp, pp->mediasize - pp->sectorsize, pp->sectorsize,
621	    &error);
622	g_topology_lock();
623	g_access(cp, -1, 0, 0);
624	if (buf == NULL)
625		return (error);
626
627	/* Decode metadata. */
628	cache_metadata_decode(buf, md);
629	g_free(buf);
630
631	return (0);
632}
633
634static int
635g_cache_write_metadata(struct g_consumer *cp, struct g_cache_metadata *md)
636{
637	struct g_provider *pp;
638	u_char *buf;
639	int error;
640
641	g_topology_assert();
642
643	error = g_access(cp, 0, 1, 0);
644	if (error != 0)
645		return (error);
646	pp = cp->provider;
647	buf = malloc((size_t)pp->sectorsize, M_GCACHE, M_WAITOK | M_ZERO);
648	cache_metadata_encode(md, buf);
649	g_topology_unlock();
650	error = g_write_data(cp, pp->mediasize - pp->sectorsize, buf, pp->sectorsize);
651	g_topology_lock();
652	g_access(cp, 0, -1, 0);
653	free(buf, M_GCACHE);
654
655	return (error);
656}
657
658static struct g_geom *
659g_cache_taste(struct g_class *mp, struct g_provider *pp, int flags __unused)
660{
661	struct g_cache_metadata md;
662	struct g_consumer *cp;
663	struct g_geom *gp;
664	int error;
665
666	g_trace(G_T_TOPOLOGY, "%s(%s, %s)", __func__, mp->name, pp->name);
667	g_topology_assert();
668
669	G_CACHE_DEBUG(3, "Tasting %s.", pp->name);
670
671	gp = g_new_geomf(mp, "cache:taste");
672	gp->start = g_cache_start;
673	gp->orphan = g_cache_orphan;
674	gp->access = g_cache_access;
675	cp = g_new_consumer(gp);
676	error = g_attach(cp, pp);
677	if (error == 0) {
678		error = g_cache_read_metadata(cp, &md);
679		g_detach(cp);
680	}
681	g_destroy_consumer(cp);
682	g_destroy_geom(gp);
683	if (error != 0)
684		return (NULL);
685
686	if (strcmp(md.md_magic, G_CACHE_MAGIC) != 0)
687		return (NULL);
688	if (md.md_version > G_CACHE_VERSION) {
689		printf("geom_cache.ko module is too old to handle %s.\n",
690		    pp->name);
691		return (NULL);
692	}
693	if (md.md_provsize != pp->mediasize)
694		return (NULL);
695
696	gp = g_cache_create(mp, pp, &md, G_CACHE_TYPE_AUTOMATIC);
697	if (gp == NULL) {
698		G_CACHE_DEBUG(0, "Can't create %s.", md.md_name);
699		return (NULL);
700	}
701	return (gp);
702}
703
704static void
705g_cache_ctl_create(struct gctl_req *req, struct g_class *mp)
706{
707	struct g_cache_metadata md;
708	struct g_provider *pp;
709	struct g_geom *gp;
710	intmax_t *bsize, *size;
711	const char *name;
712	int *nargs;
713
714	g_topology_assert();
715
716	nargs = gctl_get_paraml(req, "nargs", sizeof(*nargs));
717	if (nargs == NULL) {
718		gctl_error(req, "No '%s' argument", "nargs");
719		return;
720	}
721	if (*nargs != 2) {
722		gctl_error(req, "Invalid number of arguments.");
723		return;
724	}
725
726	strlcpy(md.md_magic, G_CACHE_MAGIC, sizeof(md.md_magic));
727	md.md_version = G_CACHE_VERSION;
728	name = gctl_get_asciiparam(req, "arg0");
729	if (name == NULL) {
730		gctl_error(req, "No 'arg0' argument");
731		return;
732	}
733	strlcpy(md.md_name, name, sizeof(md.md_name));
734
735	size = gctl_get_paraml(req, "size", sizeof(*size));
736	if (size == NULL) {
737		gctl_error(req, "No '%s' argument", "size");
738		return;
739	}
740	if ((u_int)*size < 100) {
741		gctl_error(req, "Invalid '%s' argument", "size");
742		return;
743	}
744	md.md_size = (u_int)*size;
745
746	bsize = gctl_get_paraml(req, "blocksize", sizeof(*bsize));
747	if (bsize == NULL) {
748		gctl_error(req, "No '%s' argument", "blocksize");
749		return;
750	}
751	if (*bsize < 0) {
752		gctl_error(req, "Invalid '%s' argument", "blocksize");
753		return;
754	}
755	md.md_bsize = (u_int)*bsize;
756
757	/* This field is not important here. */
758	md.md_provsize = 0;
759
760	pp = gctl_get_provider(req, "arg1");
761	if (pp == NULL)
762		return;
763	gp = g_cache_create(mp, pp, &md, G_CACHE_TYPE_MANUAL);
764	if (gp == NULL) {
765		gctl_error(req, "Can't create %s.", md.md_name);
766		return;
767	}
768}
769
770static void
771g_cache_ctl_configure(struct gctl_req *req, struct g_class *mp)
772{
773	struct g_cache_metadata md;
774	struct g_cache_softc *sc;
775	struct g_consumer *cp;
776	intmax_t *bsize, *size;
777	const char *name;
778	int error, *nargs;
779
780	g_topology_assert();
781
782	nargs = gctl_get_paraml(req, "nargs", sizeof(*nargs));
783	if (nargs == NULL) {
784		gctl_error(req, "No '%s' argument", "nargs");
785		return;
786	}
787	if (*nargs != 1) {
788		gctl_error(req, "Missing device.");
789		return;
790	}
791
792	name = gctl_get_asciiparam(req, "arg0");
793	if (name == NULL) {
794		gctl_error(req, "No 'arg0' argument");
795		return;
796	}
797	sc = g_cache_find_device(mp, name);
798	if (sc == NULL) {
799		G_CACHE_DEBUG(1, "Device %s is invalid.", name);
800		gctl_error(req, "Device %s is invalid.", name);
801		return;
802	}
803
804	size = gctl_get_paraml(req, "size", sizeof(*size));
805	if (size == NULL) {
806		gctl_error(req, "No '%s' argument", "size");
807		return;
808	}
809	if ((u_int)*size != 0 && (u_int)*size < 100) {
810		gctl_error(req, "Invalid '%s' argument", "size");
811		return;
812	}
813	if ((u_int)*size != 0)
814		sc->sc_maxent = (u_int)*size;
815
816	bsize = gctl_get_paraml(req, "blocksize", sizeof(*bsize));
817	if (bsize == NULL) {
818		gctl_error(req, "No '%s' argument", "blocksize");
819		return;
820	}
821	if (*bsize < 0) {
822		gctl_error(req, "Invalid '%s' argument", "blocksize");
823		return;
824	}
825
826	if (sc->sc_type != G_CACHE_TYPE_AUTOMATIC)
827		return;
828
829	strlcpy(md.md_name, name, sizeof(md.md_name));
830	strlcpy(md.md_magic, G_CACHE_MAGIC, sizeof(md.md_magic));
831	md.md_version = G_CACHE_VERSION;
832	if ((u_int)*size != 0)
833		md.md_size = (u_int)*size;
834	else
835		md.md_size = sc->sc_maxent;
836	if ((u_int)*bsize != 0)
837		md.md_bsize = (u_int)*bsize;
838	else
839		md.md_bsize = sc->sc_bsize;
840	cp = LIST_FIRST(&sc->sc_geom->consumer);
841	md.md_provsize = cp->provider->mediasize;
842	error = g_cache_write_metadata(cp, &md);
843	if (error == 0)
844		G_CACHE_DEBUG(2, "Metadata on %s updated.", cp->provider->name);
845	else
846		G_CACHE_DEBUG(0, "Cannot update metadata on %s (error=%d).",
847		    cp->provider->name, error);
848}
849
850static void
851g_cache_ctl_destroy(struct gctl_req *req, struct g_class *mp)
852{
853	int *nargs, *force, error, i;
854	struct g_cache_softc *sc;
855	const char *name;
856	char param[16];
857
858	g_topology_assert();
859
860	nargs = gctl_get_paraml(req, "nargs", sizeof(*nargs));
861	if (nargs == NULL) {
862		gctl_error(req, "No '%s' argument", "nargs");
863		return;
864	}
865	if (*nargs <= 0) {
866		gctl_error(req, "Missing device(s).");
867		return;
868	}
869	force = gctl_get_paraml(req, "force", sizeof(*force));
870	if (force == NULL) {
871		gctl_error(req, "No 'force' argument");
872		return;
873	}
874
875	for (i = 0; i < *nargs; i++) {
876		snprintf(param, sizeof(param), "arg%d", i);
877		name = gctl_get_asciiparam(req, param);
878		if (name == NULL) {
879			gctl_error(req, "No 'arg%d' argument", i);
880			return;
881		}
882		sc = g_cache_find_device(mp, name);
883		if (sc == NULL) {
884			G_CACHE_DEBUG(1, "Device %s is invalid.", name);
885			gctl_error(req, "Device %s is invalid.", name);
886			return;
887		}
888		error = g_cache_destroy(sc, *force);
889		if (error != 0) {
890			gctl_error(req, "Cannot destroy device %s (error=%d).",
891			    sc->sc_name, error);
892			return;
893		}
894	}
895}
896
897static void
898g_cache_ctl_reset(struct gctl_req *req, struct g_class *mp)
899{
900	struct g_cache_softc *sc;
901	const char *name;
902	char param[16];
903	int i, *nargs;
904
905	g_topology_assert();
906
907	nargs = gctl_get_paraml(req, "nargs", sizeof(*nargs));
908	if (nargs == NULL) {
909		gctl_error(req, "No '%s' argument", "nargs");
910		return;
911	}
912	if (*nargs <= 0) {
913		gctl_error(req, "Missing device(s).");
914		return;
915	}
916
917	for (i = 0; i < *nargs; i++) {
918		snprintf(param, sizeof(param), "arg%d", i);
919		name = gctl_get_asciiparam(req, param);
920		if (name == NULL) {
921			gctl_error(req, "No 'arg%d' argument", i);
922			return;
923		}
924		sc = g_cache_find_device(mp, name);
925		if (sc == NULL) {
926			G_CACHE_DEBUG(1, "Device %s is invalid.", name);
927			gctl_error(req, "Device %s is invalid.", name);
928			return;
929		}
930		sc->sc_reads = 0;
931		sc->sc_readbytes = 0;
932		sc->sc_cachereads = 0;
933		sc->sc_cachereadbytes = 0;
934		sc->sc_cachehits = 0;
935		sc->sc_cachemisses = 0;
936		sc->sc_cachefull = 0;
937		sc->sc_writes = 0;
938		sc->sc_wrotebytes = 0;
939	}
940}
941
942static void
943g_cache_config(struct gctl_req *req, struct g_class *mp, const char *verb)
944{
945	uint32_t *version;
946
947	g_topology_assert();
948
949	version = gctl_get_paraml(req, "version", sizeof(*version));
950	if (version == NULL) {
951		gctl_error(req, "No '%s' argument.", "version");
952		return;
953	}
954	if (*version != G_CACHE_VERSION) {
955		gctl_error(req, "Userland and kernel parts are out of sync.");
956		return;
957	}
958
959	if (strcmp(verb, "create") == 0) {
960		g_cache_ctl_create(req, mp);
961		return;
962	} else if (strcmp(verb, "configure") == 0) {
963		g_cache_ctl_configure(req, mp);
964		return;
965	} else if (strcmp(verb, "destroy") == 0 ||
966	    strcmp(verb, "stop") == 0) {
967		g_cache_ctl_destroy(req, mp);
968		return;
969	} else if (strcmp(verb, "reset") == 0) {
970		g_cache_ctl_reset(req, mp);
971		return;
972	}
973
974	gctl_error(req, "Unknown verb.");
975}
976
977static void
978g_cache_dumpconf(struct sbuf *sb, const char *indent, struct g_geom *gp,
979    struct g_consumer *cp, struct g_provider *pp)
980{
981	struct g_cache_softc *sc;
982
983	if (pp != NULL || cp != NULL)
984		return;
985	sc = gp->softc;
986	sbuf_printf(sb, "%s<Size>%u</Size>\n", indent, sc->sc_maxent);
987	sbuf_printf(sb, "%s<BlockSize>%u</BlockSize>\n", indent, sc->sc_bsize);
988	sbuf_printf(sb, "%s<TailOffset>%ju</TailOffset>\n", indent,
989	    (uintmax_t)sc->sc_tail);
990	sbuf_printf(sb, "%s<Entries>%u</Entries>\n", indent, sc->sc_nent);
991	sbuf_printf(sb, "%s<UsedEntries>%u</UsedEntries>\n", indent,
992	    sc->sc_nused);
993	sbuf_printf(sb, "%s<InvalidEntries>%u</InvalidEntries>\n", indent,
994	    sc->sc_invalid);
995	sbuf_printf(sb, "%s<Reads>%ju</Reads>\n", indent, sc->sc_reads);
996	sbuf_printf(sb, "%s<ReadBytes>%ju</ReadBytes>\n", indent,
997	    sc->sc_readbytes);
998	sbuf_printf(sb, "%s<CacheReads>%ju</CacheReads>\n", indent,
999	    sc->sc_cachereads);
1000	sbuf_printf(sb, "%s<CacheReadBytes>%ju</CacheReadBytes>\n", indent,
1001	    sc->sc_cachereadbytes);
1002	sbuf_printf(sb, "%s<CacheHits>%ju</CacheHits>\n", indent,
1003	    sc->sc_cachehits);
1004	sbuf_printf(sb, "%s<CacheMisses>%ju</CacheMisses>\n", indent,
1005	    sc->sc_cachemisses);
1006	sbuf_printf(sb, "%s<CacheFull>%ju</CacheFull>\n", indent,
1007	    sc->sc_cachefull);
1008	sbuf_printf(sb, "%s<Writes>%ju</Writes>\n", indent, sc->sc_writes);
1009	sbuf_printf(sb, "%s<WroteBytes>%ju</WroteBytes>\n", indent,
1010	    sc->sc_wrotebytes);
1011}
1012
1013DECLARE_GEOM_CLASS(g_cache_class, g_cache);
1014MODULE_VERSION(geom_cache, 0);
1015