owned this note
owned this note
Published
Linked with GitHub
# 4.x Linux Kernel Procfs Guide
* Louie Lu
* <louie.lu at hopebaytech.com>
* 2016/08/25
* derived from [Linux Kernel Procfs Guide](https://kernelnewbies.org/Documents/Kernel-Docbooks?action=AttachFile&do=get&target=procfs-guide_2.6.29.pdf)
# Chapter 1. Introduction
This is a new `/proc` file system (procfs) introduction for linux kernel.This tutorial will use kernel version 4.8.0 rc-3 for testing code.
# Chapter 2. Managing procfs entries
This chapter we will learn to managing procfs entries.
You'll need to remember to include header file `#include <proc_fs.h>`
## Creating regualr entries
```c
struct proc_dir_entry *proc_create(
const char *name, umode_t mode,
struct proc_dir_entry *parent,
const struct file_operations *proc_fops);
struct proc_dir_entry *proc_create_data(
const char *name, umode_t mode,
struct proc_dir_entry *parent,
const struct file_operations *proc_fops,
void *data);
```
These two function creates a regular file with the name `name`, file mode `mode` in the directory `parent`, and using file operations's method in `proc_fops`. To create a file in the root of the procfs (/proc), use `NULL` as `parent` parameter. When successful, the function will return a pointer to the freshly create struct proc_dir_entry; otherwise it will return `NULL`. Chapter 3 describes how to do something useful with regular files.
There is different between old style `create_proc_entry`, `create_proc_read_entry` compare to `proc_create`, is that now we using `proc_fops` for callback method, not `entry->read_proc` or `entry->write_proc` style.
We will see it at later for example.
## Creating a symlink
```c
struct proc_dir_entry *proc_symlink(const char *name,
struct proc_dir_entry *parent, const char *dest);
```
This creates a symlink in the procfs directory `parent` that points from `name` to `dest`. This translates in userland to `ln -s dest name`.
## Creating a dir
```c
struct proc_dir_entry *proc_mkdir(const char *name,
struct proc_dir_entry *parent);
```
Create a directory with `name` in `parent`.
## Removing entry
```c
void proc_remove(struct proc_dir_entry *de);
void remove_proc_entry(const char *, struct proc_dir_entry *);
int remove_proc_subtree(const char *, struct proc_dir_entry *);
```
`proc_remove` is just a syntactic sugar for `remove_proc_subtree`, you can see it at `/linux/fs/proc/generic.c`, then search for `proc_remove`, you will see this code below:
```c=
void proc_remove(struct proc_dir_entry *de)
{
if (de)
remove_proc_subtree(de->name, de->parent);
}
```
So now, instend of using `remove_proc_entry` for entry in a dir, we can use `proc_remove` for sure.
# Chapter 3. Communicating with userland
Instead of reading (or writing) information directly from kernel memory, procfs works with *call back* functions for files: functions that are called when a specific file is being read or written. Such functionshave to be initialised after the procfs file is created by setting theread_procand/orwrite_procfieldsin the struct proc_dir_entry* that the functioncreate_proc_entryreturned:
We are now using a different method to initialised such functions callback method. In the early days, call back function needs to be setting in `read_proc` `write_proc` in `struct proc_dir_entry` like this.
```c=
...
foo = create_proc_read_entry();
foo->read_proc = foo_read_proc;
foo->write_proc = foo_write_proc;
...
```
Now we using `struct file_operations` to create call back operations.
For example:
```c
static struct file_operations foo_ops = {
.open = foo_read,
.read = seq_read,
.llseek = seq_lseek,
.release = seq_release,
.write = foo_write,
}
```
If you only need read method, simply create by:
```c
static struct file_operations foo_ops = {
.open = foo_read,
.read = seq_read,
.llseek = seq_lseek,
.release = seq_release,
}
```
## Reading data
```c
static int foobar_proc_show(struct seq_file *m, void *v)
{
struct fb_data_t *fb_data = (struct fb_data_t *) m->private;
seq_printf(m, "%s - '%s'\n",
fb_data->name, fb_data->value);
return 0;
}
static int foobar_proc_open(struct inode *inode, struct file *file)
{
single_open(file, foobar_proc_show, PDE_DATA(inode));
return 0;
}
```
## Writing data
```c
static ssize_t foobar_proc_write(struct file *file,
const char __user *buffer, size_t count, loff_t *pos)
{
struct fb_data_t *fb_data;
size_t len;
fb_data = (struct fb_data_t *) PDE_DATA(file_inode(file));
len = min(count, sizeof(fb_data->value) - 1);
if (copy_from_user(fb_data->value, buffer, len))
return -EFAULT;
fb_data->value[len] = '\0';
return len;
}
```
# Chapter 4. Tips and tricks
## Data
`struct proc_dir_entry` is now opaque, so things like `pde->data` are now using different way to access it.
### PDE_DATA
```c
void *PDE_DATA(const struct inode *inode)
```
`PDE_DATA` will return `pde->data`, you can use it in *inode* or *file*
```c
static int foo_open(struct inode *inode, struct file *file)
{
int a = PDE_DATA(inode); // a = 10
int b = PDE_DATA(file_inode(file)); // b = 10
...
}
...
int data = 10;
proc_create_data("foo", 0, NULL, &foo_ops, (void *) data);
```
### proc_set_size
```c
void proc_set_size(struct proc_dir_entry *de, loff_t size)
{
de->size = size;
}
```
### proc_set_user
```c
void proc_set_user(struct proc_dir_entry *de, kuid_t uid, kgid_t gid)
{
de->uid = uid;
de->gid = gid;
}
```
# Chapter 5. Example
```c=
/*
* procfs_4_example.c: an example for 4.x linux proc interface
*
* Copyright (C) 2016, Louie Lu (louie.lu@hopebaytech.com)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/jiffies.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/string.h>
#include <asm/uaccess.h>
#define MODULE_VERS "2.0"
#define MODULE_NAME "procfs_4.x_example"
#define FOOBAR_LEN 8
struct fb_data_t {
char name[FOOBAR_LEN + 1];
char value[FOOBAR_LEN + 1];
};
static struct proc_dir_entry *example_dir, *foo_file,
*bar_file, *jiffies_file, *symlink;
static struct fb_data_t foo_data, bar_data;
static int jiffies_proc_show(struct seq_file *m, void *v)
{
seq_printf(m, "Jiffies: %llu\n", get_jiffies_64());
return 0;
}
static int jiffies_proc_open(struct inode *inode, struct file *file)
{
single_open(file, jiffies_proc_show, NULL);
return 0;
}
static int foobar_proc_show(struct seq_file *m, void *v)
{
struct fb_data_t *fb_data = (struct fb_data_t *) m->private;
seq_printf(m, "%s - '%s'\n",
fb_data->name, fb_data->value);
return 0;
}
static int foobar_proc_open(struct inode *inode, struct file *file)
{
single_open(file, foobar_proc_show, PDE_DATA(inode));
return 0;
}
static ssize_t foobar_proc_write(struct file *file,
const char __user *buffer, size_t count, loff_t *pos)
{
struct fb_data_t *fb_data;
size_t len;
fb_data = (struct fb_data_t *) PDE_DATA(file_inode(file));
len = min(count, sizeof(fb_data->value) - 1);
if (copy_from_user(fb_data->value, buffer, len))
return -EFAULT;
fb_data->value[len] = '\0';
return len;
}
static struct file_operations jiffies_ops = {
.open = jiffies_proc_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
static struct file_operations foobar_ops = {
.open = foobar_proc_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
.write = foobar_proc_write,
};
static int __init init_procfs_example(void)
{
int rv = 0;
/* create directory */
example_dir = proc_mkdir(MODULE_NAME, NULL);
if (example_dir == NULL) {
rv = -ENOMEM;
goto out;
}
/* create jiffies */
jiffies_file = proc_create("jiffies", 0644,
example_dir, &jiffies_ops);
if (jiffies_file == NULL) {
rv = -ENOMEM;
goto no_jiffies;
}
/* create foo and bar files using same callback
* functions
*/
strncpy(foo_data.name, "foo", 3);
strncpy(foo_data.value, "foov", 4);
foo_file = proc_create_data("foo", 0644,
example_dir, &foobar_ops,
(void *) &foo_data);
if (foo_file == NULL) {
rv = -ENOMEM;
goto no_foo;
}
strncpy(bar_data.name, "bar", 3);
strncpy(bar_data.value, "barv", 4);
bar_file = proc_create_data("bar", 0644,
example_dir, &foobar_ops,
(void *) &bar_data);
if (bar_file == NULL) {
rv = -ENOMEM;
goto no_bar;
}
/* create symlink */
symlink = proc_symlink("jiffies_too", example_dir,
"jiffies");
if (symlink == NULL) {
rv = -ENOMEM;
goto no_symlink;
}
/* everything DONE */
printk(KERN_INFO "%s %s initialised\n",
MODULE_NAME, MODULE_VERS);
return 0;
no_symlink:
remove_proc_entry("bar", example_dir);
no_bar:
remove_proc_entry("foo", example_dir);
no_foo:
remove_proc_entry("jiffies", example_dir);
no_jiffies:
remove_proc_entry(MODULE_NAME, NULL);
out:
return rv;
}
static void __exit cleanup_procfs_example(void)
{
// remove_proc_entry("jiffies_too", example_dir);
// remove_proc_entry("bar", example_dir);
// remove_proc_entry("foo", example_dir);
// remove_proc_entry("jiffies", example_dir);
// remove_proc_entry(MODULE_NAME, NULL);
proc_remove(example_dir);
printk(KERN_INFO "%s %s removed\n",
MODULE_NAME, MODULE_VERS);
}
module_init(init_procfs_example);
module_exit(cleanup_procfs_example);
MODULE_DESCRIPTION("procfs 4.x example");
MODULE_AUTHOR("Louie Lu");
MODULE_LICENSE("GPL");
```
# Chapter 6. Pieces in kernel
# Chapter 7. further reading
* [jit.c](https://github.com/duxing2007/ldd3-examples-3.x/blob/master/misc-modules/jit.c)
* [adding proc files to your modules](http://crashcourse.ca/introduction-linux-kernel-programming/lesson-11-adding-proc-files-your-modules-part-1)
* https://lwn.net/Articles/549477/