How to Use a Seq_File in Linux Kernel Modules

Writable seq_file in Linux Kernel

The helper you are looking for is simple_write_to_buffer.

Given a pointer to the buffer and its available size, the helper processes all parameters passed to the .write function:

ssize_t pfsw_write(struct file *filp, const char __user * user_data, size_t sz, loff_t *off)
{
// This performs the most work.
ssize_t res = simple_write_to_buffer(buf, sizeof(buf), off, user_data, sz);

// Optionally, you may update the buffer's actual size.
// NOTE: In the simple form this doesn't work with **concurrent** writing.

if ((res > 0) && *off > buf_size) {
buf_size = *off;
}

return res;
}

The "pair" for simple_write_to_buffer helper is simple_read_from_buffer. That helper can be used in the implementation of the .read method:

ssize_t pfsw_read(struct file filp*, char __user * user_data, size_t sz, loff_t *off)
{
// Here the helper does the whole work.
//
// 'buf_size' could be 'sizeof(buf)' in case of the buffer of the fixed size.
// Alternatively, you need to take care about concurrency;
// see the comments in the 'pfsw_write' function.
return simple_read_from_buffer(buf, buf_size, off, user_data, sz);
}

Subsystem seq_file is only for implement the reading from the file (.read function).

While seq_read is an actual implementation for the .read function, seq_write is just a helper for .show function in struct seq_operations.

See that my answer for the related question about seq_file and writing into the file.

How to implement a writable proc file by using seq_file in a driver module

After I tied a lot. I found there is actually no seq version write function. However, you can treat /proc file as a normal device file which can be operated by methods defined in file_operations.

reading seq_file from kernel

You can't use fopen as this is a libc function. The example bellow shows how to read a file from the kernel.

http://www.wasm.ru/forum/viewtopic.php?pid=467952#p467952

seq_file not working properly after next returns NULL

There is some incompatibility between Linux kernel 4.x and 5.x versions about handling *pos argument passed to seq_file callback functions.

Linux kernel 5.x only sets *pos to 0 and leave other modifications to the seq_file callbacks (.start and .next functions in struct seq_operations).

However, in Linux kernel 4.x *pos can be incremented by 1 outside of your callbacks. This is done if the last .show call produces exactly number of bytes needed to fulfill request of read syscall.

Because your code is ready only to *pos values which are divisible by BUFFER_STEP, it works incorrectly when *pos is incremented outside of your functions, so it is divisible by BUFFER_STEP no longer.


I don't know whether behavior of Linux 4.x is correct, but if you want your code to work with such behavior, you need to prepare to all values of *pos, not only to ones produced by your code.

Simple fix of your code would be interpret *pos as a chunk index instead of a byte index:

static void* seqf_ex_start (struct seq_file* m, loff_t* pos)
{
if (*pos >= (BUFFER_SIZE/BUFFER_STEP)) {
return NULL;
}

return buffer + (*pos * BUFFER_STEP);
}

static void* seqf_ex_next (struct seq_file* m, void* v, loff_t* pos)
{
*pos += 1;

if (*pos >= (BUFFER_SIZE/BUFFER_STEP)) {
return NULL;
}

return buffer + (*pos * BUFFER_STEP);
}

static int seqf_ex_show (struct seq_file* m, void* v)
{
seq_printf(m, "%.*s\n", BUFFER_STEP, (char*)v);

return 0;
}

Such a way your code will work with all values of *pos. (Those which are too high would be cut off by checks if (*pos >= (BUFFER_SIZE/BUFFER_STEP))). And increment of *pos by 1 performed outside of your code is equivalent to calling your .next callback.

Return value of Linux kernel sequence 'show' routine

Sequential files are described in the kernel documentation, under Documentation/filesystems/seq_file.txt. According to docs, show function should return:

  • 0 on success
  • -ERR (negative error code) on fail
  • SEQ_SKIP for discard any output for current item

Current implementation treats any positive return value of show as SEQ_SKIP:

// (from traverse() function in fs/seq_file.c)
// ...
error = m->op->show(m, p);

if (error < 0) // <-- negative return value is an error indicator
break;
if (unlikely(error)) { // <-- this is for positive return values
error = 0; // <-- as if 'show' returns 0...
m->count = 0; // <-- ... but with output dropped
}

So, if you want to report show to be succeed, just return 0.

Building the Linux /proc file driver with seq_file

It's because there is no struct seq_operation but struct seq_operations



Related Topics



Leave a reply



Submit