new file mode 100644
@@ -0,0 +1,175 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+#include <linux/dax.h>
+
+#include "pcache_internal.h"
+#include "cache_dev.h"
+#include "cache.h"
+#include "backing_dev.h"
+#include "meta_segment.h"
+#include "segment.h"
+
+int segment_pos_advance(struct pcache_segment_pos *seg_pos, u32 len)
+{
+ u32 to_advance;
+
+ while (len) {
+ to_advance = len;
+
+ if (to_advance > seg_pos->segment->data_size - seg_pos->off)
+ to_advance = seg_pos->segment->data_size - seg_pos->off;
+
+ seg_pos->off += to_advance;
+
+ len -= to_advance;
+ }
+
+ return 0;
+}
+
+int segment_copy_to_bio(struct pcache_segment *segment,
+ u32 data_off, u32 data_len, struct bio *bio, u32 bio_off)
+{
+ struct bio_vec bv;
+ struct bvec_iter iter;
+ void *dst;
+ u32 to_copy, page_off = 0;
+ struct pcache_segment_pos pos = { .segment = segment,
+ .off = data_off };
+next:
+ bio_for_each_segment(bv, bio, iter) {
+ if (bio_off > bv.bv_len) {
+ bio_off -= bv.bv_len;
+ continue;
+ }
+ page_off = bv.bv_offset;
+ page_off += bio_off;
+ bio_off = 0;
+
+ dst = kmap_local_page(bv.bv_page);
+again:
+ segment = pos.segment;
+
+ to_copy = min(bv.bv_offset + bv.bv_len - page_off,
+ segment->data_size - pos.off);
+ if (to_copy > data_len)
+ to_copy = data_len;
+
+ flush_dcache_page(bv.bv_page);
+ memcpy(dst + page_off, segment->data + pos.off, to_copy);
+
+ /* advance */
+ pos.off += to_copy;
+ page_off += to_copy;
+ data_len -= to_copy;
+ if (!data_len) {
+ kunmap_local(dst);
+ return 0;
+ }
+
+ /* more data in this bv page */
+ if (page_off < bv.bv_offset + bv.bv_len)
+ goto again;
+ kunmap_local(dst);
+ }
+
+ if (bio->bi_next) {
+ bio = bio->bi_next;
+ goto next;
+ }
+
+ return 0;
+}
+
+void segment_copy_from_bio(struct pcache_segment *segment,
+ u32 data_off, u32 data_len, struct bio *bio, u32 bio_off)
+{
+ struct bio_vec bv;
+ struct bvec_iter iter;
+ void *src;
+ u32 to_copy, page_off = 0;
+ struct pcache_segment_pos pos = { .segment = segment,
+ .off = data_off };
+next:
+ bio_for_each_segment(bv, bio, iter) {
+ if (bio_off > bv.bv_len) {
+ bio_off -= bv.bv_len;
+ continue;
+ }
+ page_off = bv.bv_offset;
+ page_off += bio_off;
+ bio_off = 0;
+
+ src = kmap_local_page(bv.bv_page);
+again:
+ segment = pos.segment;
+
+ to_copy = min(bv.bv_offset + bv.bv_len - page_off,
+ segment->data_size - pos.off);
+ if (to_copy > data_len)
+ to_copy = data_len;
+
+ memcpy_flushcache(segment->data + pos.off, src + page_off, to_copy);
+ flush_dcache_page(bv.bv_page);
+
+ /* advance */
+ pos.off += to_copy;
+ page_off += to_copy;
+ data_len -= to_copy;
+ if (!data_len) {
+ kunmap_local(src);
+ return;
+ }
+
+ /* more data in this bv page */
+ if (page_off < bv.bv_offset + bv.bv_len)
+ goto again;
+ kunmap_local(src);
+ }
+
+ if (bio->bi_next) {
+ bio = bio->bi_next;
+ goto next;
+ }
+}
+
+int pcache_segment_init(struct pcache_cache_dev *cache_dev, struct pcache_segment *segment,
+ struct pcache_segment_init_options *options)
+{
+ segment->seg_info = options->seg_info;
+
+ segment->seg_info->type = options->type;
+ segment->seg_info->state = options->state;
+ segment->seg_info->seg_id = options->seg_id;
+ segment->seg_info->data_off = options->data_off;
+
+ segment->cache_dev = cache_dev;
+ segment->data_size = PCACHE_SEG_SIZE - options->data_off;
+ segment->data = CACHE_DEV_SEGMENT(cache_dev, options->seg_id) + options->data_off;
+
+ return 0;
+}
+
+void pcache_segment_info_write(struct pcache_cache_dev *cache_dev, struct pcache_segment_info *seg_info, u32 seg_id)
+{
+ struct pcache_segment_info *seg_info_addr;
+
+ seg_info->header.seq++;
+
+ seg_info_addr = CACHE_DEV_SEGMENT(cache_dev, seg_id);
+ seg_info_addr = pcache_meta_find_oldest(&seg_info_addr->header, PCACHE_SEG_INFO_SIZE);
+
+ memcpy(seg_info_addr, seg_info, sizeof(struct pcache_segment_info));
+
+ seg_info_addr->header.crc = pcache_meta_crc(&seg_info_addr->header, PCACHE_SEG_INFO_SIZE);
+ cache_dev_flush(cache_dev, seg_info_addr, PCACHE_SEG_INFO_SIZE);
+
+}
+
+struct pcache_segment_info *pcache_segment_info_read(struct pcache_cache_dev *cache_dev, u32 seg_id)
+{
+ struct pcache_segment_info *seg_info_addr;
+
+ seg_info_addr = CACHE_DEV_SEGMENT(cache_dev, seg_id);
+
+ return pcache_meta_find_latest(&seg_info_addr->header, PCACHE_SEG_INFO_SIZE);
+}
new file mode 100644
@@ -0,0 +1,78 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+#ifndef _PCACHE_SEGMENT_H
+#define _PCACHE_SEGMENT_H
+
+#include <linux/bio.h>
+
+#include "pcache_internal.h"
+
+#define segment_err(segment, fmt, ...) \
+ cache_dev_err(segment->cache_dev, "segment%d: " fmt, \
+ segment->seg_id, ##__VA_ARGS__)
+#define segment_info(segment, fmt, ...) \
+ cache_dev_info(segment->cache_dev, "segment%d: " fmt, \
+ segment->seg_id, ##__VA_ARGS__)
+#define segment_debug(segment, fmt, ...) \
+ cache_dev_debug(segment->cache_dev, "segment%d: " fmt, \
+ segment->seg_id, ##__VA_ARGS__)
+
+
+#define PCACHE_SEGMENT_STATE_NONE 0
+#define PCACHE_SEGMENT_STATE_RUNNING 1
+
+#define PCACHES_TYPE_NONE 0
+#define PCACHES_TYPE_META 1
+#define PCACHE_SEGMENT_TYPE_DATA 2
+
+struct pcache_segment_info {
+ struct pcache_meta_header header; /* Metadata header for the segment */
+ u8 type;
+ u8 state;
+ u16 flags;
+ u32 next_seg;
+ u32 seg_id;
+ u32 data_off;
+};
+
+#define PCACHE_SEG_INFO_FLAGS_HAS_NEXT (1 << 0)
+
+static inline bool segment_info_has_next(struct pcache_segment_info *seg_info)
+{
+ return (seg_info->flags & PCACHE_SEG_INFO_FLAGS_HAS_NEXT);
+}
+
+struct pcache_segment_pos {
+ struct pcache_segment *segment; /* Segment associated with the position */
+ u32 off; /* Offset within the segment */
+};
+
+struct pcache_segment_init_options {
+ u8 type;
+ u8 state;
+ u32 seg_id;
+ u32 data_off;
+
+ struct pcache_segment_info *seg_info;
+};
+
+struct pcache_segment {
+ struct pcache_cache_dev *cache_dev;
+
+ void *data;
+ u32 data_size;
+
+ struct pcache_segment_info *seg_info;
+};
+
+int segment_copy_to_bio(struct pcache_segment *segment,
+ u32 data_off, u32 data_len, struct bio *bio, u32 bio_off);
+void segment_copy_from_bio(struct pcache_segment *segment,
+ u32 data_off, u32 data_len, struct bio *bio, u32 bio_off);
+int segment_pos_advance(struct pcache_segment_pos *seg_pos, u32 len);
+int pcache_segment_init(struct pcache_cache_dev *cache_dev, struct pcache_segment *segment,
+ struct pcache_segment_init_options *options);
+
+void pcache_segment_info_write(struct pcache_cache_dev *cache_dev, struct pcache_segment_info *seg_info, u32 seg_id);
+struct pcache_segment_info *pcache_segment_info_read(struct pcache_cache_dev *cache_dev, u32 set_id);
+
+#endif /* _PCACHE_SEGMENT_H */
pcache: introduce segment abstraction and metadata support This patch introduces the basic infrastructure for managing segments in the pcache system. A "segment" is the minimum unit of allocation and persistence on the persistent memory used as cache. Key features introduced: - `struct pcache_segment` and associated helpers for managing segment data. - Metadata handling for segments via `struct pcache_segment_info`, including type, state, data offset, and next-segment pointer. - Support for reading and writing segment metadata with on-media consistency using `pcache_meta_find_latest()` and `pcache_meta_find_oldest()` helpers. - Abstractions for copying data to and from segments and bio vectors, including: - `segment_copy_to_bio()` - `segment_copy_from_bio()` - Logical cursor `segment_pos_advance()` for iterating over data inside a segment. Segment metadata is stored inline in each segment and versioned with CRC to ensure integrity and crash safety. The segment design also lays the foundation for segment chaining via `next_seg`, which will be used in cache_segment and other higher level structures. This patch is part of the core segment layer and will be utilized by metadata and data layers such as meta_segment and cache_segment in subsequent patches. Signed-off-by: Dongsheng Yang <dongsheng.yang@linux.dev> --- drivers/block/pcache/segment.c | 175 +++++++++++++++++++++++++++++++++ drivers/block/pcache/segment.h | 78 +++++++++++++++ 2 files changed, 253 insertions(+) create mode 100644 drivers/block/pcache/segment.c create mode 100644 drivers/block/pcache/segment.h