mirror of
https://github.com/apache/nuttx.git
synced 2026-10-06 13:55:15 +00:00
fs/mnemofs: Add LRU and CTZ methods
LRU methods and CTZ methods to mnemofs Signed-off-by: Saurav Pal <resyfer.dev@gmail.com>
This commit is contained in:
parent
211e2cd8b9
commit
9ef9788b6f
7 changed files with 1355 additions and 19 deletions
|
|
@ -50,6 +50,7 @@ if(CONFIG_FS_MNEMOFS)
|
|||
target_sources(
|
||||
fs
|
||||
PRIVATE mnemofs_blkalloc.c
|
||||
mnemofs_ctz.c
|
||||
mnemofs_fsobj.c
|
||||
mnemofs_journal.c
|
||||
mnemofs_lru.c
|
||||
|
|
|
|||
|
|
@ -53,6 +53,7 @@ ifeq ($(CONFIG_FS_MNEMOFS),y)
|
|||
# Add the mnemofs C files to the build
|
||||
|
||||
CSRCS += mnemofs_blkalloc.c
|
||||
CSRCS += mnemofs_ctz.c
|
||||
CSRCS += mnemofs_fsobj.c
|
||||
CSRCS += mnemofs_journal.c
|
||||
CSRCS += mnemofs_lru.c
|
||||
|
|
|
|||
|
|
@ -677,6 +677,28 @@ int mfs_isbadblk(FAR const struct mfs_sb_s * const sb, mfs_t blk);
|
|||
|
||||
int mfs_markbadblk(FAR const struct mfs_sb_s * const sb, mfs_t blk);
|
||||
|
||||
/****************************************************************************
|
||||
* Name: mfs_write_page
|
||||
*
|
||||
* Description:
|
||||
* Write a page.
|
||||
*
|
||||
* Input Parameters:
|
||||
* sb - Superblock instance of the device.
|
||||
* data - Buffer
|
||||
* datalen - Length of buffer.
|
||||
* pg - Page number.
|
||||
* pgoff - Offset into the page.
|
||||
*
|
||||
* Assumptions/Limitations:
|
||||
* This assumes a locked environment when called.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
ssize_t mfs_write_page(FAR const struct mfs_sb_s * const sb,
|
||||
FAR const char *data, const mfs_t datalen,
|
||||
const off_t page, const mfs_t pgoff);
|
||||
|
||||
/****************************************************************************
|
||||
* Name: mfs_read_page
|
||||
*
|
||||
|
|
@ -998,6 +1020,103 @@ mfs_t mfs_v2n(mfs_t n);
|
|||
|
||||
mfs_t mfs_set_msb(mfs_t n);
|
||||
|
||||
/* mnemofs_ctz.c */
|
||||
|
||||
/****************************************************************************
|
||||
* Name: mfs_ctz_rdfromoff
|
||||
*
|
||||
* Description:
|
||||
* Read data from data offset in a CTZ list. This includes updates from the
|
||||
* journal. The ctz list is taken as the last element in the path, got
|
||||
* using `path[depth - 1]`.
|
||||
*
|
||||
* Input Parameters:
|
||||
* sb - Superblock instance of the device.
|
||||
* data_off - Data offset into the CTZ list.
|
||||
* path - CTZ representation of the relpath.
|
||||
* depth - Depth of the path.
|
||||
* buf - Buffer will be populated with the contents.
|
||||
* buflen - Length of `buf`.
|
||||
*
|
||||
* Returned Value:
|
||||
* 0 - OK
|
||||
* < 0 - Error
|
||||
*
|
||||
* Assumptions/Limitations:
|
||||
* This updates the value of path to reflect the latest location.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
int mfs_ctz_rdfromoff(FAR struct mfs_sb_s * const sb, mfs_t data_off,
|
||||
FAR struct mfs_path_s * const path, const mfs_t depth,
|
||||
FAR char *buf, mfs_t buflen);
|
||||
|
||||
/****************************************************************************
|
||||
* Name: mfs_ctz_wrtooff
|
||||
*
|
||||
* Description:
|
||||
* Replace `o_bytes` of data from CTZ list with `n_bytes` of data from
|
||||
* `buf` at CTZ data offset `data_off`.
|
||||
*
|
||||
* In mnemofs, the CTZ lists are all stored in a Copy On Write manner.
|
||||
* Hence to update a CTZ list, the common CTZ blocks will be kept as it is,
|
||||
* then in the CTZ block containing `data_off`, the bytes appearing before
|
||||
* `data_off` (which remain unchanged) will be copied to the new CTZ block
|
||||
* then `n_bytes` of content from `buf` will follow, and then the data from
|
||||
* `data_off + o_bytes` will follow (both these will be copied to new
|
||||
* CTZ blocks as well due to Copy On Write).
|
||||
*
|
||||
* The new location will be written to the journal upon success as well.
|
||||
*
|
||||
* Input Parameters:
|
||||
* sb - Superblock instance of the device.
|
||||
* data_off - Data offset into the CTZ list.
|
||||
* o_bytes - Number of bytes in old CTZ list from `data_off` that will be
|
||||
* replaced.
|
||||
* n_bytes - Number of bytes in new CTZ list from `data_off` that will be
|
||||
* replacing `o_bytes`.
|
||||
* o_ctz_sz - The size in bytes of the old CTZ list.
|
||||
* path - CTZ representation of the relpath.
|
||||
* depth - Depth of the path.
|
||||
* buf - Buffer that contains the data to be replaced in CTZ list.
|
||||
* ctz - CTZ list to be updated with the new position.
|
||||
*
|
||||
* Returned Value:
|
||||
* 0 - OK
|
||||
* < 0 - Error
|
||||
*
|
||||
* Assumptions/Limitations:
|
||||
* This updates the value of path to reflect the latest location.s
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
int mfs_ctz_wrtooff(FAR struct mfs_sb_s * const sb, const mfs_t data_off,
|
||||
mfs_t o_bytes, const mfs_t n_bytes,
|
||||
mfs_t o_ctz_sz, FAR struct mfs_path_s * const path,
|
||||
const mfs_t depth, FAR const char *buf,
|
||||
FAR struct mfs_ctz_s *ctz);
|
||||
|
||||
/****************************************************************************
|
||||
* Name: mfs_ctz_nwrtooff
|
||||
*
|
||||
* Description:
|
||||
* Write deltas of an LRU node to flash.
|
||||
*
|
||||
* Input Parameters:
|
||||
* sb - Superblock instance of the device.
|
||||
* node - LRU Node.
|
||||
* path - CTZ representation of the relpath.
|
||||
* depth - Depth of path.
|
||||
* ctz_sz - Number of bytes in the data of the CTZ list.
|
||||
* new_ctz - New CTZ location
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
int mfs_ctz_nwrtooff(FAR struct mfs_sb_s * const sb,
|
||||
FAR struct mfs_node_s *node,
|
||||
FAR struct mfs_path_s * const path, const mfs_t depth,
|
||||
const mfs_t ctz_sz, FAR struct mfs_ctz_s *new_ctz);
|
||||
|
||||
/* mnemofs_lru.c */
|
||||
|
||||
/****************************************************************************
|
||||
|
|
|
|||
766
fs/mnemofs/mnemofs_ctz.c
Normal file
766
fs/mnemofs/mnemofs_ctz.c
Normal file
|
|
@ -0,0 +1,766 @@
|
|||
/****************************************************************************
|
||||
* fs/mnemofs/mnemofs_ctz.c
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership. The
|
||||
* ASF licenses this file to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the
|
||||
* License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* the BSD-3-Clause license:
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*
|
||||
* Copyright (c) 2024 Saurav Pal
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of the author nor the names of its contributors may
|
||||
* be used to endorse or promote products derived from this software
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* In mnemofs, the files and directories use the CTZ skip list data structure
|
||||
* defined by littlefs. These are reverse skip lists with a specific number
|
||||
* of pointers for each block. The number of pointers for a block at index
|
||||
* `x` is `ctz(x) + 1`. There are no pointers if the index is 0.
|
||||
*
|
||||
* The pointers all point to some CTZ block other than the CTZ block they are
|
||||
* part of. The `k`th pointer of a CTZ block at index `x` points to the
|
||||
* CTZ block at index `x - 2^k`.
|
||||
*
|
||||
* For example, CTZ block at index 2 has 2 pointers, and they point to the
|
||||
* block at index 1, and index 0 respectively.
|
||||
*
|
||||
* File/Dir Ptr
|
||||
* |
|
||||
* V
|
||||
* +------+ +------+ +------+ +------+ +------+ +------+
|
||||
* | |<--| |---| |---| |---| | | |
|
||||
* | Node |<--| Node |---| Node |<--| Node |---| Node | | Node |
|
||||
* | 0 |<--| 1 |<--| 2 |<--| 3 |<--| 4 |<--| 5 |
|
||||
* +------+ +------+ +------+ +------+ +------+ +------+
|
||||
*
|
||||
* In mnemofs, each CTZ block is stored in a page on the flash. All code in
|
||||
* this entire file will call CTZ blocks as blocks to honour the original
|
||||
* naming, and will specify wherever it deviates from this assumption.
|
||||
*
|
||||
* Littlefs's design documentation lists all the benefits that this data
|
||||
* structure brings to the table when it comes to storing large pieces of
|
||||
* data that will be modified considerably frequently, while being in a
|
||||
* Copy On Write (CoW) environment.
|
||||
*
|
||||
* In mnemofs, the CTZ methods only interface with the underlying R/W methods
|
||||
* , journal on the lower side and on the upper side, the LRU, and ensures
|
||||
* that whatever data it provides considers both the on-flash data, as well
|
||||
* the journal logs.
|
||||
*
|
||||
* The pointers are stored such that the first pointer, which points to
|
||||
* (x - 2^0), is stored at the very end of the CTZ block. The second pointer
|
||||
* is stored second last, and so on.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* Included Files
|
||||
****************************************************************************/
|
||||
|
||||
#include <debug.h>
|
||||
#include <fcntl.h>
|
||||
#include <nuttx/kmalloc.h>
|
||||
#include <math.h>
|
||||
#include <sys/param.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include "mnemofs.h"
|
||||
|
||||
/****************************************************************************
|
||||
* Pre-processor Definitions
|
||||
****************************************************************************/
|
||||
|
||||
#define MFS_CTZ_PTRSZ (sizeof(mfs_t))
|
||||
|
||||
/****************************************************************************
|
||||
* Private Types
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* Private Function Prototypes
|
||||
****************************************************************************/
|
||||
|
||||
static mfs_t ctz_idx_nptrs(const mfs_t idx);
|
||||
static void ctz_off2loc(FAR const struct mfs_sb_s * const sb, mfs_t off,
|
||||
FAR mfs_t *idx, FAR mfs_t *pgoff);
|
||||
static mfs_t ctz_blkdatasz(FAR const struct mfs_sb_s * const sb,
|
||||
const mfs_t idx);
|
||||
static mfs_t ctz_travel(FAR const struct mfs_sb_s * const sb, mfs_t idx_src,
|
||||
mfs_t pg_src, mfs_t idx_dest);
|
||||
static void ctz_copyidxptrs(FAR const struct mfs_sb_s * const sb,
|
||||
FAR struct mfs_ctz_s ctz, const mfs_t idx,
|
||||
FAR char *buf);
|
||||
|
||||
/****************************************************************************
|
||||
* Private Data
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* Public Data
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* Private Functions
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* Name: ctz_idx_nptrs
|
||||
*
|
||||
* Description:
|
||||
* Gives the numbers of pointers that a CTZ block of given index should
|
||||
* have.
|
||||
*
|
||||
* Input Parameters:
|
||||
* idx - Index of the ctz block.
|
||||
*
|
||||
* Returned Value:
|
||||
* The number of pointers in the CTZ block.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
static mfs_t ctz_idx_nptrs(const mfs_t idx)
|
||||
{
|
||||
return idx == 0 ? 0 : mfs_ctz(idx) + 1;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: ctz_off2loc
|
||||
*
|
||||
* Description:
|
||||
* Converts ctz offset (which is the offset of the data stored in the ctz
|
||||
* list, which is unaware of the presence of pointers) into the CTZ
|
||||
* block index and the offset in that CTZ block.
|
||||
*
|
||||
* Input Parameters:
|
||||
* sb - Superblock instance of the device.
|
||||
* off - Offset of the data stored in the CTZ list.
|
||||
* idx - Indes of the CTZ block, to be populated.
|
||||
* pgoff - Offset inside the CTZ block, to be populated.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
static void ctz_off2loc(FAR const struct mfs_sb_s * const sb, mfs_t off,
|
||||
FAR mfs_t *idx, FAR mfs_t *pgoff)
|
||||
{
|
||||
const mfs_t den = MFS_PGSZ(sb) - 8;
|
||||
if (off < MFS_PGSZ(sb))
|
||||
{
|
||||
*idx = 0;
|
||||
*pgoff = off;
|
||||
return;
|
||||
}
|
||||
|
||||
*idx = floor((off - 4 * (__builtin_popcount((off / den) - 1) + 2))
|
||||
/ den);
|
||||
*pgoff = off - den * (*idx) - 4 * __builtin_popcount(*idx);
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: ctz_blkdatasz
|
||||
*
|
||||
* Description:
|
||||
* The size of data in B that can be fit inside a CTZ block at index `idx`.
|
||||
*
|
||||
* Input Parameters:
|
||||
* sb - Superblock instance of the device.
|
||||
* idx - Index of the ctz block.
|
||||
*
|
||||
* Returned Value:
|
||||
* The size of data in the CTZ block.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
static mfs_t ctz_blkdatasz(FAR const struct mfs_sb_s * const sb,
|
||||
const mfs_t idx)
|
||||
{
|
||||
return MFS_PGSZ(sb) - (ctz_idx_nptrs(idx) * MFS_LOGPGSZ(sb));
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: ctz_travel
|
||||
*
|
||||
* Description:
|
||||
* From CTZ block at page `pg_src` and index `idx_src`, give the page
|
||||
* number of index `idx_dest`.
|
||||
*
|
||||
* The source is preferably the last CTZ block in the CTZ list, but it can
|
||||
* realistically be any CTZ block in the CTZ list whos position is known.
|
||||
* However, `idx_dest <= idx_src` has to be followed. Takes O(log(n))
|
||||
* complexity to travel.
|
||||
*
|
||||
* Input Parameters:
|
||||
* sb - Superblock instance of the device.
|
||||
* idx_src - Index of the source ctz block.
|
||||
* pg_src - Page number of the source ctz block.
|
||||
* idx_dest - Index of the destination ctz block.
|
||||
*
|
||||
* Returned Value:
|
||||
* The page number corresponding to `idx_dest`.
|
||||
*
|
||||
* Assumptions/Limitations:
|
||||
* `idx_dest <= idx_src`.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
static mfs_t ctz_travel(FAR const struct mfs_sb_s * const sb, mfs_t idx_src,
|
||||
mfs_t pg_src, mfs_t idx_dest)
|
||||
{
|
||||
char buf[4];
|
||||
mfs_t pg;
|
||||
mfs_t idx;
|
||||
mfs_t pow;
|
||||
mfs_t diff;
|
||||
mfs_t max_pow;
|
||||
|
||||
/* Rising phase. */
|
||||
|
||||
max_pow = (sizeof(mfs_t) * 8) - mfs_clz(idx_src ^ idx_dest);
|
||||
idx = idx_src;
|
||||
pow = 1;
|
||||
pg = pg_src;
|
||||
|
||||
for (pow = mfs_ctz(idx); pow < max_pow - 1; pow = mfs_ctz(idx))
|
||||
{
|
||||
mfs_read_page(sb, buf, 4, pg, MFS_PGSZ(sb) - (4 * pow));
|
||||
mfs_deser_mfs(buf, &pg);
|
||||
idx -= (1 << pow);
|
||||
}
|
||||
|
||||
if (idx == idx_dest)
|
||||
{
|
||||
return pg;
|
||||
}
|
||||
|
||||
/* Falling phase. */
|
||||
|
||||
diff = idx - idx_dest;
|
||||
|
||||
for (pow = mfs_set_msb(diff); diff != 0; pow = mfs_set_msb(diff))
|
||||
{
|
||||
mfs_read_page(sb, buf, 4, pg, MFS_PGSZ(sb) - (4 * pow));
|
||||
mfs_deser_mfs(buf, &pg);
|
||||
idx -= (1 << pow);
|
||||
diff -= (1 << pow);
|
||||
}
|
||||
|
||||
return pg;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: ctz_copyidxptrs
|
||||
*
|
||||
* Description:
|
||||
* This is used for cases when you want to expand a CTZ list from any point
|
||||
* in the list. If we want to expand the CTZ list from a particular index,
|
||||
* say `start_idx`, while keeping all indexes before it untouched, we
|
||||
* would need to first allocate new blocks on the flash, and then copy
|
||||
* the pointers to the location.
|
||||
*
|
||||
* Usage of this function is, the caller needs to first allocate a CTZ
|
||||
* block (a page on flash), allocate buffer which is the size of a CTZ
|
||||
* block (a page on flash), and use this method to copy the pointers to the
|
||||
* buffer, then write the data to the flash.
|
||||
*
|
||||
* Input Parameters:
|
||||
* sb - Superblock instance of the device.
|
||||
* ctz - CTZ list to use as a reference.
|
||||
* idx - Index of the block who's supposed pointers are to be copied.
|
||||
* buf - Buffer representing the entire CTZ block where pointers are
|
||||
* copied to.
|
||||
*
|
||||
* Assumptions/Limitations:
|
||||
* This assumes `idx` is not more than `ctz->idx_e + 1`.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
static void ctz_copyidxptrs(FAR const struct mfs_sb_s * const sb,
|
||||
FAR struct mfs_ctz_s ctz, const mfs_t idx,
|
||||
FAR char *buf)
|
||||
{
|
||||
mfs_t i;
|
||||
mfs_t n_ptrs;
|
||||
mfs_t prev_pg;
|
||||
mfs_t prev_idx;
|
||||
|
||||
if (idx == 0)
|
||||
{
|
||||
/* No pointers for first block. */
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
n_ptrs = ctz_idx_nptrs(idx);
|
||||
|
||||
if (idx != ctz.idx_e + 1)
|
||||
{
|
||||
ctz.pg_e = ctz_travel(sb, ctz.idx_e, ctz.pg_e, idx - 1);
|
||||
ctz.idx_e = idx - 1;
|
||||
}
|
||||
|
||||
buf += MFS_PGSZ(sb); /* Go to buf + pg_sz */
|
||||
|
||||
for (i = 0; i < n_ptrs; i++)
|
||||
{
|
||||
if (predict_false(i == 0))
|
||||
{
|
||||
prev_idx = ctz.idx_e;
|
||||
prev_pg = ctz.pg_e;
|
||||
}
|
||||
else
|
||||
{
|
||||
prev_pg = ctz_travel(sb, prev_idx, prev_pg, prev_idx - 1);
|
||||
prev_idx--;
|
||||
}
|
||||
|
||||
ctz.idx_e = prev_idx;
|
||||
|
||||
/* Do buf + pg_sz - (idx * sizeof(mfs_t)) iteratively. */
|
||||
|
||||
buf -= MFS_CTZ_PTRSZ;
|
||||
mfs_ser_mfs(prev_pg, buf);
|
||||
}
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Public Functions
|
||||
****************************************************************************/
|
||||
|
||||
int mfs_ctz_rdfromoff(FAR struct mfs_sb_s * const sb, mfs_t data_off,
|
||||
FAR struct mfs_path_s * const path, const mfs_t depth,
|
||||
FAR char *buf, mfs_t buflen)
|
||||
{
|
||||
int ret = OK;
|
||||
mfs_t sz;
|
||||
mfs_t pg;
|
||||
mfs_t idx;
|
||||
mfs_t off;
|
||||
struct mfs_ctz_s ctz;
|
||||
|
||||
/* Get updated location from the journal */
|
||||
|
||||
DEBUGASSERT(depth > 0);
|
||||
ctz = path[depth - 1].ctz;
|
||||
|
||||
ctz_off2loc(sb, data_off, &idx, &off);
|
||||
|
||||
/* TODO: Make the traversal in reverse direction. It would cause
|
||||
* a lot less traversals.
|
||||
*/
|
||||
|
||||
while (idx <= ctz.idx_e && off - data_off < buflen)
|
||||
{
|
||||
sz = ctz_blkdatasz(sb, idx);
|
||||
pg = ctz_travel(sb, ctz.idx_e, ctz.pg_e, idx);
|
||||
|
||||
ret = mfs_read_page(sb, buf, sz, pg, off);
|
||||
if (predict_false(ret < 0))
|
||||
{
|
||||
return ret;
|
||||
}
|
||||
|
||||
off = 0;
|
||||
idx++;
|
||||
buf += sz;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int mfs_ctz_nwrtooff(FAR struct mfs_sb_s * const sb,
|
||||
FAR struct mfs_node_s *node,
|
||||
FAR struct mfs_path_s * const path, const mfs_t depth,
|
||||
const mfs_t ctz_sz, FAR struct mfs_ctz_s *new_ctz)
|
||||
{
|
||||
int ret = OK;
|
||||
bool del;
|
||||
mfs_t pg;
|
||||
mfs_t sz;
|
||||
mfs_t inc;
|
||||
mfs_t idx;
|
||||
mfs_t bytes;
|
||||
mfs_t pgoff;
|
||||
mfs_t itr_min;
|
||||
mfs_t itr_max;
|
||||
mfs_t neg_off; /* Negative offset due to delete. */
|
||||
FAR char *buf = NULL;
|
||||
const mfs_t range_min = node->range_min;
|
||||
const mfs_t range_max = node->range_max;
|
||||
struct mfs_ctz_s ctz;
|
||||
FAR struct mfs_delta_s *delta = NULL;
|
||||
|
||||
buf = kmm_zalloc(MFS_PGSZ(sb));
|
||||
if (predict_false(buf == NULL))
|
||||
{
|
||||
ret = -ENOMEM;
|
||||
goto errout;
|
||||
}
|
||||
|
||||
/* CoW of items in block before range_min */
|
||||
|
||||
ctz_off2loc(sb, range_min, &idx, &pgoff);
|
||||
DEBUGASSERT(depth > 0);
|
||||
pg = ctz_travel(sb, path[depth - 1].ctz.idx_e, path[depth - 1].ctz.pg_e,
|
||||
idx);
|
||||
if (predict_false(pg == 0))
|
||||
{
|
||||
ret = -EINVAL;
|
||||
goto errout_with_buf;
|
||||
}
|
||||
|
||||
ctz.idx_e = idx - 1;
|
||||
ctz.pg_e = pg;
|
||||
mfs_read_page(sb, buf, pgoff, pg, pgoff);
|
||||
|
||||
/* Updates */
|
||||
|
||||
bytes = range_min;
|
||||
itr_max = range_min;
|
||||
neg_off = 0;
|
||||
del = false;
|
||||
|
||||
while (bytes < range_max)
|
||||
{
|
||||
if (!del)
|
||||
{
|
||||
memset(buf, 0, MFS_PGSZ(sb));
|
||||
sz = ctz_blkdatasz(sb, ctz.idx_e + 1);
|
||||
itr_min = itr_max;
|
||||
itr_max += sz;
|
||||
ctz_copyidxptrs(sb, ctz, ctz.idx_e + 1, buf);
|
||||
}
|
||||
else
|
||||
{
|
||||
inc = itr_max - itr_min;
|
||||
itr_min = itr_max;
|
||||
itr_min = sz - inc;
|
||||
}
|
||||
|
||||
del = false;
|
||||
mfs_ctz_rdfromoff(sb, itr_min + neg_off, path, depth, buf + pgoff, sz);
|
||||
|
||||
list_for_every_entry(&node->delta, delta, struct mfs_delta_s, list)
|
||||
{
|
||||
if (delta->off + delta->n_b <= itr_min || itr_max <= delta->off)
|
||||
{
|
||||
/* Out of range */
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
inc = MIN(itr_max, delta->off + delta->n_b) - delta->off;
|
||||
|
||||
if (delta->upd != NULL)
|
||||
{
|
||||
/* Update */
|
||||
|
||||
memcpy(buf + pgoff + (delta->off - itr_min), delta->upd, inc);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Delete */
|
||||
|
||||
memmove(buf + pgoff + (delta->off - itr_min),
|
||||
buf + pgoff + (delta->off - itr_min) + inc,
|
||||
itr_max - (delta->off + inc));
|
||||
itr_max -= inc;
|
||||
neg_off += inc;
|
||||
del = true;
|
||||
}
|
||||
}
|
||||
|
||||
bytes += itr_max - itr_min;
|
||||
|
||||
if (!del)
|
||||
{
|
||||
pgoff = 0;
|
||||
pg = mfs_ba_getpg(sb);
|
||||
if (pg == 0)
|
||||
{
|
||||
ret = -ENOSPC;
|
||||
goto errout_with_buf;
|
||||
}
|
||||
|
||||
mfs_write_page(sb, buf, MFS_PGSZ(sb), pg, 0);
|
||||
ctz.pg_e = pg;
|
||||
ctz.idx_e++;
|
||||
}
|
||||
else
|
||||
{
|
||||
pgoff += itr_max - itr_min;
|
||||
}
|
||||
}
|
||||
|
||||
/* Copy rest of the file. */
|
||||
|
||||
if (del)
|
||||
{
|
||||
mfs_ctz_rdfromoff(sb, itr_max + neg_off, path, depth, buf + pgoff,
|
||||
sz - pgoff);
|
||||
pg = mfs_ba_getpg(sb);
|
||||
if (pg == 0)
|
||||
{
|
||||
ret = -ENOSPC;
|
||||
goto errout_with_buf;
|
||||
}
|
||||
|
||||
mfs_write_page(sb, buf, MFS_PGSZ(sb), pg, 0);
|
||||
ctz.pg_e = pg;
|
||||
ctz.idx_e++;
|
||||
itr_max += sz - pgoff;
|
||||
pgoff = 0;
|
||||
}
|
||||
|
||||
while (bytes < ctz_sz)
|
||||
{
|
||||
sz = ctz_blkdatasz(sb, ctz.idx_e + 1);
|
||||
mfs_ctz_rdfromoff(sb, itr_max + neg_off, path, depth, buf,
|
||||
MIN(MFS_PGSZ(sb), ctz_sz - bytes));
|
||||
pg = mfs_ba_getpg(sb);
|
||||
if (pg == 0)
|
||||
{
|
||||
ret = -ENOSPC;
|
||||
goto errout_with_buf;
|
||||
}
|
||||
|
||||
mfs_write_page(sb, buf, MFS_PGSZ(sb), pg, 0);
|
||||
bytes += MIN(MFS_PGSZ(sb), ctz_sz - bytes);
|
||||
}
|
||||
|
||||
/* TODO: Check for cases where delete, but no further data at the end.
|
||||
* , which might cause an infinite loop.
|
||||
*/
|
||||
|
||||
errout_with_buf:
|
||||
kmm_free(buf);
|
||||
|
||||
errout:
|
||||
return ret;
|
||||
}
|
||||
|
||||
int mfs_ctz_wrtooff(FAR struct mfs_sb_s * const sb, const mfs_t data_off,
|
||||
mfs_t o_bytes, const mfs_t n_bytes,
|
||||
mfs_t o_ctz_sz, FAR struct mfs_path_s * const path,
|
||||
const mfs_t depth, FAR const char *buf,
|
||||
FAR struct mfs_ctz_s *ctz)
|
||||
{
|
||||
int ret = OK;
|
||||
bool partial;
|
||||
mfs_t partial_bytes; /* Bytes partially filled. */
|
||||
mfs_t pg;
|
||||
mfs_t off;
|
||||
mfs_t idx;
|
||||
mfs_t bytes;
|
||||
mfs_t o_pg;
|
||||
mfs_t o_off;
|
||||
mfs_t o_idx;
|
||||
mfs_t o_rembytes;
|
||||
mfs_t o_data_off;
|
||||
mfs_t n_pg;
|
||||
mfs_t n_data_off;
|
||||
mfs_t ctz_blk_datasz;
|
||||
FAR char *data;
|
||||
struct mfs_ctz_s o_ctz;
|
||||
struct mfs_ctz_s n_ctz;
|
||||
|
||||
data = kmm_zalloc(MFS_PGSZ(sb));
|
||||
if (predict_false(data == NULL))
|
||||
{
|
||||
ret = -ENOMEM;
|
||||
goto errout;
|
||||
}
|
||||
|
||||
/* Get updated location from the journal. */
|
||||
|
||||
DEBUGASSERT(depth > 0);
|
||||
o_ctz = path[depth - 1].ctz;
|
||||
|
||||
/* TODO: Make the traversal in reverse direction. It would cause
|
||||
* a lot less traversals.
|
||||
*/
|
||||
|
||||
ctz_off2loc(sb, data_off, &idx, &off);
|
||||
|
||||
/* Reach the common part. */
|
||||
|
||||
if (idx != 0 && off != 0)
|
||||
{
|
||||
pg = ctz_travel(sb, o_ctz.idx_e, o_ctz.pg_e, idx - 1);
|
||||
n_ctz.idx_e = idx - 1;
|
||||
n_ctz.pg_e = pg;
|
||||
}
|
||||
else
|
||||
{
|
||||
pg = o_ctz.pg_e;
|
||||
n_ctz.idx_e = 0;
|
||||
n_ctz.pg_e = pg;
|
||||
finfo("CTZ: %u %u %u", pg, n_ctz.idx_e, n_ctz.pg_e);
|
||||
}
|
||||
|
||||
/* Add new data from buf. */
|
||||
|
||||
partial = false;
|
||||
o_rembytes = o_ctz_sz - (data_off + o_bytes);
|
||||
bytes = data_off;
|
||||
if (n_bytes != 0)
|
||||
{
|
||||
while (bytes < data_off + n_bytes)
|
||||
{
|
||||
n_pg = mfs_ba_getpg(sb);
|
||||
if (n_pg)
|
||||
{
|
||||
ret = -ENOSPC;
|
||||
goto errout_with_data;
|
||||
}
|
||||
|
||||
ctz_copyidxptrs(sb, n_ctz, idx, data); /* Handles idx == 0. */
|
||||
ctz_blk_datasz = ctz_blkdatasz(sb, idx);
|
||||
|
||||
if (predict_false(off != 0))
|
||||
{
|
||||
/* This happens at max for the first iteration of the loop. */
|
||||
|
||||
mfs_read_page(sb, data, off,
|
||||
ctz_travel(sb, o_ctz.idx_e, o_ctz.pg_e, idx), 0);
|
||||
bytes += off;
|
||||
}
|
||||
|
||||
memcpy(data + off, buf + bytes,
|
||||
MIN(ctz_blk_datasz - off, n_bytes - bytes));
|
||||
if (n_bytes - bytes < ctz_blk_datasz - off && o_rembytes != 0)
|
||||
{
|
||||
partial = true;
|
||||
partial_bytes = n_bytes - bytes;
|
||||
}
|
||||
else
|
||||
{
|
||||
bytes += ctz_blk_datasz - off;
|
||||
}
|
||||
|
||||
if (predict_false(off != 0))
|
||||
{
|
||||
/* This happens at max for the first iteration of the loop. */
|
||||
|
||||
off = 0;
|
||||
}
|
||||
|
||||
if (predict_true(!partial))
|
||||
{
|
||||
mfs_write_page(sb, data, MFS_PGSZ(sb), n_pg, 0);
|
||||
n_ctz.idx_e = idx;
|
||||
n_ctz.pg_e = pg;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* In this case, we have a situation where there is still
|
||||
* some empty area left in the page, and there is some data
|
||||
* that is waiting to be fit in there, so we won't write it
|
||||
* to the flash JUST yet. We'll fill in the rest of the data
|
||||
* and THEN write it.
|
||||
*
|
||||
* It's okay to leave the loop as this case will happen, if at
|
||||
* all, on the last iteration of the loop.
|
||||
*/
|
||||
|
||||
n_ctz.idx_e = idx;
|
||||
n_ctz.pg_e = pg;
|
||||
break;
|
||||
}
|
||||
|
||||
idx++;
|
||||
|
||||
memset(data, 0, MFS_PGSZ(sb));
|
||||
}
|
||||
}
|
||||
|
||||
o_data_off = data_off + o_bytes;
|
||||
n_data_off = data_off + n_bytes;
|
||||
|
||||
/* Completing partially filled data, if present. */
|
||||
|
||||
if (partial)
|
||||
{
|
||||
ctz_off2loc(sb, o_data_off, &o_idx, &o_off);
|
||||
o_pg = ctz_travel(sb, o_ctz.idx_e, o_ctz.pg_e, o_idx);
|
||||
mfs_read_page(sb, data + partial_bytes,
|
||||
ctz_blkdatasz(sb, n_ctz.idx_e) - partial_bytes, o_pg,
|
||||
o_off);
|
||||
mfs_write_page(sb, data, MFS_PGSZ(sb), n_ctz.pg_e, 0);
|
||||
|
||||
o_data_off += partial_bytes;
|
||||
n_data_off += partial_bytes;
|
||||
partial_bytes = 0;
|
||||
partial = false;
|
||||
}
|
||||
|
||||
/* Adding old bytes in. */
|
||||
|
||||
while (o_data_off < o_ctz_sz)
|
||||
{
|
||||
memset(data, 0, MFS_PGSZ(sb));
|
||||
|
||||
pg = mfs_ba_getpg(sb);
|
||||
if (predict_false(pg == 0))
|
||||
{
|
||||
ret = -ENOSPC;
|
||||
goto errout_with_data;
|
||||
}
|
||||
|
||||
ctz_blk_datasz = ctz_blkdatasz(sb, n_ctz.idx_e + 1);
|
||||
ctz_copyidxptrs(sb, n_ctz, n_ctz.idx_e + 1, data);
|
||||
|
||||
mfs_ctz_rdfromoff(sb, o_data_off, path, depth, data, ctz_blk_datasz);
|
||||
mfs_write_page(sb, data, MFS_PGSZ(sb), pg, 0);
|
||||
|
||||
n_ctz.idx_e++;
|
||||
n_ctz.pg_e = pg;
|
||||
|
||||
o_data_off += ctz_blk_datasz;
|
||||
}
|
||||
|
||||
/* path is not updated to point to the new ctz. This is upto the caller. */
|
||||
|
||||
*ctz = n_ctz;
|
||||
|
||||
errout_with_data:
|
||||
kmm_free(data);
|
||||
|
||||
errout:
|
||||
return ret;
|
||||
}
|
||||
|
|
@ -83,10 +83,26 @@
|
|||
* Private Types
|
||||
****************************************************************************/
|
||||
|
||||
enum
|
||||
{
|
||||
MFS_LRU_UPD,
|
||||
MFS_LRU_DEL,
|
||||
};
|
||||
|
||||
/****************************************************************************
|
||||
* Private Function Prototypes
|
||||
****************************************************************************/
|
||||
|
||||
static void lru_nodesearch(FAR struct mfs_sb_s * const sb,
|
||||
FAR const struct mfs_path_s * const path,
|
||||
const mfs_t depth, FAR struct mfs_node_s **node);
|
||||
static bool lru_islrufull(FAR struct mfs_sb_s * const sb);
|
||||
static bool lru_isnodefull(FAR struct mfs_node_s *node);
|
||||
static int lru_nodeflush(FAR struct mfs_sb_s * const sb,
|
||||
FAR struct mfs_path_s * const path,
|
||||
const mfs_t depth, const mfs_t ctz_sz,
|
||||
FAR struct mfs_node_s *node, bool clean_node);
|
||||
|
||||
/****************************************************************************
|
||||
* Private Data
|
||||
****************************************************************************/
|
||||
|
|
@ -99,54 +115,476 @@
|
|||
* Private Functions
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* Name: lru_nodesearch
|
||||
*
|
||||
* Description:
|
||||
* Searches a node by the `path` and `depth` in the LRU.
|
||||
*
|
||||
* Input Parameters:
|
||||
* sb - Superblock instance of the device.
|
||||
* path - CTZ representation of the relpath.
|
||||
* depth - Depth of `path`.
|
||||
* node - To populate with the node corresponding to the CTZ.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
static void lru_nodesearch(FAR struct mfs_sb_s * const sb,
|
||||
FAR const struct mfs_path_s * const path,
|
||||
const mfs_t depth, FAR struct mfs_node_s **node)
|
||||
{
|
||||
bool found;
|
||||
mfs_t i;
|
||||
FAR struct mfs_node_s *n;
|
||||
|
||||
*node = NULL;
|
||||
list_for_every_entry(&MFS_LRU(sb), n, struct mfs_node_s, list)
|
||||
{
|
||||
if (n == NULL)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
found = true;
|
||||
if (n->depth != depth)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (depth != 0)
|
||||
{
|
||||
for (i = depth - 1; i + 1 > 0; i--) /* i + 1 prevents underflow. */
|
||||
{
|
||||
if (n->path[i].off == path[i].off &&
|
||||
n->path[i].ctz.idx_e == path[i].ctz.idx_e &&
|
||||
n->path[i].ctz.pg_e == path[i].ctz.pg_e)
|
||||
{
|
||||
/* OK */
|
||||
}
|
||||
else
|
||||
{
|
||||
found = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (found)
|
||||
{
|
||||
finfo("Node search ended with match of node %p at depth %u"
|
||||
" for CTZ of %u original size with range [%u, %u).", n,
|
||||
n->depth, n->sz, n->range_min, n->range_max);
|
||||
*node = n;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
*node = NULL;
|
||||
finfo("Node search ended without match.");
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: lru_islrufull
|
||||
*
|
||||
* Description:
|
||||
* Check whether the number of nodes in the LRU has reaches its limit.
|
||||
*
|
||||
* Input Parameters:
|
||||
* sb - Superblock instance of the device.
|
||||
*
|
||||
* Returned Value:
|
||||
* true - LRU is full
|
||||
* false - LRU is not full.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
static bool lru_islrufull(FAR struct mfs_sb_s * const sb)
|
||||
{
|
||||
return sb->n_lru == CONFIG_MNEMOFS_NLRU;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: lru_isnodefull
|
||||
*
|
||||
* Description:
|
||||
* Check whether the number of deltas in an LRU node has reaches its limit.
|
||||
*
|
||||
* Input Parameters:
|
||||
* node - LRU node.
|
||||
*
|
||||
* Returned Value:
|
||||
* true - LRU node is full
|
||||
* false - LRU node is not full.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
static bool lru_isnodefull(FAR struct mfs_node_s *node)
|
||||
{
|
||||
return node->n_list == CONFIG_MNEMOFS_NLRUDELTA;
|
||||
}
|
||||
|
||||
static void lru_free_delta(FAR struct mfs_delta_s *delta)
|
||||
{
|
||||
kmm_free(delta->upd);
|
||||
kmm_free(delta);
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: lru_nodeflush
|
||||
*
|
||||
* Description:
|
||||
* Clear out the deltas in a node by writing them to the flash, and adding
|
||||
* a log about it to the journal.
|
||||
*
|
||||
* Input Parameters:
|
||||
* sb - Superblock instance of the device.
|
||||
* path - CTZ representation of the relpath.
|
||||
* depth - Depth of `path`.
|
||||
* node - LRU node to flush.
|
||||
* clean_node - To remove node out of LRU (true), or just clear the
|
||||
* deltas (false).
|
||||
*
|
||||
* Returned Value:
|
||||
* 0 - OK
|
||||
* < 0 - Error
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
static int lru_nodeflush(FAR struct mfs_sb_s * const sb,
|
||||
FAR struct mfs_path_s * const path,
|
||||
const mfs_t depth, const mfs_t ctz_sz,
|
||||
FAR struct mfs_node_s *node, bool clean_node)
|
||||
{
|
||||
int ret = OK;
|
||||
FAR struct mfs_delta_s *delta = NULL;
|
||||
FAR struct mfs_delta_s *tmp = NULL;
|
||||
|
||||
if (predict_false(node == NULL))
|
||||
{
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
ret = mfs_ctz_nwrtooff(sb, node, path, depth, ctz_sz,
|
||||
&path[depth - 1].ctz);
|
||||
if (predict_false(ret < 0))
|
||||
{
|
||||
goto errout;
|
||||
}
|
||||
|
||||
/* Reset node stats. */
|
||||
|
||||
node->range_max = 0;
|
||||
node->range_min = UINT32_MAX;
|
||||
|
||||
/* Free deltas after flush. */
|
||||
|
||||
list_for_every_entry_safe(&node->list, delta, tmp, struct mfs_delta_s,
|
||||
list)
|
||||
{
|
||||
list_delete_init(&delta->list);
|
||||
lru_free_delta(delta);
|
||||
}
|
||||
|
||||
errout:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int lru_wrtooff(FAR struct mfs_sb_s * const sb,
|
||||
const mfs_t data_off, mfs_t bytes, mfs_t ctz_sz,
|
||||
int op, FAR struct mfs_path_s * const path,
|
||||
const mfs_t depth, FAR const char *buf)
|
||||
{
|
||||
int ret = OK;
|
||||
bool found = true;
|
||||
FAR struct mfs_node_s *node = NULL;
|
||||
FAR struct mfs_node_s *last_node = NULL;
|
||||
FAR struct mfs_delta_s *delta = NULL;
|
||||
|
||||
lru_nodesearch(sb, path, depth, &node);
|
||||
|
||||
if (node == NULL)
|
||||
{
|
||||
node = kmm_zalloc(sizeof(*node) + (depth * sizeof(struct mfs_path_s)));
|
||||
if (predict_false(node == NULL))
|
||||
{
|
||||
ret = -ENOMEM;
|
||||
goto errout;
|
||||
}
|
||||
|
||||
node->sz = ctz_sz;
|
||||
node->depth = depth;
|
||||
node->n_list = 0;
|
||||
node->range_max = 0;
|
||||
node->range_min = UINT32_MAX;
|
||||
list_initialize(&node->delta);
|
||||
memcpy(node->path, path, depth * sizeof(struct mfs_path_s));
|
||||
found = false;
|
||||
|
||||
finfo("Node not found, allocated, ready to be inserted into LRU.");
|
||||
}
|
||||
|
||||
if (!found)
|
||||
{
|
||||
if (lru_islrufull(sb))
|
||||
{
|
||||
finfo("LRU is full, need to flush a node.");
|
||||
last_node = list_container_of(list_peek_tail(&MFS_LRU(sb)),
|
||||
struct mfs_node_s, list);
|
||||
list_delete_init(&last_node->list);
|
||||
list_add_tail(&MFS_LRU(sb), &node->list);
|
||||
finfo("LRU flushing node complete, now only %u nodes", sb->n_lru);
|
||||
}
|
||||
else
|
||||
{
|
||||
list_add_tail(&MFS_LRU(sb), &node->list);
|
||||
sb->n_lru++;
|
||||
finfo("Node inserted into LRU, and it now %u node(s).", sb->n_lru);
|
||||
}
|
||||
}
|
||||
else if (found && lru_isnodefull(node))
|
||||
{
|
||||
/* Node flush writes to the flash and journal. */
|
||||
|
||||
ret = lru_nodeflush(sb, path, depth, ctz_sz, node, false);
|
||||
if (predict_false(ret < 0))
|
||||
{
|
||||
goto errout_with_node;
|
||||
}
|
||||
}
|
||||
|
||||
/* Add delta to node. */
|
||||
|
||||
finfo("Adding delta to the node.");
|
||||
delta = kmm_zalloc(sizeof(*delta));
|
||||
if (predict_false(delta == NULL))
|
||||
{
|
||||
ret = -ENOMEM;
|
||||
goto errout_with_node;
|
||||
}
|
||||
|
||||
finfo("Delta allocated.");
|
||||
|
||||
if (op == MFS_LRU_UPD)
|
||||
{
|
||||
delta->upd = kmm_zalloc(bytes);
|
||||
if (predict_false(delta->upd == NULL))
|
||||
{
|
||||
ret = -ENOMEM;
|
||||
goto errout_with_delta;
|
||||
}
|
||||
|
||||
finfo("Delta is of the update type, has %u bytes of updates." , bytes);
|
||||
}
|
||||
|
||||
delta->n_b = bytes;
|
||||
delta->off = data_off;
|
||||
list_add_tail(&node->delta, &delta->list);
|
||||
if (op == MFS_LRU_UPD)
|
||||
{
|
||||
memcpy(delta->upd, buf, bytes);
|
||||
}
|
||||
|
||||
node->n_list++;
|
||||
node->range_min = MIN(node->range_min, data_off);
|
||||
node->range_max = MAX(node->range_max, data_off + bytes);
|
||||
|
||||
finfo("Delta attached to node. Now there are %lu nodes and the node has"
|
||||
" %lu deltas. Node with range [%u, %u).", list_length(&MFS_LRU(sb)),
|
||||
list_length(&node->delta), node->range_min, node->range_max);
|
||||
|
||||
return ret;
|
||||
|
||||
errout_with_delta:
|
||||
kmm_free(delta);
|
||||
|
||||
errout_with_node:
|
||||
if (!found && node != NULL)
|
||||
{
|
||||
kmm_free(node);
|
||||
}
|
||||
|
||||
errout:
|
||||
return ret;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Public Functions
|
||||
****************************************************************************/
|
||||
|
||||
int mfs_lru_ctzflush(FAR struct mfs_sb_s * const sb,
|
||||
FAR struct mfs_path_s * const path, const mfs_t depth,
|
||||
const mfs_t ctz_sz)
|
||||
FAR struct mfs_path_s * const path, const mfs_t depth,
|
||||
const mfs_t ctz_sz)
|
||||
{
|
||||
/* TODO */
|
||||
struct mfs_node_s *node = NULL;
|
||||
|
||||
return OK;
|
||||
lru_nodesearch(sb, path, depth, &node);
|
||||
if (node == NULL)
|
||||
{
|
||||
return OK;
|
||||
}
|
||||
|
||||
return lru_nodeflush(sb, path, depth, ctz_sz, node, true);
|
||||
}
|
||||
|
||||
int mfs_lru_del(FAR struct mfs_sb_s * const sb, const mfs_t data_off,
|
||||
mfs_t bytes, mfs_t ctz_sz,
|
||||
FAR struct mfs_path_s * const path, const mfs_t depth)
|
||||
{
|
||||
/* TODO */
|
||||
|
||||
return OK;
|
||||
return lru_wrtooff(sb, data_off, bytes, ctz_sz, MFS_LRU_DEL, path, depth,
|
||||
NULL);
|
||||
}
|
||||
|
||||
int mfs_lru_wr(FAR struct mfs_sb_s * const sb, const mfs_t data_off,
|
||||
mfs_t bytes, mfs_t ctz_sz, FAR struct mfs_path_s * const path,
|
||||
const mfs_t depth, FAR const char *buf)
|
||||
mfs_t bytes, mfs_t ctz_sz, FAR struct mfs_path_s * const path,
|
||||
const mfs_t depth, FAR const char *buf)
|
||||
{
|
||||
/* TODO */
|
||||
|
||||
return OK;
|
||||
return lru_wrtooff(sb, data_off, bytes, ctz_sz, MFS_LRU_UPD, path, depth,
|
||||
buf);
|
||||
}
|
||||
|
||||
int mfs_lru_rdfromoff(FAR struct mfs_sb_s * const sb, const mfs_t data_off,
|
||||
FAR struct mfs_path_s * const path, const mfs_t depth,
|
||||
FAR char *buf, const mfs_t buflen)
|
||||
{
|
||||
/* TODO */
|
||||
int ret = OK;
|
||||
mfs_t s;
|
||||
mfs_t e;
|
||||
mfs_t end;
|
||||
mfs_t start;
|
||||
mfs_t del_b;
|
||||
FAR struct mfs_node_s *node = NULL;
|
||||
FAR struct mfs_delta_s *delta = NULL;
|
||||
|
||||
return OK;
|
||||
finfo("Reading from offset %u, %u bytes", data_off, buflen);
|
||||
|
||||
ret = mfs_ctz_rdfromoff(sb, data_off, path, depth, buf, buflen);
|
||||
if (predict_false(ret < 0))
|
||||
{
|
||||
goto errout;
|
||||
}
|
||||
|
||||
lru_nodesearch(sb, path, depth, &node);
|
||||
if (node == NULL)
|
||||
{
|
||||
goto errout;
|
||||
}
|
||||
|
||||
start = data_off;
|
||||
end = data_off + buflen;
|
||||
del_b = 0;
|
||||
|
||||
do
|
||||
{
|
||||
list_for_every_entry(&node->delta, delta, struct mfs_delta_s, list)
|
||||
{
|
||||
if (delta->upd)
|
||||
{
|
||||
s = MAX(delta->off, start);
|
||||
e = MIN(delta->off + delta->n_b, end);
|
||||
|
||||
if (s >= e)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
memcpy(buf + (s - data_off), delta->upd + (s - delta->off),
|
||||
e - s);
|
||||
}
|
||||
else
|
||||
{
|
||||
s = MAX(delta->off, start);
|
||||
e = MIN(delta->off + delta->n_b, end);
|
||||
|
||||
if (s >= end)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (e <= start)
|
||||
{
|
||||
del_b += delta->n_b;
|
||||
s = data_off + delta->n_b;
|
||||
e = end;
|
||||
memmove(buf, buf + delta->n_b,
|
||||
buflen - delta->n_b);
|
||||
e = data_off + buflen - delta->n_b;
|
||||
}
|
||||
else
|
||||
{
|
||||
del_b += e - s;
|
||||
memmove(buf + s, buf + e, buflen - (e - s));
|
||||
e = data_off + buflen - (e - s);
|
||||
}
|
||||
}
|
||||
|
||||
start = s;
|
||||
end = e;
|
||||
}
|
||||
|
||||
start = end;
|
||||
end = data_off + buflen;
|
||||
}
|
||||
while (start != end);
|
||||
|
||||
errout:
|
||||
return ret;
|
||||
}
|
||||
|
||||
void mfs_lru_init(FAR struct mfs_sb_s * const sb)
|
||||
{
|
||||
/* TODO */
|
||||
list_initialize(&MFS_LRU(sb));
|
||||
sb->n_lru = 0;
|
||||
|
||||
finfo("LRU Initialized\n");
|
||||
}
|
||||
|
||||
void mfs_lru_updatedsz(FAR struct mfs_sb_s * const sb,
|
||||
FAR const struct mfs_path_s * const path,
|
||||
const mfs_t depth, mfs_t *n_sz)
|
||||
FAR const struct mfs_path_s * const path,
|
||||
const mfs_t depth, mfs_t *n_sz)
|
||||
{
|
||||
/* TODO */
|
||||
mfs_t o_sz;
|
||||
FAR struct mfs_node_s *node = NULL;
|
||||
FAR struct mfs_delta_s *delta = NULL;
|
||||
|
||||
if (depth == 0)
|
||||
{
|
||||
/* Master node */
|
||||
|
||||
o_sz = 0;
|
||||
}
|
||||
|
||||
if (depth == 1)
|
||||
{
|
||||
/* Root node. */
|
||||
|
||||
o_sz = MFS_MN(sb).root_sz;
|
||||
}
|
||||
else
|
||||
{
|
||||
o_sz = path[depth - 1].sz;
|
||||
}
|
||||
|
||||
*n_sz = o_sz;
|
||||
|
||||
lru_nodesearch(sb, path, depth, &node);
|
||||
if (node == NULL)
|
||||
{
|
||||
finfo("No updates for file size, with depth %u.", depth);
|
||||
return;
|
||||
}
|
||||
|
||||
list_for_every_entry(&node->delta, delta, struct mfs_delta_s, list)
|
||||
{
|
||||
finfo("depth %u %u %u %p", depth, delta->n_b, delta->off, delta->upd);
|
||||
if (delta->upd == NULL)
|
||||
{
|
||||
*n_sz -= MIN((*n_sz) - delta->off, delta->n_b);
|
||||
}
|
||||
else
|
||||
{
|
||||
*n_sz = MAX(*n_sz, delta->off + delta->n_b);
|
||||
}
|
||||
}
|
||||
|
||||
finfo("Updated file size is %u with depth %u.", *n_sz, depth);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -93,10 +93,21 @@ int mfs_markbadblk(FAR const struct mfs_sb_s * const sb, mfs_t blk)
|
|||
return MTD_ISBAD(MFS_MTD(sb), blk);
|
||||
}
|
||||
|
||||
ssize_t mfs_write_page(FAR const struct mfs_sb_s * const sb,
|
||||
FAR const char *data, const mfs_t datalen,
|
||||
const off_t page, const mfs_t pgoff)
|
||||
{
|
||||
/* TODO */
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
ssize_t mfs_read_page(FAR const struct mfs_sb_s * const sb,
|
||||
FAR char *data, const mfs_t datalen, const off_t page,
|
||||
const mfs_t pgoff)
|
||||
{
|
||||
/* TODO */
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -221,7 +221,7 @@ mfs_t mfs_v2n(mfs_t n)
|
|||
return (n & (~(n - 1)));
|
||||
}
|
||||
|
||||
mfs_t set_msb(mfs_t n)
|
||||
mfs_t mfs_set_msb(mfs_t n)
|
||||
{
|
||||
return 31 - mfs_clz(n);
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue