Update Red Hat Copyright Notices
[nbdkit.git] / filters / cacheextents / cacheextents.c
blob71f73c41d89f688f4b6ad7b7591a1a353f38b2a7
1 /* nbdkit
2 * Copyright Red Hat
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
6 * met:
8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
11 * * Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
15 * * Neither the name of Red Hat nor the names of its contributors may be
16 * used to endorse or promote products derived from this software without
17 * specific prior written permission.
19 * THIS SOFTWARE IS PROVIDED BY RED HAT AND CONTRIBUTORS ''AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
21 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
22 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL RED HAT OR
23 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
26 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
27 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
28 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
29 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 * SUCH DAMAGE.
33 #include <config.h>
35 #include <stdio.h>
36 #include <stdlib.h>
37 #include <stdint.h>
38 #include <string.h>
39 #include <errno.h>
40 #include <inttypes.h>
42 #include <pthread.h>
44 #include <nbdkit-filter.h>
46 #include "cleanup.h"
48 /* -D cacheextents.cache=1: Debug cache operations. */
49 NBDKIT_DLL_PUBLIC int cacheextents_debug_cache = 0;
51 /* This lock protects the global state. */
52 static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
54 /* Cached extents from the last extents () call and its start and end
55 * for the sake of simplicity.
57 struct nbdkit_extents *cache_extents;
58 static uint64_t cache_start;
59 static uint64_t cache_end;
61 static void
62 cacheextents_unload (void)
64 nbdkit_extents_free (cache_extents);
67 static int
68 cacheextents_add (struct nbdkit_extents *extents, int *err)
70 size_t i = 0;
72 for (i = 0; i < nbdkit_extents_count (cache_extents); i++) {
73 struct nbdkit_extent ex = nbdkit_get_extent (cache_extents, i);
74 if (nbdkit_add_extent (extents, ex.offset, ex.length, ex.type) == -1) {
75 *err = errno;
76 return -1;
80 return 0;
83 static int
84 fill (struct nbdkit_extents *extents, int *err)
86 size_t i = 0;
87 size_t count = nbdkit_extents_count (extents);
88 struct nbdkit_extent first = nbdkit_get_extent (extents, 0);
89 struct nbdkit_extent last = nbdkit_get_extent (extents, count - 1);
91 nbdkit_extents_free (cache_extents);
92 cache_start = first.offset;
93 cache_end = last.offset + last.length;
94 cache_extents = nbdkit_extents_new (cache_start, cache_end);
96 if (!cache_extents)
97 return -1;
99 for (i = 0; i < count; i++) {
100 struct nbdkit_extent ex = nbdkit_get_extent (extents, i);
102 if (cacheextents_debug_cache)
103 nbdkit_debug ("cacheextents: updating cache with:"
104 " offset=%" PRIu64
105 " length=%" PRIu64
106 " type=%x",
107 ex.offset, ex.length, ex.type);
109 if (nbdkit_add_extent (cache_extents, ex.offset, ex.length,
110 ex.type) == -1) {
111 *err = errno;
112 nbdkit_extents_free (cache_extents);
113 cache_extents = NULL;
114 return -1;
118 return 0;
121 static int
122 cacheextents_extents (nbdkit_next *next,
123 void *handle, uint32_t count, uint64_t offset,
124 uint32_t flags,
125 struct nbdkit_extents *extents,
126 int *err)
128 ACQUIRE_LOCK_FOR_CURRENT_SCOPE (&lock);
130 if (cacheextents_debug_cache)
131 nbdkit_debug ("cacheextents:"
132 " cache_start=%" PRIu64
133 " cache_end=%" PRIu64
134 " cache_extents=%p",
135 cache_start, cache_end, cache_extents);
137 if (cache_extents &&
138 offset >= cache_start && offset < cache_end) {
139 if (cacheextents_debug_cache)
140 nbdkit_debug ("cacheextents: returning from cache");
141 return cacheextents_add (extents, err);
144 if (cacheextents_debug_cache)
145 nbdkit_debug ("cacheextents: cache miss");
147 /* Clear REQ_ONE to ask the plugin for as much information as it is
148 * willing to return (the plugin may still truncate if it is too
149 * costly to provide everything).
151 flags &= ~(NBDKIT_FLAG_REQ_ONE);
152 if (next->extents (next, count, offset, flags, extents, err) == -1)
153 return -1;
155 return fill (extents, err);
158 /* Any changes to the data needs to clean the cache.
160 * Similar to the readahead filter this could be more intelligent, but
161 * there would be very little benefit.
164 static void
165 kill_cacheextents (void)
167 ACQUIRE_LOCK_FOR_CURRENT_SCOPE (&lock);
168 nbdkit_extents_free (cache_extents);
169 cache_extents = NULL;
172 static int
173 cacheextents_pwrite (nbdkit_next *next,
174 void *handle,
175 const void *buf, uint32_t count, uint64_t offset,
176 uint32_t flags, int *err)
178 kill_cacheextents ();
179 return next->pwrite (next, buf, count, offset, flags, err);
182 static int
183 cacheextents_trim (nbdkit_next *next,
184 void *handle,
185 uint32_t count, uint64_t offset, uint32_t flags,
186 int *err)
188 kill_cacheextents ();
189 return next->trim (next, count, offset, flags, err);
192 static int
193 cacheextents_zero (nbdkit_next *next,
194 void *handle,
195 uint32_t count, uint64_t offset, uint32_t flags,
196 int *err)
198 kill_cacheextents ();
199 return next->zero (next, count, offset, flags, err);
202 static struct nbdkit_filter filter = {
203 .name = "cacheextents",
204 .longname = "nbdkit cacheextents filter",
205 .unload = cacheextents_unload,
206 .pwrite = cacheextents_pwrite,
207 .trim = cacheextents_trim,
208 .zero = cacheextents_zero,
209 .extents = cacheextents_extents,
212 NBDKIT_REGISTER_FILTER (filter)