Skip to content

Commit

Permalink
configfs: implement binary attributes
Browse files Browse the repository at this point in the history
ConfigFS lacked binary attributes up until now. This patch
introduces support for binary attributes in a somewhat similar
manner of sysfs binary attributes albeit with changes that
fit the configfs usage model.

Problems that configfs binary attributes fix are everything that
requires a binary blob as part of the configuration of a resource,
such as bitstream loading for FPGAs, DTBs for dynamically created
devices etc.

Look at Documentation/filesystems/configfs/configfs.txt for internals
and howto use them.

This patch is against linux-next as of today that contains
Christoph's configfs rework.

Signed-off-by: Pantelis Antoniou <pantelis.antoniou@konsulko.com>
[hch: folded a fix from Geert Uytterhoeven <geert+renesas@glider.be>]
[hch: a few tiny updates based on review feedback]
Signed-off-by: Christoph Hellwig <hch@lst.de>
  • Loading branch information
pantoniou authored and RobertCNelson committed Aug 23, 2016
1 parent 122113d commit eb80c3c
Show file tree
Hide file tree
Showing 6 changed files with 374 additions and 22 deletions.
57 changes: 48 additions & 9 deletions Documentation/filesystems/configfs/configfs.txt
Expand Up @@ -51,15 +51,27 @@ configfs tree is always there, whether mounted on /config or not.
An item is created via mkdir(2). The item's attributes will also
appear at this time. readdir(3) can determine what the attributes are,
read(2) can query their default values, and write(2) can store new
values. Like sysfs, attributes should be ASCII text files, preferably
with only one value per file. The same efficiency caveats from sysfs
apply. Don't mix more than one attribute in one attribute file.

Like sysfs, configfs expects write(2) to store the entire buffer at
once. When writing to configfs attributes, userspace processes should
first read the entire file, modify the portions they wish to change, and
then write the entire buffer back. Attribute files have a maximum size
of one page (PAGE_SIZE, 4096 on i386).
values. Don't mix more than one attribute in one attribute file.

There are two types of configfs attributes:

* Normal attributes, which similar to sysfs attributes, are small ASCII text
files, with a maximum size of one page (PAGE_SIZE, 4096 on i386). Preferably
only one value per file should be used, and the same caveats from sysfs apply.
Configfs expects write(2) to store the entire buffer at once. When writing to
normal configfs attributes, userspace processes should first read the entire
file, modify the portions they wish to change, and then write the entire
buffer back.

* Binary attributes, which are somewhat similar to sysfs binary attributes,
but with a few slight changes to semantics. The PAGE_SIZE limitation does not
apply, but the whole binary item must fit in single kernel vmalloc'ed buffer.
The write(2) calls from user space are buffered, and the attributes'
write_bin_attribute method will be invoked on the final close, therefore it is
imperative for user-space to check the return code of close(2) in order to
verify that the operation finished successfully.
To avoid a malicious user OOMing the kernel, there's a per-binary attribute
maximum buffer value.

When an item needs to be destroyed, remove it with rmdir(2). An
item cannot be destroyed if any other item has a link to it (via
Expand Down Expand Up @@ -171,6 +183,7 @@ among other things. For that, it needs a type.
struct configfs_item_operations *ct_item_ops;
struct configfs_group_operations *ct_group_ops;
struct configfs_attribute **ct_attrs;
struct configfs_bin_attribute **ct_bin_attrs;
};

The most basic function of a config_item_type is to define what
Expand Down Expand Up @@ -201,6 +214,32 @@ be called whenever userspace asks for a read(2) on the attribute. If an
attribute is writable and provides a ->store method, that method will be
be called whenever userspace asks for a write(2) on the attribute.

[struct configfs_bin_attribute]

struct configfs_attribute {
struct configfs_attribute cb_attr;
void *cb_private;
size_t cb_max_size;
};

The binary attribute is used when the one needs to use binary blob to
appear as the contents of a file in the item's configfs directory.
To do so add the binary attribute to the NULL-terminated array
config_item_type->ct_bin_attrs, and the item appears in configfs, the
attribute file will appear with the configfs_bin_attribute->cb_attr.ca_name
filename. configfs_bin_attribute->cb_attr.ca_mode specifies the file
permissions.
The cb_private member is provided for use by the driver, while the
cb_max_size member specifies the maximum amount of vmalloc buffer
to be used.

If binary attribute is readable and the config_item provides a
ct_item_ops->read_bin_attribute() method, that method will be called
whenever userspace asks for a read(2) on the attribute. The converse
will happen for write(2). The reads/writes are bufferred so only a
single read/write will occur; the attributes' need not concern itself
with it.

[struct config_group]

A config_item cannot live in a vacuum. The only way one can be created
Expand Down
14 changes: 12 additions & 2 deletions fs/configfs/configfs_internal.h
Expand Up @@ -53,13 +53,14 @@ struct configfs_dirent {
#define CONFIGFS_ROOT 0x0001
#define CONFIGFS_DIR 0x0002
#define CONFIGFS_ITEM_ATTR 0x0004
#define CONFIGFS_ITEM_BIN_ATTR 0x0008
#define CONFIGFS_ITEM_LINK 0x0020
#define CONFIGFS_USET_DIR 0x0040
#define CONFIGFS_USET_DEFAULT 0x0080
#define CONFIGFS_USET_DROPPING 0x0100
#define CONFIGFS_USET_IN_MKDIR 0x0200
#define CONFIGFS_USET_CREATING 0x0400
#define CONFIGFS_NOT_PINNED (CONFIGFS_ITEM_ATTR)
#define CONFIGFS_NOT_PINNED (CONFIGFS_ITEM_ATTR | CONFIGFS_ITEM_BIN_ATTR)

extern struct mutex configfs_symlink_mutex;
extern spinlock_t configfs_dirent_lock;
Expand All @@ -72,6 +73,8 @@ extern struct inode * configfs_new_inode(umode_t mode, struct configfs_dirent *,
extern int configfs_create(struct dentry *, umode_t mode, void (*init)(struct inode *));

extern int configfs_create_file(struct config_item *, const struct configfs_attribute *);
extern int configfs_create_bin_file(struct config_item *,
const struct configfs_bin_attribute *);
extern int configfs_make_dirent(struct configfs_dirent *,
struct dentry *, void *, umode_t, int);
extern int configfs_dirent_is_ready(struct configfs_dirent *);
Expand All @@ -88,7 +91,7 @@ extern void configfs_release_fs(void);
extern struct rw_semaphore configfs_rename_sem;
extern const struct file_operations configfs_dir_operations;
extern const struct file_operations configfs_file_operations;
extern const struct file_operations bin_fops;
extern const struct file_operations configfs_bin_file_operations;
extern const struct inode_operations configfs_dir_inode_operations;
extern const struct inode_operations configfs_root_inode_operations;
extern const struct inode_operations configfs_symlink_inode_operations;
Expand Down Expand Up @@ -119,6 +122,13 @@ static inline struct configfs_attribute * to_attr(struct dentry * dentry)
return ((struct configfs_attribute *) sd->s_element);
}

static inline struct configfs_bin_attribute *to_bin_attr(struct dentry *dentry)
{
struct configfs_attribute *attr = to_attr(dentry);

return container_of(attr, struct configfs_bin_attribute, cb_attr);
}

static inline struct config_item *configfs_get_config_item(struct dentry *dentry)
{
struct config_item * item = NULL;
Expand Down
18 changes: 17 additions & 1 deletion fs/configfs/dir.c
Expand Up @@ -255,6 +255,12 @@ static void configfs_init_file(struct inode * inode)
inode->i_fop = &configfs_file_operations;
}

static void configfs_init_bin_file(struct inode *inode)
{
inode->i_size = 0;
inode->i_fop = &configfs_bin_file_operations;
}

static void init_symlink(struct inode * inode)
{
inode->i_op = &configfs_symlink_inode_operations;
Expand Down Expand Up @@ -423,7 +429,9 @@ static int configfs_attach_attr(struct configfs_dirent * sd, struct dentry * den
spin_unlock(&configfs_dirent_lock);

error = configfs_create(dentry, (attr->ca_mode & S_IALLUGO) | S_IFREG,
configfs_init_file);
(sd->s_type & CONFIGFS_ITEM_BIN_ATTR) ?
configfs_init_bin_file :
configfs_init_file);
if (error) {
configfs_put(sd);
return error;
Expand Down Expand Up @@ -583,6 +591,7 @@ static int populate_attrs(struct config_item *item)
{
struct config_item_type *t = item->ci_type;
struct configfs_attribute *attr;
struct configfs_bin_attribute *bin_attr;
int error = 0;
int i;

Expand All @@ -594,6 +603,13 @@ static int populate_attrs(struct config_item *item)
break;
}
}
if (t->ct_bin_attrs) {
for (i = 0; (bin_attr = t->ct_bin_attrs[i]) != NULL; i++) {
error = configfs_create_bin_file(item, bin_attr);
if (error)
break;
}
}

if (error)
detach_attrs(item);
Expand Down

0 comments on commit eb80c3c

Please sign in to comment.