4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
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
44 #include <nbdkit-filter.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
;
62 cacheextents_unload (void)
64 nbdkit_extents_free (cache_extents
);
68 cacheextents_add (struct nbdkit_extents
*extents
, int *err
)
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) {
84 fill (struct nbdkit_extents
*extents
, int *err
)
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
);
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:"
107 ex
.offset
, ex
.length
, ex
.type
);
109 if (nbdkit_add_extent (cache_extents
, ex
.offset
, ex
.length
,
112 nbdkit_extents_free (cache_extents
);
113 cache_extents
= NULL
;
122 cacheextents_extents (nbdkit_next
*next
,
123 void *handle
, uint32_t count
, uint64_t offset
,
125 struct nbdkit_extents
*extents
,
128 ACQUIRE_LOCK_FOR_CURRENT_SCOPE (&lock
);
130 if (cacheextents_debug_cache
)
131 nbdkit_debug ("cacheextents:"
132 " cache_start=%" PRIu64
133 " cache_end=%" PRIu64
135 cache_start
, cache_end
, 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)
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.
165 kill_cacheextents (void)
167 ACQUIRE_LOCK_FOR_CURRENT_SCOPE (&lock
);
168 nbdkit_extents_free (cache_extents
);
169 cache_extents
= NULL
;
173 cacheextents_pwrite (nbdkit_next
*next
,
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
);
183 cacheextents_trim (nbdkit_next
*next
,
185 uint32_t count
, uint64_t offset
, uint32_t flags
,
188 kill_cacheextents ();
189 return next
->trim (next
, count
, offset
, flags
, err
);
193 cacheextents_zero (nbdkit_next
*next
,
195 uint32_t count
, uint64_t offset
, uint32_t flags
,
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
)