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
Linux, Gnu Gcc, Ld, Version Scripts and the Elf Binary Format -- How Does It Work
What Are Some Conditions That May Cause Fork() or System() Calls to Fail on Linux
How to Acess the Physical Address from Linux Kernel Space
Bluetooth Low Energy:Android Gatt-Client Connect to Linux Gatt Server
Arm Assembly "Retne" Instruction
How to Capture All the Commands Typed in Unix/Linux by Any User
How to Initialize the Attribute Group Correctly for a Platform Driver
Cannot Make Bash Script Work from Cloud-Init
Cannot Run Rake Db:Migrate, Relation Does Not Exist
Why Non-Pic Code Can't Be Totally Aslr Using Run-Time Fixups
Bash: No Such File or Directory
Shutdown (Embedded) Linux from Kernel-Space
Pkill Returns 255 in Combination with Another Command via Remote Ssh
Why Does If [ !$(Grep -Q) ] Not Work When If Grep -Q Does
How to Install Haskell Platform on Linux Debian Wheezy
Bash Script to Run a Constant Number of Jobs in the Background