Skip to content

Commit 0dd0691

Browse files
yangdongshengkawasaki
authored andcommitted
dm-pcache: add cache_segment
Introduce *cache_segment.c*, the in-memory/on-disk glue that lets a `struct pcache_cache` manage its array of data segments. * Metadata handling - Loads the most-recent replica of both the segment-info block (`struct pcache_segment_info`) and per-segment generation counter (`struct pcache_cache_seg_gen`) using `pcache_meta_find_latest()`. - Updates those structures atomically with CRC + sequence rollover, writing alternately to the two metadata slots inside each segment. * Segment initialisation (`cache_seg_init`) - Builds a `struct pcache_segment` pointing to the segment’s data area, sets up locks, generation counters, and, when formatting a new cache, zeroes the on-segment kset header. * Linked-list of segments - `cache_seg_set_next_seg()` stores the *next* segment id in `seg_info->next_seg` and sets the HAS_NEXT flag, allowing a cache to span multiple segments. This is important to allow other type of segment added in future. * Runtime life-cycle - Reference counting (`cache_seg_get/put`) with invalidate-on-last-put that clears the bitmap slot and schedules cleanup work. - Generation bump (`cache_seg_gen_increase`) persists a new generation record whenever the segment is modified. * Allocator - `get_cache_segment()` uses a bitmap and per-cache hint to pick the next free segment, retrying with micro-delays when none are immediately available. Signed-off-by: Dongsheng Yang <[email protected]>
1 parent 7446a69 commit 0dd0691

1 file changed

Lines changed: 293 additions & 0 deletions

File tree

Lines changed: 293 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,293 @@
1+
// SPDX-License-Identifier: GPL-2.0-or-later
2+
3+
#include "cache_dev.h"
4+
#include "cache.h"
5+
#include "backing_dev.h"
6+
#include "dm_pcache.h"
7+
8+
static inline struct pcache_segment_info *get_seg_info_addr(struct pcache_cache_segment *cache_seg)
9+
{
10+
struct pcache_segment_info *seg_info_addr;
11+
u32 seg_id = cache_seg->segment.seg_id;
12+
void *seg_addr;
13+
14+
seg_addr = CACHE_DEV_SEGMENT(cache_seg->cache->cache_dev, seg_id);
15+
seg_info_addr = seg_addr + PCACHE_SEG_INFO_SIZE * cache_seg->info_index;
16+
17+
return seg_info_addr;
18+
}
19+
20+
static void cache_seg_info_write(struct pcache_cache_segment *cache_seg)
21+
{
22+
struct pcache_segment_info *seg_info_addr;
23+
struct pcache_segment_info *seg_info = &cache_seg->cache_seg_info;
24+
25+
mutex_lock(&cache_seg->info_lock);
26+
seg_info->header.seq++;
27+
seg_info->header.crc = pcache_meta_crc(&seg_info->header, sizeof(struct pcache_segment_info));
28+
29+
seg_info_addr = get_seg_info_addr(cache_seg);
30+
memcpy_flushcache(seg_info_addr, seg_info, sizeof(struct pcache_segment_info));
31+
pmem_wmb();
32+
33+
cache_seg->info_index = (cache_seg->info_index + 1) % PCACHE_META_INDEX_MAX;
34+
mutex_unlock(&cache_seg->info_lock);
35+
}
36+
37+
static int cache_seg_info_load(struct pcache_cache_segment *cache_seg)
38+
{
39+
struct pcache_segment_info *cache_seg_info_addr_base, *cache_seg_info_addr;
40+
struct pcache_cache_dev *cache_dev = cache_seg->cache->cache_dev;
41+
struct dm_pcache *pcache = CACHE_DEV_TO_PCACHE(cache_dev);
42+
u32 seg_id = cache_seg->segment.seg_id;
43+
int ret = 0;
44+
45+
cache_seg_info_addr_base = CACHE_DEV_SEGMENT(cache_dev, seg_id);
46+
47+
mutex_lock(&cache_seg->info_lock);
48+
cache_seg_info_addr = pcache_meta_find_latest(&cache_seg_info_addr_base->header,
49+
sizeof(struct pcache_segment_info),
50+
PCACHE_SEG_INFO_SIZE,
51+
&cache_seg->cache_seg_info);
52+
if (IS_ERR(cache_seg_info_addr)) {
53+
ret = PTR_ERR(cache_seg_info_addr);
54+
goto out;
55+
} else if (!cache_seg_info_addr) {
56+
ret = -EIO;
57+
goto out;
58+
}
59+
cache_seg->info_index = cache_seg_info_addr - cache_seg_info_addr_base;
60+
out:
61+
mutex_unlock(&cache_seg->info_lock);
62+
63+
if (ret)
64+
pcache_dev_err(pcache, "can't read segment info of segment: %u, ret: %d\n",
65+
cache_seg->segment.seg_id, ret);
66+
return ret;
67+
}
68+
69+
static int cache_seg_ctrl_load(struct pcache_cache_segment *cache_seg)
70+
{
71+
struct pcache_cache_seg_ctrl *cache_seg_ctrl = cache_seg->cache_seg_ctrl;
72+
struct pcache_cache_seg_gen cache_seg_gen, *cache_seg_gen_addr;
73+
int ret = 0;
74+
75+
mutex_lock(&cache_seg->ctrl_lock);
76+
cache_seg_gen_addr = pcache_meta_find_latest(&cache_seg_ctrl->gen->header,
77+
sizeof(struct pcache_cache_seg_gen),
78+
sizeof(struct pcache_cache_seg_gen),
79+
&cache_seg_gen);
80+
if (IS_ERR(cache_seg_gen_addr)) {
81+
ret = PTR_ERR(cache_seg_gen_addr);
82+
goto out;
83+
}
84+
85+
if (!cache_seg_gen_addr) {
86+
cache_seg->gen = 0;
87+
cache_seg->gen_seq = 0;
88+
cache_seg->gen_index = 0;
89+
goto out;
90+
}
91+
92+
cache_seg->gen = cache_seg_gen.gen;
93+
cache_seg->gen_seq = cache_seg_gen.header.seq;
94+
cache_seg->gen_index = (cache_seg_gen_addr - cache_seg_ctrl->gen);
95+
out:
96+
mutex_unlock(&cache_seg->ctrl_lock);
97+
98+
return ret;
99+
}
100+
101+
static inline struct pcache_cache_seg_gen *get_cache_seg_gen_addr(struct pcache_cache_segment *cache_seg)
102+
{
103+
struct pcache_cache_seg_ctrl *cache_seg_ctrl = cache_seg->cache_seg_ctrl;
104+
105+
return (cache_seg_ctrl->gen + cache_seg->gen_index);
106+
}
107+
108+
static void cache_seg_ctrl_write(struct pcache_cache_segment *cache_seg)
109+
{
110+
struct pcache_cache_seg_gen cache_seg_gen;
111+
112+
mutex_lock(&cache_seg->ctrl_lock);
113+
cache_seg_gen.gen = cache_seg->gen;
114+
cache_seg_gen.header.seq = ++cache_seg->gen_seq;
115+
cache_seg_gen.header.crc = pcache_meta_crc(&cache_seg_gen.header,
116+
sizeof(struct pcache_cache_seg_gen));
117+
118+
memcpy_flushcache(get_cache_seg_gen_addr(cache_seg), &cache_seg_gen, sizeof(struct pcache_cache_seg_gen));
119+
pmem_wmb();
120+
121+
cache_seg->gen_index = (cache_seg->gen_index + 1) % PCACHE_META_INDEX_MAX;
122+
mutex_unlock(&cache_seg->ctrl_lock);
123+
}
124+
125+
static void cache_seg_ctrl_init(struct pcache_cache_segment *cache_seg)
126+
{
127+
cache_seg->gen = 0;
128+
cache_seg->gen_seq = 0;
129+
cache_seg->gen_index = 0;
130+
cache_seg_ctrl_write(cache_seg);
131+
}
132+
133+
static int cache_seg_meta_load(struct pcache_cache_segment *cache_seg)
134+
{
135+
int ret;
136+
137+
ret = cache_seg_info_load(cache_seg);
138+
if (ret)
139+
goto err;
140+
141+
ret = cache_seg_ctrl_load(cache_seg);
142+
if (ret)
143+
goto err;
144+
145+
return 0;
146+
err:
147+
return ret;
148+
}
149+
150+
/**
151+
* cache_seg_set_next_seg - Sets the ID of the next segment
152+
* @cache_seg: Pointer to the cache segment structure.
153+
* @seg_id: The segment ID to set as the next segment.
154+
*
155+
* A pcache_cache allocates multiple cache segments, which are linked together
156+
* through next_seg. When loading a pcache_cache, the first cache segment can
157+
* be found using cache->seg_id, which allows access to all the cache segments.
158+
*/
159+
void cache_seg_set_next_seg(struct pcache_cache_segment *cache_seg, u32 seg_id)
160+
{
161+
cache_seg->cache_seg_info.flags |= PCACHE_SEG_INFO_FLAGS_HAS_NEXT;
162+
cache_seg->cache_seg_info.next_seg = seg_id;
163+
cache_seg_info_write(cache_seg);
164+
}
165+
166+
int cache_seg_init(struct pcache_cache *cache, u32 seg_id, u32 cache_seg_id,
167+
bool new_cache)
168+
{
169+
struct pcache_cache_dev *cache_dev = cache->cache_dev;
170+
struct pcache_cache_segment *cache_seg = &cache->segments[cache_seg_id];
171+
struct pcache_segment_init_options seg_options = { 0 };
172+
struct pcache_segment *segment = &cache_seg->segment;
173+
int ret;
174+
175+
cache_seg->cache = cache;
176+
cache_seg->cache_seg_id = cache_seg_id;
177+
spin_lock_init(&cache_seg->gen_lock);
178+
atomic_set(&cache_seg->refs, 0);
179+
mutex_init(&cache_seg->info_lock);
180+
mutex_init(&cache_seg->ctrl_lock);
181+
182+
/* init pcache_segment */
183+
seg_options.type = PCACHE_SEGMENT_TYPE_CACHE_DATA;
184+
seg_options.data_off = PCACHE_CACHE_SEG_CTRL_OFF + PCACHE_CACHE_SEG_CTRL_SIZE;
185+
seg_options.seg_id = seg_id;
186+
seg_options.seg_info = &cache_seg->cache_seg_info;
187+
pcache_segment_init(cache_dev, segment, &seg_options);
188+
189+
cache_seg->cache_seg_ctrl = CACHE_DEV_SEGMENT(cache_dev, seg_id) + PCACHE_CACHE_SEG_CTRL_OFF;
190+
191+
if (new_cache) {
192+
cache_dev_zero_range(cache_dev, CACHE_DEV_SEGMENT(cache_dev, seg_id),
193+
PCACHE_SEG_INFO_SIZE * PCACHE_META_INDEX_MAX +
194+
PCACHE_CACHE_SEG_CTRL_SIZE);
195+
196+
cache_seg_ctrl_init(cache_seg);
197+
198+
cache_seg->info_index = 0;
199+
cache_seg_info_write(cache_seg);
200+
201+
/* clear outdated kset in segment */
202+
memcpy_flushcache(segment->data, &pcache_empty_kset, sizeof(struct pcache_cache_kset_onmedia));
203+
pmem_wmb();
204+
} else {
205+
ret = cache_seg_meta_load(cache_seg);
206+
if (ret)
207+
goto err;
208+
}
209+
210+
return 0;
211+
err:
212+
return ret;
213+
}
214+
215+
/**
216+
* get_cache_segment - Retrieves a free cache segment from the cache.
217+
* @cache: Pointer to the cache structure.
218+
*
219+
* This function attempts to find a free cache segment that can be used.
220+
* It locks the segment map and checks for the next available segment ID.
221+
* If a free segment is found, it initializes it and returns a pointer to the
222+
* cache segment structure. Returns NULL if no segments are available.
223+
*/
224+
struct pcache_cache_segment *get_cache_segment(struct pcache_cache *cache)
225+
{
226+
struct pcache_cache_segment *cache_seg;
227+
u32 seg_id;
228+
229+
spin_lock(&cache->seg_map_lock);
230+
again:
231+
seg_id = find_next_zero_bit(cache->seg_map, cache->n_segs, cache->last_cache_seg);
232+
if (seg_id == cache->n_segs) {
233+
/* reset the hint of ->last_cache_seg and retry */
234+
if (cache->last_cache_seg) {
235+
cache->last_cache_seg = 0;
236+
goto again;
237+
}
238+
cache->cache_full = true;
239+
spin_unlock(&cache->seg_map_lock);
240+
return NULL;
241+
}
242+
243+
/*
244+
* found an available cache_seg, mark it used in seg_map
245+
* and update the search hint ->last_cache_seg
246+
*/
247+
__set_bit(seg_id, cache->seg_map);
248+
cache->last_cache_seg = seg_id;
249+
spin_unlock(&cache->seg_map_lock);
250+
251+
cache_seg = &cache->segments[seg_id];
252+
cache_seg->cache_seg_id = seg_id;
253+
254+
return cache_seg;
255+
}
256+
257+
static void cache_seg_gen_increase(struct pcache_cache_segment *cache_seg)
258+
{
259+
spin_lock(&cache_seg->gen_lock);
260+
cache_seg->gen++;
261+
spin_unlock(&cache_seg->gen_lock);
262+
263+
cache_seg_ctrl_write(cache_seg);
264+
}
265+
266+
void cache_seg_get(struct pcache_cache_segment *cache_seg)
267+
{
268+
atomic_inc(&cache_seg->refs);
269+
}
270+
271+
static void cache_seg_invalidate(struct pcache_cache_segment *cache_seg)
272+
{
273+
struct pcache_cache *cache;
274+
275+
cache = cache_seg->cache;
276+
cache_seg_gen_increase(cache_seg);
277+
278+
spin_lock(&cache->seg_map_lock);
279+
if (cache->cache_full)
280+
cache->cache_full = false;
281+
clear_bit(cache_seg->cache_seg_id, cache->seg_map);
282+
spin_unlock(&cache->seg_map_lock);
283+
284+
pcache_defer_reqs_kick(CACHE_TO_PCACHE(cache));
285+
/* clean_work will clean the bad key in key_tree*/
286+
queue_work(cache_get_wq(cache), &cache->clean_work);
287+
}
288+
289+
void cache_seg_put(struct pcache_cache_segment *cache_seg)
290+
{
291+
if (atomic_dec_and_test(&cache_seg->refs))
292+
cache_seg_invalidate(cache_seg);
293+
}

0 commit comments

Comments
 (0)