Cache Backend API

The FS-Cache system provides an API by which actual caches can be supplied toFS-Cache for it to then serve out to network filesystems and other interestedparties. This API is used by:

#include <linux/fscache-cache.h>.

Overview

Interaction with the API is handled on three levels: cache, volume and datastorage, and each level has its own type of cookie object:

COOKIE

C TYPE

Cache cookie

structfscache_cache

Volume cookie

structfscache_volume

Data storage cookie

structfscache_cookie

Cookies are used to provide some filesystem data to the cache, manage state andpin the cache during access in addition to acting as reference points for theAPI functions. Each cookie has a debugging ID that is included in trace pointsto make it easier to correlate traces. Note, though, that debugging IDs aresimply allocated from incrementing counters and will eventually wrap.

The cache backend and the network filesystem can both ask for cache cookies -and if they ask for one of the same name, they’ll get the same cookie. Volumeand data cookies, however, are created at the behest of the filesystem only.

Cache Cookies

Caches are represented in the API by cache cookies. These are objects oftype:

struct fscache_cache {        void            *cache_priv;        unsigned int    debug_id;        char            *name;        ...};

There are a few fields that the cache backend might be interested in. Thedebug_id can be used in tracing to match lines referring to the same cacheandname is the name the cache was registered with. Thecache_privmember is private data provided by the cache when it is brought online. Theother fields are for internal use.

Registering a Cache

When a cache backend wants to bring a cache online, it should first registerthe cache name and that will get it a cache cookie. This is done with:

struct fscache_cache *fscache_acquire_cache(const char *name);

This will look up and potentially create a cache cookie. The cache cookie mayhave already been created by a network filesystem looking for it, in which casethat cache cookie will be used. If the cache cookie is not in use by anothercache, it will be moved into the preparing state, otherwise it will returnbusy.

If successful, the cache backend can then start setting up the cache. In theevent that the initialisation fails, the cache backend should call:

void fscache_relinquish_cache(struct fscache_cache *cache);

to reset and discard the cookie.

Bringing a Cache Online

Once the cache is set up, it can be brought online by calling:

int fscache_add_cache(struct fscache_cache *cache,                      const struct fscache_cache_ops *ops,                      void *cache_priv);

This stores the cache operations table pointer and cache private data into thecache cookie and moves the cache to the active state, thereby allowing accessesto take place.

Withdrawing a Cache From Service

The cache backend can withdraw a cache from service by calling this function:

void fscache_withdraw_cache(struct fscache_cache *cache);

This moves the cache to the withdrawn state to prevent new cache- andvolume-level accesses from starting and then waits for outstanding cache-levelaccesses to complete.

The cache must then go through the data storage objects it has and tell fscacheto withdraw them, calling:

void fscache_withdraw_cookie(struct fscache_cookie *cookie);

on the cookie that each object belongs to. This schedules the specified cookiefor withdrawal. This gets offloaded to a workqueue. The cache backend canwait for completion by calling:

void fscache_wait_for_objects(struct fscache_cache *cache);

Once all the cookies are withdrawn, a cache backend can withdraw all thevolumes, calling:

void fscache_withdraw_volume(struct fscache_volume *volume);

to tell fscache that a volume has been withdrawn. This waits for alloutstanding accesses on the volume to complete before returning.

When the cache is completely withdrawn, fscache should be notified bycalling:

void fscache_relinquish_cache(struct fscache_cache *cache);

to clear fields in the cookie and discard the caller’s ref on it.

Volume Cookies

Within a cache, the data storage objects are organised into logical volumes.These are represented in the API as objects of type:

struct fscache_volume {        struct fscache_cache            *cache;        void                            *cache_priv;        unsigned int                    debug_id;        char                            *key;        unsigned int                    key_hash;        ...        u8                              coherency_len;        u8                              coherency[];};

There are a number of fields here that are of interest to the caching backend:

  • cache - The parent cache cookie.

  • cache_priv - A place for the cache to stash private data.

  • debug_id - A debugging ID for logging in tracepoints.

  • key - A printable string with no ‘/’ characters in it that representsthe index key for the volume. The key is NUL-terminated and padded out toa multiple of 4 bytes.

  • key_hash - A hash of the index key. This should work out the same, nomatter the cpu arch and endianness.

  • coherency - A piece of coherency data that should be checked when thevolume is bound to in the cache.

  • coherency_len - The amount of data in the coherency buffer.

Data Storage Cookies

A volume is a logical group of data storage objects, each of which isrepresented to the network filesystem by a cookie. Cookies are represented inthe API as objects of type:

struct fscache_cookie {        struct fscache_volume           *volume;        void                            *cache_priv;        unsigned long                   flags;        unsigned int                    debug_id;        unsigned int                    inval_counter;        loff_t                          object_size;        u8                              advice;        u32                             key_hash;        u8                              key_len;        u8                              aux_len;        ...};

The fields in the cookie that are of interest to the cache backend are:

  • volume - The parent volume cookie.

  • cache_priv - A place for the cache to stash private data.

  • flags - A collection of bit flags, including:

    • FSCACHE_COOKIE_NO_DATA_TO_READ - There is no data available in thecache to be read as the cookie has been created or invalidated.

    • FSCACHE_COOKIE_NEEDS_UPDATE - The coherency data and/or object size hasbeen changed and needs committing.

    • FSCACHE_COOKIE_LOCAL_WRITE - The netfs’s data has been modifiedlocally, so the cache object may be in an incoherent state with respectto the server.

    • FSCACHE_COOKIE_HAVE_DATA - The backend should set this if itsuccessfully stores data into the cache.

    • FSCACHE_COOKIE_RETIRED - The cookie was invalidated when it wasrelinquished and the cached data should be discarded.

  • debug_id - A debugging ID for logging in tracepoints.

  • inval_counter - The number of invalidations done on the cookie.

  • advice - Information about how the cookie is to be used.

  • key_hash - A hash of the index key. This should work out the same, nomatter the cpu arch and endianness.

  • key_len - The length of the index key.

  • aux_len - The length of the coherency data buffer.

Each cookie has an index key, which may be stored inline to the cookie orelsewhere. A pointer to this can be obtained by calling:

void *fscache_get_key(struct fscache_cookie *cookie);

The index key is a binary blob, the storage for which is padded out to amultiple of 4 bytes.

Each cookie also has a buffer for coherency data. This may also be inline ordetached from the cookie and a pointer is obtained by calling:

void *fscache_get_aux(struct fscache_cookie *cookie);

Cookie Accounting

Data storage cookies are counted and this is used to block cache withdrawalcompletion until all objects have been destroyed. The following functions areprovided to the cache to deal with that:

void fscache_count_object(struct fscache_cache *cache);void fscache_uncount_object(struct fscache_cache *cache);void fscache_wait_for_objects(struct fscache_cache *cache);

The count function records the allocation of an object in a cache and theuncount function records its destruction. Warning: by the time the uncountfunction returns, the cache may have been destroyed.

The wait function can be used during the withdrawal procedure to wait forfscache to finish withdrawing all the objects in the cache. When it completes,there will be no remaining objects referring to the cache object or any volumeobjects.

Cache Management API

The cache backend implements the cache management API by providing a table ofoperations that fscache can use to manage various aspects of the cache. Theseare held in a structure of type:

struct fscache_cache_ops {        const char *name;        ...};

This contains a printable name for the cache backend driver plus a number ofpointers to methods to allow fscache to request management of the cache:

  • Set up a volume cookie [optional]:

    void (*acquire_volume)(struct fscache_volume *volume);

    This method is called when a volume cookie is being created. The callerholds a cache-level access pin to prevent the cache from going away forthe duration. This method should set up the resources to access a volumein the cache and should not return until it has done so.

    If successful, it can setcache_priv to its own data.

  • Clean up volume cookie [optional]:

    void (*free_volume)(struct fscache_volume *volume);

    This method is called when a volume cookie is being released ifcache_priv is set.

  • Look up a cookie in the cache [mandatory]:

    bool (*lookup_cookie)(struct fscache_cookie *cookie);

    This method is called to look up/create the resources needed to access thedata storage for a cookie. It is called from a worker thread with avolume-level access pin in the cache to prevent it from being withdrawn.

    True should be returned if successful and false otherwise. If false isreturned, the withdraw_cookie op (see below) will be called.

    If lookup fails, but the object could still be created (e.g. it hasn’tbeen cached before), then:

    void fscache_cookie_lookup_negative(        struct fscache_cookie *cookie);

    can be called to let the network filesystem proceed and start downloadingstuff whilst the cache backend gets on with the job of creating things.

    If successful,cookie->cache_priv can be set.

  • Withdraw an object without any cookie access counts held [mandatory]:

    void (*withdraw_cookie)(struct fscache_cookie *cookie);

    This method is called to withdraw a cookie from service. It will becalled when the cookie is relinquished by the netfs, withdrawn or culledby the cache backend or closed after a period of non-use by fscache.

    The caller doesn’t hold any access pins, but it is called from anon-reentrant work item to manage races between the various wayswithdrawal can occur.

    The cookie will have theFSCACHE_COOKIE_RETIRED flag set on it if theassociated data is to be removed from the cache.

  • Change the size of a data storage object [mandatory]:

    void (*resize_cookie)(struct netfs_cache_resources *cres,                      loff_t new_size);

    This method is called to inform the cache backend of a change in size ofthe netfs file due to local truncation. The cache backend should make allof the changes it needs to make before returning as this is done under thenetfs inode mutex.

    The caller holds a cookie-level access pin to prevent a race withwithdrawal and the netfs must have the cookie marked in-use to preventgarbage collection or culling from removing any resources.

  • Invalidate a data storage object [mandatory]:

    bool (*invalidate_cookie)(struct fscache_cookie *cookie);

    This is called when the network filesystem detects a third-partymodification or when an O_DIRECT write is made locally. This requeststhat the cache backend should throw away all the data in the cache forthis object and start afresh. It should return true if successful andfalse otherwise.

    On entry, new I O/operations are blocked. Once the cache is in a positionto accept I/O again, the backend should release the block by calling:

    void fscache_resume_after_invalidation(struct fscache_cookie *cookie);

    If the method returns false, caching will be withdrawn for this cookie.

  • Prepare to make local modifications to the cache [mandatory]:

    void (*prepare_to_write)(struct fscache_cookie *cookie);

    This method is called when the network filesystem finds that it is goingto need to modify the contents of the cache due to local writes ortruncations. This gives the cache a chance to note that a cache objectmay be incoherent with respect to the server and may need writing backlater. This may also cause the cached data to be scrapped on laterrebinding if not properly committed.

  • Begin an operation for the netfs lib [mandatory]:

    bool (*begin_operation)(struct netfs_cache_resources *cres,                        enum fscache_want_state want_state);

    This method is called when an I/O operation is being set up (read, writeor resize). The caller holds an access pin on the cookie and must havemarked the cookie as in-use.

    If it can, the backend should attach any resources it needs to keep aroundto the netfs_cache_resources object and return true.

    If it can’t complete the setup, it should return false.

    The want_state parameter indicates the state the caller needs the cacheobject to be in and what it wants to do during the operation:

    • FSCACHE_WANT_PARAMS - The caller just wants to access cacheobject parameters; it doesn’t need to do data I/O yet.

    • FSCACHE_WANT_READ - The caller wants to read data.

    • FSCACHE_WANT_WRITE - The caller wants to write to or resize thecache object.

    Note that there won’t necessarily be anything attached to the cookie’scache_priv yet if the cookie is still being created.

Data I/O API

A cache backend provides a data I/O API by through the netfs library’sstructnetfs_cache_ops attached to astructnetfs_cache_resources by thebegin_operation method described above.

See theNetwork Filesystem Services Library for a description.

Miscellaneous Functions

FS-Cache provides some utilities that a cache backend may make use of:

  • Note occurrence of an I/O error in a cache:

    void fscache_io_error(struct fscache_cache *cache);

    This tells FS-Cache that an I/O error occurred in the cache. Thisprevents any new I/O from being started on the cache.

    This does not actually withdraw the cache. That must be done separately.

  • Note cessation of caching on a cookie due to failure:

    void fscache_caching_failed(struct fscache_cookie *cookie);

    This notes that a the caching that was being done on a cookie failed insome way, for instance the backing storage failed to be created orinvalidation failed and that no further I/O operations should take placeon it until the cache is reset.

  • Count I/O requests:

    void fscache_count_read(void);void fscache_count_write(void);

    These record reads and writes from/to the cache. The numbers aredisplayed in /proc/fs/fscache/stats.

  • Count out-of-space errors:

    void fscache_count_no_write_space(void);void fscache_count_no_create_space(void);

    These record ENOSPC errors in the cache, divided into failures of datawrites and failures of filesystem object creations (e.g. mkdir).

  • Count objects culled:

    void fscache_count_culled(void);

    This records the culling of an object.

  • Get the cookie from a set of cache resources:

    struct fscache_cookie *fscache_cres_cookie(struct netfs_cache_resources *cres)

    Pull a pointer to the cookie from the cache resources. This may return aNULL cookie if no cookie was set.

API Function Reference

enumfscache_cookie_statefscache_cookie_state(structfscache_cookie*cookie)

Read the state of a cookie

Parameters

structfscache_cookie*cookie

The cookie to query

Description

Get the state of a cookie, imposing an ordering between the cookie contentsand the state value. Paired withfscache_set_cookie_state().

void*fscache_get_key(structfscache_cookie*cookie)

Get a pointer to the cookie key

Parameters

structfscache_cookie*cookie

The cookie to query

Description

Return a pointer to the where a cookie’s key is stored.

voidfscache_count_object(structfscache_cache*cache)

Tell fscache that an object has been added

Parameters

structfscache_cache*cache

The cache to account to

Description

Tell fscache that an object has been added to the cache. This prevents thecache from tearing down the cache structure until the object is uncounted.

voidfscache_uncount_object(structfscache_cache*cache)

Tell fscache that an object has been removed

Parameters

structfscache_cache*cache

The cache to account to

Description

Tell fscache that an object has been removed from the cache and will nolonger be accessed. After this point, the cache cookie may be destroyed.

voidfscache_wait_for_objects(structfscache_cache*cache)

Wait for all objects to be withdrawn

Parameters

structfscache_cache*cache

The cache to query

Description

Wait for all extant objects in a cache to finish being withdrawnand go away.