代码拉取完成,页面将自动刷新
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) 2021. Huawei Technologies Co., Ltd. All rights reserved.
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
* only version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#ifndef EUFS_PBATCH_H
#define EUFS_PBATCH_H
/**
* To prevent data races, only two cases are allowed:
* 1) nvmalloc -> alloc_batch_persist -> nvfree
* 2) nvmalloc -> nvfree
*/
/**
* eufs_alloc_batch_* API usage:
*
* struct alloc_batch batch;
* [ eufs_alloc_batch_init(&batch, estimated_size); ]
* eufs_alloc_batch_hint(&batch, estimated_size);
* eufs_alloc_batch_add(&batch, the_page_pointer);
* eufs_alloc_batch_add(&batch, the_page_pointer);
* ...
* eufs_alloc_batch_add(&batch, the_page_pointer);
* eufs_alloc_batch_persist_reset(&batch);
*
* eufs_alloc_batch_fini(&batch);
*
*/
/* TODO: consider using list? */
#define EUFS_AB_MAX_SIZE (KMALLOC_MAX_SIZE / 8)
/* log2(cache_line size / page_info_t size) */
#define EUFS_PMAP_CNT_SHIFT_PER_CACHELINE 6
static __always_inline void eufs_alloc_batch_hint(struct alloc_batch *pb,
ssize_t size);
static __always_inline void
eufs_alloc_batch_persist_reset(struct super_block *sb, struct alloc_batch *pb);
static __always_inline void eufs_alloc_batch_init(struct alloc_batch *pb,
ssize_t size)
{
pb->n_used = 0;
pb->batch = NULL;
pb->size = 0;
pb->n_pending = 0;
eufs_alloc_batch_hint(pb, size);
BUG_ON(!pb->batch);
}
/* This gives only hints, no guarantees. */
static __always_inline void eufs_alloc_batch_hint(struct alloc_batch *pb,
ssize_t size)
{
ssize_t realsize;
void **batch;
realsize = round_up(size * sizeof(void *), PAGE_SIZE);
if (realsize > KMALLOC_MAX_SIZE)
realsize = KMALLOC_MAX_SIZE;
size = realsize / sizeof(void *);
if (pb->size >= size)
return;
batch = krealloc(pb->batch, realsize, GFP_KERNEL | __GFP_NOFAIL);
BUG_ON(batch == NULL);
pb->batch = batch;
pb->size = size;
eufs_dbg("! eufs_alloc_batch_hint ; ab=%px size=%ld\n", pb, size);
}
static __always_inline void eufs_alloc_batch_hint_off(struct alloc_batch *pb,
ssize_t off_size)
{
eufs_alloc_batch_hint(pb, pb->size + pb->n_pending + off_size);
}
static __always_inline void eufs_alloc_batch_fini(struct alloc_batch *pb)
{
kfree(pb->batch);
pb->batch = NULL;
pb->size = pb->n_used = 0;
}
/* Add an already allocated address */
static __always_inline void eufs_alloc_batch_add(struct super_block *sb,
struct alloc_batch *pb,
void *page)
{
if (pb->n_used == pb->size) {
/* Enlarge */
if (pb->size == EUFS_AB_MAX_SIZE)
eufs_alloc_batch_persist_reset(sb, pb);
else
eufs_alloc_batch_hint(pb, pb->size * 2);
BUG_ON(pb->n_used >= pb->size);
}
BUG_ON(pb->n_used >= pb->size);
pb->batch[pb->n_used] = page;
pb->n_used++;
}
/*
* With the following four functions, alloc_batch can be used as a pool of
* preallocation.
*/
static __always_inline int
eufs_alloc_batch_pre_allocate_begin(struct super_block *sb,
struct alloc_batch *ab, size_t need_blocks)
{
long r;
BUG_ON(ab->n_pending);
eufs_alloc_batch_hint_off(ab, need_blocks);
ab->n_pending = need_blocks;
r = nvmalloc_pre(sb, ab, need_blocks, PAGE_SIZE);
if (r)
ab->n_pending = 0;
return r;
}
static __always_inline void
eufs_alloc_batch_pre_allocate_end(struct super_block *sb,
struct alloc_batch *ab)
{
WARN((ab->n_pending != 0),
"Some pre-allocated pages are not used in %px!\n", ab);
BUG_ON(!list_empty(&ab->list));
}
/* Allocate from the pre-allocated addresses */
static __always_inline void *eufs_alloc_batch_allocate(struct super_block *sb,
struct alloc_batch *ab,
u8 tag)
{
void *page = NULL;
/* used up */
BUG_ON(ab->n_pending <= 0);
page = nvmalloc_pre_get_from_list(sb, &ab->list, tag);
BUG_ON(!page);
ab->n_pending--;
eufs_alloc_batch_add(sb, ab, page);
return page;
}
static __always_inline void *
eufs_alloc_batch_allocate_file_index(struct super_block *sb,
struct alloc_batch *ab)
{
return eufs_alloc_batch_allocate(sb, ab, EUFS_PAGE_FILE_INDEX);
}
static __always_inline void *
eufs_alloc_batch_allocate_file_data(struct super_block *sb,
struct alloc_batch *ab)
{
return eufs_alloc_batch_allocate(sb, ab, EUFS_PAGE_FILE_DATA);
}
static int cmp_func(const void *a, const void *b)
{
const void **_a = (const void **)a;
const void **_b = (const void **)b;
if (*_a > *_b)
return 1;
if (*_a < *_b)
return -1;
return 0;
}
#define _PAGE_NO(ptr) (((u64)ptr - (u64)sbi->data_start) / PAGE_SIZE)
#define _LINE_MAP(addr) ((line_info_t *)((u64)(addr)&PAGE_MASK))
#define _IS_LINE(addr) ((u64)addr % PAGE_SIZE)
static __always_inline void _set_bitmap(struct eufs_sb_info *sbi, u64 addr,
bool forced)
{
u64 page_no = _PAGE_NO(addr);
u64 rem = addr % PAGE_SIZE;
line_info_t __pmem *line_map;
/* no one can free this address now, so no race will happen */
struct ptr_list_node *node;
int line_no;
if (rem == 0) {
/* page */
node = sbi->cached_nodes + (page_no);
if (!forced) {
BUG_ON(node->solid);
BUG_ON(sbi->page_map[page_no] != EUFS_PAGE_FREE);
}
WARN(node->tag == 0,
"unexpected page node tag %u (addr 0x%llx)\n", node->tag,
addr);
sbi->page_map[page_no] = node->tag;
node->solid = true;
} else {
/* line */
BUG_ON(rem % CACHELINE_SIZE != 0);
line_map = (void *)(addr - rem);
line_no = rem / CACHELINE_SIZE;
BUG_ON(sbi->page_map[page_no] != EUFS_PAGE_FREE &&
sbi->page_map[page_no] != EUFS_PAGE_LINE_USED);
/* \ _set _unset
* _set idempotent
* _unset
*/
if (sbi->page_map[page_no] == EUFS_PAGE_FREE) {
/* idempotent */
sbi->page_map[page_no] = EUFS_PAGE_LINE_USED;
node = sbi->cached_nodes + (page_no);
BUG_ON(!node->busy);
node->solid = true;
}
node = &sbi->line_node_ptrs[page_no][line_no];
if (!forced) {
BUG_ON(node->solid);
if (line_map[line_no]) {
eufs_info(
"!line_map[line_no] = %px[%d] = %d\n",
line_map, line_no, line_map[line_no]);
BUG();
}
BUG_ON(line_map[line_no]);
}
WARN(node->tag == 0,
"unexpected line node tag %u (addr 0x%llx)\n", node->tag,
addr);
line_map[line_no] = node->tag;
eufs_dbg("set %px[%d] = %d forced=%d\n", line_map, line_no,
line_map[line_no], forced);
node->solid = true;
BUG_ON(!node->busy);
}
}
static __always_inline void
eufs_alloc_batch_persist_reset(struct super_block *sb, struct alloc_batch *pb)
{
struct eufs_sb_info *sbi = EUFS_SB(sb);
u64 page_no, page_no0;
int i;
if (pb->n_used == 0)
goto reset;
if (pb->size == 0)
goto reset;
BUG_ON(!pb->batch);
sort(pb->batch, pb->n_used, sizeof(void *), cmp_func, NULL);
for (i = 0; i < pb->n_used; ++i) {
if (i > 0 && pb->batch[i] == pb->batch[i - 1]) {
pr_info("!pb->batch[i]=%px [i-1]=%px i=%d\n",
pb->batch[i], pb->batch[i - 1], i);
BUG();
}
_set_bitmap(sbi, (u64)pb->batch[i], false);
}
page_no0 = _PAGE_NO(pb->batch[0]);
if (_IS_LINE(pb->batch[0]))
eufs_flush_cacheline(_LINE_MAP(pb->batch[0]));
eufs_flush_cacheline(&sbi->page_map[page_no0]);
for (i = 1; i < pb->n_used; ++i) {
page_no = _PAGE_NO(pb->batch[i]);
if (page_no == page_no0)
/* same page, must be allocation of two cache lines */
continue;
/* different page */
if (_IS_LINE(pb->batch[i]))
eufs_flush_cacheline(_LINE_MAP(pb->batch[i]));
/* not in a single cache line */
if ((page_no >> EUFS_PMAP_CNT_SHIFT_PER_CACHELINE) !=
(page_no0 >> EUFS_PMAP_CNT_SHIFT_PER_CACHELINE))
eufs_flush_cacheline(&sbi->page_map[page_no]);
page_no0 = page_no;
}
eufs_dbg("!persistallocation: pb=%px sorted %px~%px %ld\n", pb,
pb->batch[0], pb->batch[pb->n_used - 1], pb->n_used);
reset:
pb->n_used = 0;
}
static __always_inline void eufs_alloc_persist(struct super_block *sb,
void *ptr, bool forced)
{
struct eufs_sb_info *sbi = EUFS_SB(sb);
u64 page_no = _PAGE_NO(ptr);
_set_bitmap(sbi, (u64)ptr, forced);
if (_IS_LINE(ptr))
eufs_flush_cacheline(_LINE_MAP(ptr));
eufs_flush_cacheline(&sbi->page_map[page_no]);
}
#undef _PAGE_NO
#undef _LINE_MAP
#undef _IS_LINE
#endif /* EUFS_PBATCH_H */
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。