요약·해설과 원문, 전문 번역을 서로 분리했습니다. API 이름, symbol, source path는 원문 표기를 사용합니다.
1. 요약·해설
원문의 핵심 논리와 kernel programming 관점의 보충 설명입니다. 아래의 전문 번역과는 별도로 작성했습니다.
2. 영어 원문 전체
번역 기준이 된 Linux v6.18.37 원문입니다. 줄 번호는 이 버전의 파일 좌표입니다.
원문 전체 펼치기
.. SPDX-License-Identifier: GPL-2.0
==============================
Network Filesystem Caching API
==============================
Fscache provides an API by which a network filesystem can make use of local
caching facilities. The API is arranged around a number of principles:
(1) A cache is logically organised into volumes and data storage objects
within those volumes.
(2) Volumes and data storage objects are represented by various types of
cookie.
(3) Cookies have keys that distinguish them from their peers.
(4) Cookies have coherency data that allows a cache to determine if the
cached data is still valid.
(5) I/O is done asynchronously where possible.
This API is used by::
#include <linux/fscache.h>.
.. This document contains the following sections:
(1) Overview
(2) Volume registration
(3) Data file registration
(4) Declaring a cookie to be in use
(5) Resizing a data file (truncation)
(6) Data I/O API
(7) Data file coherency
(8) Data file invalidation
(9) Write back resource management
(10) Caching of local modifications
(11) Page release and invalidation
Overview
========
The fscache hierarchy is organised on two levels from a network filesystem's
point of view. The upper level represents "volumes" and the lower level
represents "data storage objects". These are represented by two types of
cookie, hereafter referred to as "volume cookies" and "cookies".
A network filesystem acquires a volume cookie for a volume using a volume key,
which represents all the information that defines that volume (e.g. cell name
or server address, volume ID or share name). This must be rendered as a
printable string that can be used as a directory name (ie. no '/' characters
and shouldn't begin with a '.'). The maximum name length is one less than the
maximum size of a filename component (allowing the cache backend one char for
its own purposes).
A filesystem would typically have a volume cookie for each superblock.
The filesystem then acquires a cookie for each file within that volume using an
object key. Object keys are binary blobs and only need to be unique within
their parent volume. The cache backend is responsible for rendering the binary
blob into something it can use and may employ hash tables, trees or whatever to
improve its ability to find an object. This is transparent to the network
filesystem.
A filesystem would typically have a cookie for each inode, and would acquire it
in iget and relinquish it when evicting the cookie.
Once it has a cookie, the filesystem needs to mark the cookie as being in use.
This causes fscache to send the cache backend off to look up/create resources
for the cookie in the background, to check its coherency and, if necessary, to
mark the object as being under modification.
A filesystem would typically "use" the cookie in its file open routine and
unuse it in file release and it needs to use the cookie around calls to
truncate the cookie locally. It *also* needs to use the cookie when the
pagecache becomes dirty and unuse it when writeback is complete. This is
slightly tricky, and provision is made for it.
When performing a read, write or resize on a cookie, the filesystem must first
begin an operation. This copies the resources into a holding struct and puts
extra pins into the cache to stop cache withdrawal from tearing down the
structures being used. The actual operation can then be issued and conflicting
invalidations can be detected upon completion.
The filesystem is expected to use netfslib to access the cache, but that's not
actually required and it can use the fscache I/O API directly.
Volume Registration
===================
The first step for a network filesystem is to acquire a volume cookie for the
volume it wants to access::
struct fscache_volume *
fscache_acquire_volume(const char *volume_key,
const char *cache_name,
const void *coherency_data,
size_t coherency_len);
This function creates a volume cookie with the specified volume key as its name
and notes the coherency data.
The volume key must be a printable string with no '/' characters in it. It
should begin with the name of the filesystem and should be no longer than 254
characters. It should uniquely represent the volume and will be matched with
what's stored in the cache.
The caller may also specify the name of the cache to use. If specified,
fscache will look up or create a cache cookie of that name and will use a cache
of that name if it is online or comes online. If no cache name is specified,
it will use the first cache that comes to hand and set the name to that.
The specified coherency data is stored in the cookie and will be matched
against coherency data stored on disk. The data pointer may be NULL if no data
is provided. If the coherency data doesn't match, the entire cache volume will
be invalidated.
This function can return errors such as EBUSY if the volume key is already in
use by an acquired volume or ENOMEM if an allocation failure occurred. It may
also return a NULL volume cookie if fscache is not enabled. It is safe to
pass a NULL cookie to any function that takes a volume cookie. This will
cause that function to do nothing.
When the network filesystem has finished with a volume, it should relinquish it
by calling::
void fscache_relinquish_volume(struct fscache_volume *volume,
const void *coherency_data,
bool invalidate);
This will cause the volume to be committed or removed, and if sealed the
coherency data will be set to the value supplied. The amount of coherency data
must match the length specified when the volume was acquired. Note that all
data cookies obtained in this volume must be relinquished before the volume is
relinquished.
Data File Registration
======================
Once it has a volume cookie, a network filesystem can use it to acquire a
cookie for data storage::
struct fscache_cookie *
fscache_acquire_cookie(struct fscache_volume *volume,
u8 advice,
const void *index_key,
size_t index_key_len,
const void *aux_data,
size_t aux_data_len,
loff_t object_size)
This creates the cookie in the volume using the specified index key. The index
key is a binary blob of the given length and must be unique for the volume.
This is saved into the cookie. There are no restrictions on the content, but
its length shouldn't exceed about three quarters of the maximum filename length
to allow for encoding.
The caller should also pass in a piece of coherency data in aux_data. A buffer
of size aux_data_len will be allocated and the coherency data copied in. It is
assumed that the size is invariant over time. The coherency data is used to
check the validity of data in the cache. Functions are provided by which the
coherency data can be updated.
The file size of the object being cached should also be provided. This may be
used to trim the data and will be stored with the coherency data.
This function never returns an error, though it may return a NULL cookie on
allocation failure or if fscache is not enabled. It is safe to pass in a NULL
volume cookie and pass the NULL cookie returned to any function that takes it.
This will cause that function to do nothing.
When the network filesystem has finished with a cookie, it should relinquish it
by calling::
void fscache_relinquish_cookie(struct fscache_cookie *cookie,
bool retire);
This will cause fscache to either commit the storage backing the cookie or
delete it.
Marking A Cookie In-Use
=======================
Once a cookie has been acquired by a network filesystem, the filesystem should
tell fscache when it intends to use the cookie (typically done on file open)
and should say when it has finished with it (typically on file close)::
void fscache_use_cookie(struct fscache_cookie *cookie,
bool will_modify);
void fscache_unuse_cookie(struct fscache_cookie *cookie,
const void *aux_data,
const loff_t *object_size);
The *use* function tells fscache that it will use the cookie and, additionally,
indicate if the user is intending to modify the contents locally. If not yet
done, this will trigger the cache backend to go and gather the resources it
needs to access/store data in the cache. This is done in the background, and
so may not be complete by the time the function returns.
The *unuse* function indicates that a filesystem has finished using a cookie.
It optionally updates the stored coherency data and object size and then
decreases the in-use counter. When the last user unuses the cookie, it is
scheduled for garbage collection. If not reused within a short time, the
resources will be released to reduce system resource consumption.
A cookie must be marked in-use before it can be accessed for read, write or
resize - and an in-use mark must be kept whilst there is dirty data in the
pagecache in order to avoid an oops due to trying to open a file during process
exit.
Note that in-use marks are cumulative. For each time a cookie is marked
in-use, it must be unused.
Resizing A Data File (Truncation)
=================================
If a network filesystem file is resized locally by truncation, the following
should be called to notify the cache::
void fscache_resize_cookie(struct fscache_cookie *cookie,
loff_t new_size);
The caller must have first marked the cookie in-use. The cookie and the new
size are passed in and the cache is synchronously resized. This is expected to
be called from ``->setattr()`` inode operation under the inode lock.
Data I/O API
============
To do data I/O operations directly through a cookie, the following functions
are available::
int fscache_begin_read_operation(struct netfs_cache_resources *cres,
struct fscache_cookie *cookie);
int fscache_read(struct netfs_cache_resources *cres,
loff_t start_pos,
struct iov_iter *iter,
enum netfs_read_from_hole read_hole,
netfs_io_terminated_t term_func,
void *term_func_priv);
int fscache_write(struct netfs_cache_resources *cres,
loff_t start_pos,
struct iov_iter *iter,
netfs_io_terminated_t term_func,
void *term_func_priv);
The *begin* function sets up an operation, attaching the resources required to
the cache resources block from the cookie. Assuming it doesn't return an error
(for instance, it will return -ENOBUFS if given a NULL cookie, but otherwise do
nothing), then one of the other two functions can be issued.
The *read* and *write* functions initiate a direct-IO operation. Both take the
previously set up cache resources block, an indication of the start file
position, and an I/O iterator that describes buffer and indicates the amount of
data.
The read function also takes a parameter to indicate how it should handle a
partially populated region (a hole) in the disk content. This may be to ignore
it, skip over an initial hole and place zeros in the buffer or give an error.
The read and write functions can be given an optional termination function that
will be run on completion::
typedef
void (*netfs_io_terminated_t)(void *priv, ssize_t transferred_or_error,
bool was_async);
If a termination function is given, the operation will be run asynchronously
and the termination function will be called upon completion. If not given, the
operation will be run synchronously. Note that in the asynchronous case, it is
possible for the operation to complete before the function returns.
Both the read and write functions end the operation when they complete,
detaching any pinned resources.
The read operation will fail with ESTALE if invalidation occurred whilst the
operation was ongoing.
Data File Coherency
===================
To request an update of the coherency data and file size on a cookie, the
following should be called::
void fscache_update_cookie(struct fscache_cookie *cookie,
const void *aux_data,
const loff_t *object_size);
This will update the cookie's coherency data and/or file size.
Data File Invalidation
======================
Sometimes it will be necessary to invalidate an object that contains data.
Typically this will be necessary when the server informs the network filesystem
of a remote third-party change - at which point the filesystem has to throw
away the state and cached data that it had for an file and reload from the
server.
To indicate that a cache object should be invalidated, the following should be
called::
void fscache_invalidate(struct fscache_cookie *cookie,
const void *aux_data,
loff_t size,
unsigned int flags);
This increases the invalidation counter in the cookie to cause outstanding
reads to fail with -ESTALE, sets the coherency data and file size from the
information supplied, blocks new I/O on the cookie and dispatches the cache to
go and get rid of the old data.
Invalidation runs asynchronously in a worker thread so that it doesn't block
too much.
Write-Back Resource Management
==============================
To write data to the cache from network filesystem writeback, the cache
resources required need to be pinned at the point the modification is made (for
instance when the page is marked dirty) as it's not possible to open a file in
a thread that's exiting.
The following facilities are provided to manage this:
* An inode flag, ``I_PINNING_FSCACHE_WB``, is provided to indicate that an
in-use is held on the cookie for this inode. It can only be changed if the
the inode lock is held.
* A flag, ``unpinned_fscache_wb`` is placed in the ``writeback_control``
struct that gets set if ``__writeback_single_inode()`` clears
``I_PINNING_FSCACHE_WB`` because all the dirty pages were cleared.
To support this, the following functions are provided::
bool fscache_dirty_folio(struct address_space *mapping,
struct folio *folio,
struct fscache_cookie *cookie);
void fscache_unpin_writeback(struct writeback_control *wbc,
struct fscache_cookie *cookie);
void fscache_clear_inode_writeback(struct fscache_cookie *cookie,
struct inode *inode,
const void *aux);
The *set* function is intended to be called from the filesystem's
``dirty_folio`` address space operation. If ``I_PINNING_FSCACHE_WB`` is not
set, it sets that flag and increments the use count on the cookie (the caller
must already have called ``fscache_use_cookie()``).
The *unpin* function is intended to be called from the filesystem's
``write_inode`` superblock operation. It cleans up after writing by unusing
the cookie if unpinned_fscache_wb is set in the writeback_control struct.
The *clear* function is intended to be called from the netfs's ``evict_inode``
superblock operation. It must be called *after*
``truncate_inode_pages_final()``, but *before* ``clear_inode()``. This cleans
up any hanging ``I_PINNING_FSCACHE_WB``. It also allows the coherency data to
be updated.
Caching of Local Modifications
==============================
If a network filesystem has locally modified data that it wants to write to the
cache, it needs to mark the pages to indicate that a write is in progress, and
if the mark is already present, it needs to wait for it to be removed first
(presumably due to an already in-progress operation). This prevents multiple
competing DIO writes to the same storage in the cache.
Firstly, the netfs should determine if caching is available by doing something
like::
bool caching = fscache_cookie_enabled(cookie);
If caching is to be attempted, pages should be waited for and then marked using
the following functions provided by the netfs helper library::
void set_page_fscache(struct page *page);
void wait_on_page_fscache(struct page *page);
int wait_on_page_fscache_killable(struct page *page);
Once all the pages in the span are marked, the netfs can ask fscache to
schedule a write of that region::
void fscache_write_to_cache(struct fscache_cookie *cookie,
struct address_space *mapping,
loff_t start, size_t len, loff_t i_size,
netfs_io_terminated_t term_func,
void *term_func_priv,
bool caching)
And if an error occurs before that point is reached, the marks can be removed
by calling::
void fscache_clear_page_bits(struct address_space *mapping,
loff_t start, size_t len,
bool caching)
In these functions, a pointer to the mapping to which the source pages are
attached is passed in and start and len indicate the size of the region that's
going to be written (it doesn't have to align to page boundaries necessarily,
but it does have to align to DIO boundaries on the backing filesystem). The
caching parameter indicates if caching should be skipped, and if false, the
functions do nothing.
The write function takes some additional parameters: the cookie representing
the cache object to be written to, i_size indicates the size of the netfs file
and term_func indicates an optional completion function, to which
term_func_priv will be passed, along with the error or amount written.
Note that the write function will always run asynchronously and will unmark all
the pages upon completion before calling term_func.
Page Release and Invalidation
=============================
Fscache keeps track of whether we have any data in the cache yet for a cache
object we've just created. It knows it doesn't have to do any reading until it
has done a write and then the page it wrote from has been released by the VM,
after which it *has* to look in the cache.
To inform fscache that a page might now be in the cache, the following function
should be called from the ``release_folio`` address space op::
void fscache_note_page_release(struct fscache_cookie *cookie);
if the page has been released (ie. release_folio returned true).
Page release and page invalidation should also wait for any mark left on the
page to say that a DIO write is underway from that page::
void wait_on_page_fscache(struct page *page);
int wait_on_page_fscache_killable(struct page *page);
API Function Reference
======================
.. kernel-doc:: include/linux/fscache.h
3. 한국어 전문 번역
영어 원문의 문단 순서와 의미를 유지한 전체 번역입니다. 코드, 함수명, symbol과 URL은 원문 표기를 유지합니다.
네트워크 파일시스템 캐싱 API의 원칙
1-40Fscache는 네트워크 파일시스템이 로컬 캐싱 기능을 사용할 수 있게 하는 API를 제공합니다. 캐시는 논리적으로 volume과 그 안의 data storage object로 구성되며, 이 둘은 여러 종류의 cookie로 표현됩니다.
각 cookie에는 같은 계층의 다른 cookie와 구별하는 key가 있습니다. 또한 디스크에 저장된 캐시 데이터가 아직 유효한지를 판정할 coherency data를 가지며, 가능한 I/O는 비동기로 실행합니다.
이 API를 사용하는 코드는 `#include <linux/fscache.h>`를 포함합니다. 원문의 주석 목차는 overview, volume·data file 등록, cookie 사용 선언, truncation, 데이터 I/O, coherency·invalidation, writeback 자원 관리, 로컬 수정 캐싱, page release·invalidation의 11개 주제를 열거합니다.
원문의 다섯 원칙을 객체 구성에서 I/O 실행까지의 흐름으로 정리합니다.
.. SPDX-License-Identifier: GPL-2.0
==============================
Network Filesystem Caching API
==============================
Fscache provides an API by which a network filesystem can make use of local
caching facilities. The API is arranged around a number of principles:
(1) A cache is logically organised into volumes and data storage objects
within those volumes.
(2) Volumes and data storage objects are represented by various types of
cookie.
(3) Cookies have keys that distinguish them from their peers.
(4) Cookies have coherency data that allows a cache to determine if the
cached data is still valid.
(5) I/O is done asynchronously where possible.
This API is used by::
#include <linux/fscache.h>.
.. This document contains the following sections:
(1) Overview
(2) Volume registration
(3) Data file registration
(4) Declaring a cookie to be in use
(5) Resizing a data file (truncation)
(6) Data I/O API
(7) Data file coherency
(8) Data file invalidation
(9) Write back resource management
(10) Caching of local modifications
(11) Page release and invalidation
계층, key, in-use와 operation
41-90네트워크 파일시스템에서 본 fscache 계층은 두 단계입니다. 위 단계는 volume, 아래 단계는 data storage object이며 각각 volume cookie와 일반 cookie로 표현됩니다.
volume cookie는 volume을 정의하는 모든 정보, 예를 들어 cell name이나 server address와 volume ID 또는 share name을 담은 volume key로 얻습니다. 이 key는 디렉터리 이름으로 쓸 수 있는 출력 가능한 문자열이어야 하므로 `/`를 포함할 수 없고 `.`으로 시작하지 않는 편이 좋습니다. 최대 길이는 파일명 구성요소의 최대 크기보다 한 글자 짧아야 하며, 남은 한 글자는 cache backend가 자체 목적으로 사용합니다.
일반적으로 파일시스템은 superblock마다 volume cookie 하나를 둡니다. 그 volume 안의 각 파일에는 object key로 cookie를 얻습니다. object key는 부모 volume 안에서만 고유하면 되는 binary blob입니다. backend가 이 blob을 사용 가능한 표현으로 바꾸고 hash table이나 tree 등 적절한 검색 구조를 선택하므로 이 과정은 네트워크 파일시스템에 투명합니다.
보통 inode마다 cookie 하나를 두고 `iget`에서 획득하며 inode를 축출할 때 반환합니다. cookie를 얻은 뒤에는 in-use로 표시해야 합니다. 그러면 fscache가 backend에 자원 검색·생성을 비동기로 요청하고 coherency를 검사하며, 필요하면 객체가 수정 중임을 표시합니다.
파일 open에서 cookie를 use하고 release에서 unuse하는 것이 일반적입니다. 로컬 truncation 호출을 감싸는 동안에도 use해야 하며, pagecache가 dirty가 된 시점부터 writeback이 끝날 때까지도 in-use 표시를 유지해야 합니다.
cookie를 read, write 또는 resize하려면 먼저 operation을 시작합니다. 이 단계는 필요한 자원을 보관 구조체에 복사하고 cache withdrawal이 사용 중 구조를 해체하지 못하도록 추가 pin을 겁니다. 실제 작업이 끝날 때 충돌한 invalidation이 있었는지도 감지할 수 있습니다.
캐시 접근에는 netfslib 사용이 권장되지만 필수는 아니며, 파일시스템이 fscache I/O API를 직접 호출할 수도 있습니다.
volume 획득부터 operation 종료까지의 일반적인 네트워크 파일시스템 경로입니다.
Overview
========
The fscache hierarchy is organised on two levels from a network filesystem's
point of view. The upper level represents "volumes" and the lower level
represents "data storage objects". These are represented by two types of
cookie, hereafter referred to as "volume cookies" and "cookies".
A network filesystem acquires a volume cookie for a volume using a volume key,
which represents all the information that defines that volume (e.g. cell name
or server address, volume ID or share name). This must be rendered as a
printable string that can be used as a directory name (ie. no '/' characters
and shouldn't begin with a '.'). The maximum name length is one less than the
maximum size of a filename component (allowing the cache backend one char for
its own purposes).
A filesystem would typically have a volume cookie for each superblock.
The filesystem then acquires a cookie for each file within that volume using an
object key. Object keys are binary blobs and only need to be unique within
their parent volume. The cache backend is responsible for rendering the binary
blob into something it can use and may employ hash tables, trees or whatever to
improve its ability to find an object. This is transparent to the network
filesystem.
A filesystem would typically have a cookie for each inode, and would acquire it
in iget and relinquish it when evicting the cookie.
Once it has a cookie, the filesystem needs to mark the cookie as being in use.
This causes fscache to send the cache backend off to look up/create resources
for the cookie in the background, to check its coherency and, if necessary, to
mark the object as being under modification.
A filesystem would typically "use" the cookie in its file open routine and
unuse it in file release and it needs to use the cookie around calls to
truncate the cookie locally. It *also* needs to use the cookie when the
pagecache becomes dirty and unuse it when writeback is complete. This is
slightly tricky, and provision is made for it.
When performing a read, write or resize on a cookie, the filesystem must first
begin an operation. This copies the resources into a holding struct and puts
extra pins into the cache to stop cache withdrawal from tearing down the
structures being used. The actual operation can then be issued and conflicting
invalidations can be detected upon completion.
The filesystem is expected to use netfslib to access the cache, but that's not
actually required and it can use the fscache I/O API directly.
Volume 등록과 반환
91-141네트워크 파일시스템의 첫 단계는 `fscache_acquire_volume()`으로 접근할 volume의 cookie를 얻는 것입니다. 인자는 `volume_key`, 선택할 `cache_name`, coherency data와 그 길이입니다. 함수는 volume key를 이름으로 삼아 volume cookie를 만들고 coherency data를 기록합니다.
`volume_key`는 `/`가 없는 출력 가능한 문자열이어야 하고 파일시스템 이름으로 시작해야 하며 254자를 넘지 않아야 합니다. 캐시에 저장된 key와 비교되므로 해당 volume을 고유하게 표현해야 합니다.
호출자는 사용할 cache의 이름도 지정할 수 있습니다. 이름을 지정하면 fscache는 그 이름의 cache cookie를 찾거나 만들고, 해당 cache가 online이거나 이후 online이 되면 사용합니다. 이름을 생략하면 먼저 사용 가능한 cache를 선택하고 그 이름을 설정합니다.
coherency data는 cookie에 보관되어 디스크의 값과 비교됩니다. 데이터가 없으면 포인터를 `NULL`로 줄 수 있습니다. 값이 일치하지 않으면 cache volume 전체가 invalidation됩니다.
이미 획득된 volume이 같은 key를 사용하면 `EBUSY`, 메모리 할당이 실패하면 `ENOMEM` 같은 오류를 반환할 수 있습니다. fscache가 비활성화된 경우에는 `NULL` volume cookie를 반환할 수 있습니다. volume cookie를 받는 모든 함수에 `NULL`을 전달해도 안전하며, 그 함수는 아무 동작도 하지 않습니다.
volume 사용을 마치면 `fscache_relinquish_volume()`으로 반환합니다. 이 호출은 volume을 commit하거나 제거하며, sealed 상태라면 제공한 coherency data로 값을 설정합니다. 데이터 길이는 획득 시 지정한 길이와 같아야 하고, 이 volume에서 얻은 모든 data cookie를 먼저 반환해야 합니다.
획득과 반환에서 보존해야 할 key·coherency·수명 조건입니다.
Volume Registration
===================
The first step for a network filesystem is to acquire a volume cookie for the
volume it wants to access::
struct fscache_volume *
fscache_acquire_volume(const char *volume_key,
const char *cache_name,
const void *coherency_data,
size_t coherency_len);
This function creates a volume cookie with the specified volume key as its name
and notes the coherency data.
The volume key must be a printable string with no '/' characters in it. It
should begin with the name of the filesystem and should be no longer than 254
characters. It should uniquely represent the volume and will be matched with
what's stored in the cache.
The caller may also specify the name of the cache to use. If specified,
fscache will look up or create a cache cookie of that name and will use a cache
of that name if it is online or comes online. If no cache name is specified,
it will use the first cache that comes to hand and set the name to that.
The specified coherency data is stored in the cookie and will be matched
against coherency data stored on disk. The data pointer may be NULL if no data
is provided. If the coherency data doesn't match, the entire cache volume will
be invalidated.
This function can return errors such as EBUSY if the volume key is already in
use by an acquired volume or ENOMEM if an allocation failure occurred. It may
also return a NULL volume cookie if fscache is not enabled. It is safe to
pass a NULL cookie to any function that takes a volume cookie. This will
cause that function to do nothing.
When the network filesystem has finished with a volume, it should relinquish it
by calling::
void fscache_relinquish_volume(struct fscache_volume *volume,
const void *coherency_data,
bool invalidate);
This will cause the volume to be committed or removed, and if sealed the
coherency data will be set to the value supplied. The amount of coherency data
must match the length specified when the volume was acquired. Note that all
data cookies obtained in this volume must be relinquished before the volume is
relinquished.
Data file cookie 등록과 반환
142-187volume cookie를 얻은 뒤 `fscache_acquire_cookie()`로 data storage용 cookie를 획득합니다. 함수는 주어진 `index_key`를 사용해 volume 안에 cookie를 만듭니다.
`index_key`는 지정한 길이의 binary blob이며 해당 volume 안에서 고유해야 합니다. 내용에는 제한이 없지만, backend가 인코딩할 공간을 남기기 위해 길이는 최대 파일명 길이의 약 4분의 3을 넘지 않는 편이 좋습니다.
호출자는 `aux_data`에 coherency data도 전달합니다. fscache는 `aux_data_len` 크기의 버퍼를 할당해 이를 복사하며, 이 크기는 시간에 따라 변하지 않는다고 가정합니다. coherency data는 캐시 데이터의 유효성을 검사하는 데 쓰이고 별도 함수로 갱신할 수 있습니다.
캐시할 객체의 파일 크기인 `object_size`도 제공해야 합니다. 이 값은 데이터를 잘라내는 데 사용될 수 있으며 coherency data와 함께 저장됩니다.
이 함수는 오류값을 반환하지 않지만 메모리 할당 실패나 fscache 비활성화 시 `NULL` cookie를 반환할 수 있습니다. `NULL` volume을 입력해도 안전하고, 반환된 `NULL` cookie 역시 cookie를 받는 다른 함수에 전달할 수 있으며 모두 no-op가 됩니다.
cookie 사용을 마치면 `fscache_relinquish_cookie(cookie, retire)`를 호출합니다. fscache는 `retire` 정책에 따라 cookie를 뒷받침하는 저장소를 commit하거나 삭제합니다.
data storage object를 식별하고 검증하는 필드를 정리합니다.
Data File Registration
======================
Once it has a volume cookie, a network filesystem can use it to acquire a
cookie for data storage::
struct fscache_cookie *
fscache_acquire_cookie(struct fscache_volume *volume,
u8 advice,
const void *index_key,
size_t index_key_len,
const void *aux_data,
size_t aux_data_len,
loff_t object_size)
This creates the cookie in the volume using the specified index key. The index
key is a binary blob of the given length and must be unique for the volume.
This is saved into the cookie. There are no restrictions on the content, but
its length shouldn't exceed about three quarters of the maximum filename length
to allow for encoding.
The caller should also pass in a piece of coherency data in aux_data. A buffer
of size aux_data_len will be allocated and the coherency data copied in. It is
assumed that the size is invariant over time. The coherency data is used to
check the validity of data in the cache. Functions are provided by which the
coherency data can be updated.
The file size of the object being cached should also be provided. This may be
used to trim the data and will be stored with the coherency data.
This function never returns an error, though it may return a NULL cookie on
allocation failure or if fscache is not enabled. It is safe to pass in a NULL
volume cookie and pass the NULL cookie returned to any function that takes it.
This will cause that function to do nothing.
When the network filesystem has finished with a cookie, it should relinquish it
by calling::
void fscache_relinquish_cookie(struct fscache_cookie *cookie,
bool retire);
This will cause fscache to either commit the storage backing the cookie or
delete it.
Cookie in-use 표시
188-221cookie를 획득한 네트워크 파일시스템은 사용할 때, 보통 file open에서 `fscache_use_cookie()`를 호출하고 사용을 마칠 때, 보통 file close에서 `fscache_unuse_cookie()`를 호출해야 합니다.
use 함수의 `will_modify`는 내용을 로컬에서 수정할 의도가 있는지를 알립니다. 아직 준비되지 않았다면 backend가 캐시 데이터 접근·저장에 필요한 자원을 비동기로 수집하도록 합니다. 따라서 함수가 반환될 때 준비가 끝나지 않았을 수도 있습니다.
unuse 함수는 cookie 사용 종료를 알리고, 선택적으로 저장된 coherency data와 object size를 갱신한 다음 in-use counter를 감소시킵니다. 마지막 사용자가 unuse하면 garbage collection 대상으로 예약되고, 짧은 시간 안에 재사용되지 않으면 시스템 자원 소비를 줄이기 위해 관련 자원이 해제됩니다.
read, write, resize로 접근하기 전에는 반드시 cookie가 in-use여야 합니다. pagecache에 dirty data가 남아 있는 동안에도 표시를 유지해야 프로세스 종료 중 파일을 열려고 하면서 발생할 수 있는 oops를 피할 수 있습니다.
in-use 표시는 누적됩니다. use를 호출한 횟수마다 정확히 대응하는 unuse 호출이 필요합니다.
파일 open과 writeback이 겹칠 때도 각 사용 주체가 독립적으로 표시를 해제해야 합니다.
Marking A Cookie In-Use
=======================
Once a cookie has been acquired by a network filesystem, the filesystem should
tell fscache when it intends to use the cookie (typically done on file open)
and should say when it has finished with it (typically on file close)::
void fscache_use_cookie(struct fscache_cookie *cookie,
bool will_modify);
void fscache_unuse_cookie(struct fscache_cookie *cookie,
const void *aux_data,
const loff_t *object_size);
The *use* function tells fscache that it will use the cookie and, additionally,
indicate if the user is intending to modify the contents locally. If not yet
done, this will trigger the cache backend to go and gather the resources it
needs to access/store data in the cache. This is done in the background, and
so may not be complete by the time the function returns.
The *unuse* function indicates that a filesystem has finished using a cookie.
It optionally updates the stored coherency data and object size and then
decreases the in-use counter. When the last user unuses the cookie, it is
scheduled for garbage collection. If not reused within a short time, the
resources will be released to reduce system resource consumption.
A cookie must be marked in-use before it can be accessed for read, write or
resize - and an in-use mark must be kept whilst there is dirty data in the
pagecache in order to avoid an oops due to trying to open a file during process
exit.
Note that in-use marks are cumulative. For each time a cookie is marked
in-use, it must be unused.
Data file 크기 변경
222-235네트워크 파일시스템의 파일을 truncation으로 로컬에서 변경하면 `fscache_resize_cookie(cookie, new_size)`를 호출해 캐시에 새 크기를 알려야 합니다.
호출 전에 cookie를 in-use로 표시해야 합니다. 함수는 cookie와 새 크기를 받아 캐시를 동기적으로 resize합니다. inode lock을 잡은 상태에서 `->setattr()` inode operation이 호출하는 것을 전제로 합니다.
inode 크기와 캐시 객체 크기를 같은 잠금 범위에서 맞춥니다.
Resizing A Data File (Truncation)
=================================
If a network filesystem file is resized locally by truncation, the following
should be called to notify the cache::
void fscache_resize_cookie(struct fscache_cookie *cookie,
loff_t new_size);
The caller must have first marked the cookie in-use. The cookie and the new
size are passed in and the cache is synchronously resized. This is expected to
be called from ``->setattr()`` inode operation under the inode lock.
직접 Data I/O API
236-288cookie를 통해 직접 data I/O를 하려면 `fscache_begin_read_operation()`, `fscache_read()`, `fscache_write()`를 사용합니다. begin 함수는 cookie에서 필요한 자원을 `netfs_cache_resources` 블록에 연결해 operation을 설정합니다.
begin이 오류를 반환하지 않은 경우 read 또는 write를 실행할 수 있습니다. 예를 들어 `NULL` cookie를 주면 `-ENOBUFS`를 반환하지만 그 밖의 부수 동작은 하지 않습니다.
read와 write는 direct I/O operation을 시작합니다. 둘 다 준비된 cache resource block, 시작 파일 위치, buffer와 데이터 양을 설명하는 `iov_iter`를 받습니다.
read는 디스크 내용 중 일부만 채워진 구간인 hole을 처리하는 방법도 받습니다. hole을 무시하거나, 앞쪽 hole을 건너뛰면서 buffer에 0을 채우거나, 오류를 반환하도록 선택할 수 있습니다.
read와 write에는 선택적인 완료 함수 `netfs_io_terminated_t`를 전달할 수 있습니다. callback은 private pointer, 전송 바이트 수 또는 오류인 `transferred_or_error`, 비동기 실행 여부인 `was_async`를 받습니다.
완료 함수를 주면 operation은 비동기로 실행되고 끝날 때 callback을 호출합니다. 완료 함수를 주지 않으면 동기적으로 실행합니다. 비동기인 경우에도 함수 자체가 반환되기 전에 operation이 완료될 수 있습니다.
read와 write는 완료할 때 operation을 끝내고 pin된 자원을 분리합니다. operation이 진행되는 동안 invalidation이 발생했다면 read는 `ESTALE`로 실패합니다.
준비, 실행 방식, 완료와 invalidation 결과를 비교합니다.
Data I/O API
============
To do data I/O operations directly through a cookie, the following functions
are available::
int fscache_begin_read_operation(struct netfs_cache_resources *cres,
struct fscache_cookie *cookie);
int fscache_read(struct netfs_cache_resources *cres,
loff_t start_pos,
struct iov_iter *iter,
enum netfs_read_from_hole read_hole,
netfs_io_terminated_t term_func,
void *term_func_priv);
int fscache_write(struct netfs_cache_resources *cres,
loff_t start_pos,
struct iov_iter *iter,
netfs_io_terminated_t term_func,
void *term_func_priv);
The *begin* function sets up an operation, attaching the resources required to
the cache resources block from the cookie. Assuming it doesn't return an error
(for instance, it will return -ENOBUFS if given a NULL cookie, but otherwise do
nothing), then one of the other two functions can be issued.
The *read* and *write* functions initiate a direct-IO operation. Both take the
previously set up cache resources block, an indication of the start file
position, and an I/O iterator that describes buffer and indicates the amount of
data.
The read function also takes a parameter to indicate how it should handle a
partially populated region (a hole) in the disk content. This may be to ignore
it, skip over an initial hole and place zeros in the buffer or give an error.
The read and write functions can be given an optional termination function that
will be run on completion::
typedef
void (*netfs_io_terminated_t)(void *priv, ssize_t transferred_or_error,
bool was_async);
If a termination function is given, the operation will be run asynchronously
and the termination function will be called upon completion. If not given, the
operation will be run synchronously. Note that in the asynchronous case, it is
possible for the operation to complete before the function returns.
Both the read and write functions end the operation when they complete,
detaching any pinned resources.
The read operation will fail with ESTALE if invalidation occurred whilst the
operation was ongoing.
Coherency data와 파일 크기 갱신
289-301cookie의 coherency data와 파일 크기 갱신을 요청하려면 `fscache_update_cookie(cookie, aux_data, object_size)`를 호출합니다.
`aux_data`와 `object_size`는 포인터이므로 호출자는 필요한 값만 제공할 수 있습니다. 함수는 cookie에 저장된 coherency data 및 파일 크기를 갱신합니다.
서버에서 확인한 상태를 캐시 객체의 검증 정보에 반영합니다.
Data File Coherency
===================
To request an update of the coherency data and file size on a cookie, the
following should be called::
void fscache_update_cookie(struct fscache_cookie *cookie,
const void *aux_data,
const loff_t *object_size);
This will update the cookie's coherency data and/or file size.
Data file invalidation
302-327서버가 원격 제3자의 변경을 네트워크 파일시스템에 알리는 경우처럼, 데이터를 담은 캐시 객체를 invalidation해야 할 때가 있습니다. 파일시스템은 해당 파일에 보관하던 상태와 cached data를 버리고 서버에서 다시 읽어야 합니다.
`fscache_invalidate(cookie, aux_data, size, flags)`를 호출하면 cookie의 invalidation counter를 증가시켜 진행 중 read가 `-ESTALE`로 실패하게 합니다. 또한 제공한 정보로 coherency data와 file size를 설정하고, cookie의 새 I/O를 차단한 뒤 backend가 오래된 데이터를 제거하도록 요청합니다.
invalidation은 worker thread에서 비동기로 실행되어 호출 경로를 오래 막지 않습니다.
서버 변경 통지에서 오래된 캐시 데이터 제거까지의 순서입니다.
Data File Invalidation
======================
Sometimes it will be necessary to invalidate an object that contains data.
Typically this will be necessary when the server informs the network filesystem
of a remote third-party change - at which point the filesystem has to throw
away the state and cached data that it had for an file and reload from the
server.
To indicate that a cache object should be invalidated, the following should be
called::
void fscache_invalidate(struct fscache_cookie *cookie,
const void *aux_data,
loff_t size,
unsigned int flags);
This increases the invalidation counter in the cookie to cause outstanding
reads to fail with -ESTALE, sets the coherency data and file size from the
information supplied, blocks new I/O on the cookie and dispatches the cache to
go and get rid of the old data.
Invalidation runs asynchronously in a worker thread so that it doesn't block
too much.
Writeback 자원 pin 관리
328-372네트워크 파일시스템 writeback에서 캐시로 데이터를 쓰려면 수정이 발생한 시점, 예를 들어 folio가 dirty로 표시될 때 필요한 cache resource를 pin해야 합니다. 종료 중인 thread에서는 파일을 새로 열 수 없기 때문입니다.
inode flag `I_PINNING_FSCACHE_WB`는 해당 inode의 cookie에 in-use 표시를 보유 중임을 나타냅니다. 이 flag는 inode lock을 잡은 상태에서만 변경할 수 있습니다.
`writeback_control` 구조체의 `unpinned_fscache_wb` flag는 모든 dirty page가 정리되어 `__writeback_single_inode()`가 `I_PINNING_FSCACHE_WB`를 지웠을 때 설정됩니다.
`fscache_dirty_folio()`는 파일시스템의 `dirty_folio` address space operation에서 호출하도록 설계되었습니다. `I_PINNING_FSCACHE_WB`가 설정되지 않았다면 flag를 설정하고 cookie use count를 증가시킵니다. 호출자는 그 전에 이미 `fscache_use_cookie()`를 호출했어야 합니다.
`fscache_unpin_writeback()`은 파일시스템의 `write_inode` superblock operation에서 호출합니다. `writeback_control`의 `unpinned_fscache_wb`가 설정되어 있으면 cookie를 unuse해 writeback 뒤를 정리합니다.
`fscache_clear_inode_writeback()`은 netfs의 `evict_inode` superblock operation에서 호출합니다. 반드시 `truncate_inode_pages_final()` 뒤이면서 `clear_inode()` 앞이어야 합니다. 남아 있는 `I_PINNING_FSCACHE_WB`를 정리하고 coherency data도 갱신할 수 있게 합니다.
세 helper의 호출 지점과 정리 책임을 구분합니다.
Write-Back Resource Management
==============================
To write data to the cache from network filesystem writeback, the cache
resources required need to be pinned at the point the modification is made (for
instance when the page is marked dirty) as it's not possible to open a file in
a thread that's exiting.
The following facilities are provided to manage this:
* An inode flag, ``I_PINNING_FSCACHE_WB``, is provided to indicate that an
in-use is held on the cookie for this inode. It can only be changed if the
the inode lock is held.
* A flag, ``unpinned_fscache_wb`` is placed in the ``writeback_control``
struct that gets set if ``__writeback_single_inode()`` clears
``I_PINNING_FSCACHE_WB`` because all the dirty pages were cleared.
To support this, the following functions are provided::
bool fscache_dirty_folio(struct address_space *mapping,
struct folio *folio,
struct fscache_cookie *cookie);
void fscache_unpin_writeback(struct writeback_control *wbc,
struct fscache_cookie *cookie);
void fscache_clear_inode_writeback(struct fscache_cookie *cookie,
struct inode *inode,
const void *aux);
The *set* function is intended to be called from the filesystem's
``dirty_folio`` address space operation. If ``I_PINNING_FSCACHE_WB`` is not
set, it sets that flag and increments the use count on the cookie (the caller
must already have called ``fscache_use_cookie()``).
The *unpin* function is intended to be called from the filesystem's
``write_inode`` superblock operation. It cleans up after writing by unusing
the cookie if unpinned_fscache_wb is set in the writeback_control struct.
The *clear* function is intended to be called from the netfs's ``evict_inode``
superblock operation. It must be called *after*
``truncate_inode_pages_final()``, but *before* ``clear_inode()``. This cleans
up any hanging ``I_PINNING_FSCACHE_WB``. It also allows the coherency data to
be updated.
로컬 수정 데이터 캐싱
373-426로컬에서 수정한 데이터를 캐시에 쓰려면 네트워크 파일시스템은 write가 진행 중임을 page에 표시해야 합니다. 표시가 이미 있으면 먼저 사라질 때까지 기다려야 하며, 이는 같은 캐시 저장소에 대한 여러 direct I/O write가 경쟁하는 것을 막습니다.
먼저 `fscache_cookie_enabled(cookie)` 같은 검사로 캐싱 가능 여부를 정합니다. 캐싱을 시도한다면 netfs helper library의 `wait_on_page_fscache()`, `wait_on_page_fscache_killable()`로 기존 표시가 풀리기를 기다린 뒤 `set_page_fscache()`로 page를 표시합니다.
구간의 모든 page가 표시되면 `fscache_write_to_cache()`로 해당 영역의 write를 예약합니다. 인자는 cache object의 cookie, source page가 연결된 `address_space`, `start`, `len`, netfs 파일 크기 `i_size`, 완료 callback과 private data, 캐싱 여부입니다.
write 예약 지점에 도달하기 전에 오류가 발생하면 `fscache_clear_page_bits(mapping, start, len, caching)`로 표시를 제거할 수 있습니다.
`start`와 `len`은 쓸 영역을 나타냅니다. 반드시 page boundary에 맞을 필요는 없지만 backing filesystem의 DIO boundary에는 맞아야 합니다. `caching`이 false이면 두 함수는 캐싱을 건너뛰고 아무 동작도 하지 않습니다.
완료 함수에는 `term_func_priv`와 함께 오류 또는 기록한 양이 전달됩니다. 이 write 함수는 항상 비동기로 실행되고, 완료 시 모든 page의 표시를 먼저 해제한 뒤 `term_func`를 호출합니다.
page 표시가 같은 cache range에 대한 경쟁 DIO를 막습니다.
Caching of Local Modifications
==============================
If a network filesystem has locally modified data that it wants to write to the
cache, it needs to mark the pages to indicate that a write is in progress, and
if the mark is already present, it needs to wait for it to be removed first
(presumably due to an already in-progress operation). This prevents multiple
competing DIO writes to the same storage in the cache.
Firstly, the netfs should determine if caching is available by doing something
like::
bool caching = fscache_cookie_enabled(cookie);
If caching is to be attempted, pages should be waited for and then marked using
the following functions provided by the netfs helper library::
void set_page_fscache(struct page *page);
void wait_on_page_fscache(struct page *page);
int wait_on_page_fscache_killable(struct page *page);
Once all the pages in the span are marked, the netfs can ask fscache to
schedule a write of that region::
void fscache_write_to_cache(struct fscache_cookie *cookie,
struct address_space *mapping,
loff_t start, size_t len, loff_t i_size,
netfs_io_terminated_t term_func,
void *term_func_priv,
bool caching)
And if an error occurs before that point is reached, the marks can be removed
by calling::
void fscache_clear_page_bits(struct address_space *mapping,
loff_t start, size_t len,
bool caching)
In these functions, a pointer to the mapping to which the source pages are
attached is passed in and start and len indicate the size of the region that's
going to be written (it doesn't have to align to page boundaries necessarily,
but it does have to align to DIO boundaries on the backing filesystem). The
caching parameter indicates if caching should be skipped, and if false, the
functions do nothing.
The write function takes some additional parameters: the cookie representing
the cache object to be written to, i_size indicates the size of the netfs file
and term_func indicates an optional completion function, to which
term_func_priv will be passed, along with the error or amount written.
Note that the write function will always run asynchronously and will unmark all
the pages upon completion before calling term_func.
Page release와 invalidation 대기
427-448Fscache는 새 cache object에 아직 캐시 데이터가 존재하는지 추적합니다. 처음에는 read할 필요가 없지만, 한 번 write를 수행하고 source page가 VM에서 release된 뒤에는 해당 데이터가 cache에 있을 수 있으므로 cache를 확인해야 합니다.
page가 실제로 release되었다면, 즉 `release_folio`가 true를 반환했다면 해당 address space operation에서 `fscache_note_page_release(cookie)`를 호출해 page가 이제 cache에 존재할 수 있음을 알립니다.
page release와 page invalidation은 해당 page에서 DIO write가 진행 중임을 나타내는 표시가 남아 있으면 `wait_on_page_fscache()` 또는 killable 변형으로 완료를 기다려야 합니다.
새 객체가 캐시를 확인해야 하는 시점을 추적하는 상태 변화입니다.
Page Release and Invalidation
=============================
Fscache keeps track of whether we have any data in the cache yet for a cache
object we've just created. It knows it doesn't have to do any reading until it
has done a write and then the page it wrote from has been released by the VM,
after which it *has* to look in the cache.
To inform fscache that a page might now be in the cache, the following function
should be called from the ``release_folio`` address space op::
void fscache_note_page_release(struct fscache_cookie *cookie);
if the page has been released (ie. release_folio returned true).
Page release and page invalidation should also wait for any mark left on the
page to say that a DIO write is underway from that page::
void wait_on_page_fscache(struct page *page);
int wait_on_page_fscache_killable(struct page *page);
API 함수 참조
449-452이 문서의 함수별 상세 참조는 `include/linux/fscache.h`의 kernel-doc 주석에서 생성됩니다. 선언과 parameter contract가 바뀌면 이 참조 섹션도 해당 header의 최신 설명을 반영합니다.
문서 끝의 Sphinx kernel-doc 지시가 API 선언 설명을 가져오는 위치입니다.
API Function Reference
======================
.. kernel-doc:: include/linux/fscache.h
요약·해설
netfs-api.rst:1-452Fscache netfs API는 superblock을 volume cookie로, inode의 data storage object를 data cookie로 모델링합니다. key는 객체를 식별하고 coherency data와 object size는 저장 데이터의 유효성을 검증합니다. cookie는 접근 전에 in-use로 표시하며, 실제 I/O 전에 operation을 시작해 backend 자원을 pin합니다.
직접 read·write는 `iov_iter` 기반 DIO이고 callback 유무에 따라 비동기 또는 동기로 실행됩니다. 원격 변경은 invalidation counter와 `-ESTALE`로 진행 중 read를 차단하며, dirty folio writeback과 로컬 수정 캐싱은 inode·page 표시를 통해 종료 중 자원 접근과 경쟁 DIO를 방지합니다.
등록, 사용, I/O, 일관성 처리, writeback과 반환을 한 흐름으로 연결합니다.