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:
Saurav Pal 2024-07-12 14:13:48 +00:00 • committed by Alan Carvalho de Assis
parent 211e2cd8b9
commit 9ef9788b6f
7 changed files with 1355 additions and 19 deletions

View file

@ -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

View file

@ -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

View file

@ -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
View 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;
}

View file

@ -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);
}

View file

@ -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;
}

View file

@ -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);
}