systemtap failed to probe the functions. Registration error - linux-kernel

systemtap registration error.
WARNING: probe process("/home/user/a.out").function("func").return inode-offset 00000000468ed0c6 registration error (rc -5)
WARNING: probe process("/home/user/a.out").function("func").call inode-offset 00000000468ed0c6 registration error (rc -5)
WARNING: task_finder mmap inode-uprobes callback for task 28532 failed: -5
I am learning systemtap. I have a process which calls a function in a while loop. When I start systemtap using "stap -v test.stp" to probe the userspace function, I get the registration error. Following is the complete screen shot;
Pass 1: parsed user script and 465 library scripts using 112640virt/48788res/6452shr/42636data kb, in 100usr/20sys/123real ms.
Pass 2: analyzed script: 3 probes, 2 functions, 4 embeds, 3 globals using 114256virt/51968res/7840shr/44252data kb, in 50usr/110sys/162real ms.
Pass 3: using cached /root/.systemtap/cache/66/stap_662fe7689c5fb5d6ef569e8246fa1c8a_3296.c
Pass 4: using cached /root/.systemtap/cache/66/stap_662fe7689c5fb5d6ef569e8246fa1c8a_3296.ko
Pass 5: starting run.
WARNING: probe process("/home/admin/a.out").function("func").return inode-offset 00000000468ed0c6 registration error (rc 0)
WARNING: probe process("/home/admin/a.out").function("func").call inode-offset 00000000468ed0c6 registration error (rc 0)
^CERROR: empty aggregate near operator '#max' at test.stp:6:37
WARNING: Number of errors: 1, skipped probes: 0
WARNING: /usr/bin/staprun exited with status: 1
Pass 5: run completed in 0usr/20sys/9318real ms.
Pass 5: run failed. [man error::pass5]
test.stp
probe process("/home/user/a.out").function("func").return {
stats <<< gettimeofday_ns() - #entry(gettimeofday_ns())
}
probe end {
printf("max/avg/min: %d/%d/%d\n", #max(stats), #avg(stats), #min(stats))
print(#hist_log(stats))
}
global stats
test.c
#include <stdlib.h>
#include <unistd.h>
void func()
{
printf("Hello\n");
sleep(1);
}
int main()
{
while (1)
{
func();
}
}

systemtap does not support overlays/union filesystems. The systemtap userspace code has to be changed to get the real inode of a file if it is in overlayfs. For this the systemtap need to be code changed and built. Download systemtap source code make changes in the file uprobes-inode.c . The change is to use the d_backing_inode to find inode. Need to make changes in two places.
inode_1 = d_backing_inode(d_real((struct dentry *) dentry, NULL, 0, 0)); //use inode_1 in the following function.
if ((vm_flags & VM_EXEC) && !(vm_flags & VM_WRITE))
rc = stapiu_change_plus(target, task, addr, length,
offset, vm_flags, inode_1);
// offset, vm_flags, dentry->d_inode);
vm_file = stap_find_exe_file(mm);
if (vm_file) {
if (vm_file->f_path.dentry)
{
//inode = vm_file->f_path.dentry->d_inode;
inode = d_backing_inode(d_real((struct dentry *) vm_file->f_path.dentry, NULL, 0, 0));
}
fput(vm_file);

Related

How to access user space function arguments with libbpf?

I am trying to instrument a user space nginx function by using libbpf. I am able to attach a uprobe it, and print pid, tid and so on from the probe. However, I am having great issues whenever I try to parse function argument data. I have been able to do this with bpftrace but am unable to do so with libbpf. My question is, how to properly access and print arguments of the user space function I want to trace?
nginx.bpf.c
#include "ngx_http.h"
#include <linux/bpf.h>
#include <bpf/bpf_helpers.h>
#include <bpf/bpf_core_read.h>
char LICENSE[] SEC("license") = "Dual BSD/GPL";
SEC("uprobe//usr/sbin/nginx:ngx_http_finalize_request")
int handle_ngx_http_finalize_request(struct ngx_http_request_s* r, ngx_int_t rc)
{
u_char *s_ptr;
u_char str[128];
int err;
err = bpf_probe_read_user(&s_ptr, sizeof(s_ptr), &r->request_line.data);
if (!s_ptr || err < 0) {
bpf_printk("Error %d\n", err);
return -2;
}
bpf_probe_read_user_str(str, sizeof(str), &s_ptr);
bpf_printk("String: %s\n", str);
return 0;
}
Whenever I try to parse the function arguments the bpf_probe_read_user returns error -14. When I try to use bpf_core_read the verifier rejects the code with following error.
❯ sudo ./nginx
libbpf: loading object 'nginx_bpf' from buffer
libbpf: elf: section(3) uprobe//usr/sbin/nginx:ngx_http_finalize_request, size 280, link 0, flags 6, type=1
libbpf: sec 'uprobe//usr/sbin/nginx:ngx_http_finalize_request': found program 'handle_ngx_http_finalize_request' at insn offset 0 (0 bytes), code size 35 insns (280 bytes)
libbpf: elf: section(4) .reluprobe//usr/sbin/nginx:ngx_http_finalize_request, size 32, link 12, flags 40, type=9
libbpf: elf: section(5) license, size 13, link 0, flags 3, type=1
libbpf: license of nginx_bpf is Dual BSD/GPL
libbpf: elf: section(6) .rodata, size 22, link 0, flags 2, type=1
libbpf: elf: section(7) .BTF, size 12720, link 0, flags 0, type=1
libbpf: elf: section(9) .BTF.ext, size 252, link 0, flags 0, type=1
libbpf: elf: section(12) .symtab, size 240, link 1, flags 0, type=2
libbpf: looking for externs among 10 symbols...
libbpf: collected 0 externs total
libbpf: map 'nginx_bp.rodata' (global data): at sec_idx 6, offset 0, flags 80.
libbpf: map 0 is "nginx_bp.rodata"
libbpf: sec '.reluprobe//usr/sbin/nginx:ngx_http_finalize_request': collecting relocation for section(3) 'uprobe//usr/sbin/nginx:ngx_http_finalize_request'
libbpf: sec '.reluprobe//usr/sbin/nginx:ngx_http_finalize_request': relo #0: insn #13 against '.rodata'
libbpf: prog 'handle_ngx_http_finalize_request': found data map 0 (nginx_bp.rodata, sec 6, off 0) for insn 13
libbpf: sec '.reluprobe//usr/sbin/nginx:ngx_http_finalize_request': relo #1: insn #28 against '.rodata'
libbpf: prog 'handle_ngx_http_finalize_request': found data map 0 (nginx_bp.rodata, sec 6, off 0) for insn 28
libbpf: loading kernel BTF '/sys/kernel/btf/vmlinux': 0
libbpf: map 'nginx_bp.rodata': created successfully, fd=4
libbpf: sec 'uprobe//usr/sbin/nginx:ngx_http_finalize_request': found 1 CO-RE relocations
libbpf: prog 'handle_ngx_http_finalize_request': relo #0: <byte_off> [2] typedef ngx_http_request_t.request_line.data (0:21:1 # offset 992)
libbpf: prog 'handle_ngx_http_finalize_request': relo #0: no matching targets found
libbpf: prog 'handle_ngx_http_finalize_request': relo #0: substituting insn #1 w/ invalid insn
libbpf: prog 'handle_ngx_http_finalize_request': BPF program load failed: Invalid argument
libbpf: prog 'handle_ngx_http_finalize_request': -- BEGIN PROG LOAD LOG --
R1 type=ctx expected=fp
; int handle_ngx_http_finalize_request(ngx_http_request_t* r, ngx_int_t rc)
0: (bf) r3 = r1
1: <invalid CO-RE relocation>
failed to resolve CO-RE relocation <byte_off> [2] typedef ngx_http_request_t.request_line.data (0:21:1 # offset 992)
processed 2 insns (limit 1000000) max_states_per_insn 0 total_states 0 peak_states 0 mark_read 0
-- END PROG LOAD LOG --
libbpf: prog 'handle_ngx_http_finalize_request': failed to load: -22
libbpf: failed to load object 'nginx_bpf'
libbpf: failed to load BPF skeleton 'nginx_bpf': -22
Failed to open and load BPF skeleton
Here is bpftrace code that works.
nginx.bt
uprobe:/usr/sbin/nginx:ngx_http_finalize_request
{
$req = (struct ngx_http_request_s*)arg0;
printf("Request Line: %s\n", str($req->request_line.data));
}
So I would like to parse data and do some custom logic depending on it.
Here are the ngx_http_request_s and the ngx_str_t structs for the curious.
EDIT
User space code:
#include <stdio.h>
#include <unistd.h>
#include <sys/resource.h>
#include <bpf/libbpf.h>
#include "nginx.skel.h"
static int libbpf_print_fn(enum libbpf_print_level level, const char *format, va_list args)
{
return vfprintf(stderr, format, args);
}
int main(int argc, char **argv)
{
struct nginx_bpf *skel;
int err;
libbpf_set_strict_mode(LIBBPF_STRICT_ALL);
libbpf_set_print(libbpf_print_fn);
skel = nginx_bpf__open_and_load();
if (!skel) {
fprintf(stderr, "Failed to open and load BPF skeleton\n");
return 1;
}
err = nginx_bpf__attach(skel);
if (err) {
fprintf(stderr, "Failed to auto-attach BPF skeleton: %d\n", err);
goto cleanup;
}
printf("Successfully started!\n");
for (;;) {
sleep(1);
}
cleanup:
nginx_bpf__destroy(skel);
return -err;
}
You are actually not defining BPF program correctly. You should do something like the below. Note that you also shouldn't use CO-RE variants of bpf_core_read() or BPF_CORE_READ(), use BPF_PROBE_READ_USER() instead:
SEC("uprobe//usr/sbin/nginx:ngx_http_finalize_request")
int BPF_KPROBE(handle_ngx_http_finalize_request,
struct ngx_http_request_s* r, ngx_int_t rc)
{
u_char *s_ptr;
u_char str[128];
int err;
/* you can access rc directly now, btw */
s_ptr = BPF_PROBE_READ_USER(r, request_line.data);
/* note no dereferencing of s_ptr above */
bpf_probe_read_user_str(str, sizeof(str), s_ptr);
bpf_printk("String: %s\n", str);
return 0;
You don't have to use BPF_PROBE_READ_USER() macro, you can do the same with just bpf_probe_read_user() like you did in your example. BPF_PROBE_READ_USER() will be especially handy if you need to follow few levels of pointers, though.
But there is no CO-RE for user-space types, it's only for kernel types, because kernel provides BTF information to allow relocating offsets properly.

Can I delete a named semaphore created with sem_open?

I am experimenting with the named semaphores and there is something that I don't understand about the behavior of sem_close and sem_destroy. In my example, it looks like the semaphore that I create does not get deleted.
#include <iostream>
#include <semaphore.h>
int main() {
char sem_name[] = "/sem-1";
{
sem_t *sptr = sem_open(sem_name, O_CREAT, 0644, 0);
if (sptr != SEM_FAILED) {
printf("sem_open success\n");
// neither of these works
sem_close(sptr);
// sem_destroy(sptr);
} else {
printf("sem_open error #1: %s\n", strerror(errno));
}
}
sem_t *sptr = sem_open(sem_name, O_CREAT | O_EXCL, 0644, 0);
printf("sem_open error #2: %s\n", strerror(errno));
assert(sptr != SEM_FAILED);
return 0;
}
The output:
sem_open success
sem_open error #2: File exists
Assertion failed: (sptr != SEM_FAILED), function main, file /tmp/delete_me/main.cpp, line 22.
I would expect the assertion to not be hit and also the error #2 to not shown.
Additionally, macOS says that the sem_destroy method is deprecated but I am still using both sem_close() and sem_destroy but they both seem to not delete the semaphore.
Background: I am trying to port NASA Core Flight System to macOS. Their POSIX implementation uses sem_ calls and I am trying to understand if I can make them work on macOS with some minor modifications.
Since macOS only recognizes named semaphores, using sem_destroy() won't work. sem_destroy() destroys an unnamed semaphore at the address pointed to by sem. Only a semaphore that has been initialized by sem_init() should be
destroyed using sem_destroy().
You could likely overcome this issue by using sem_unlink:
...
sem_close(sptr);
sem_unlink(sem_name);
...
SEM_UNLINK(2)
NAME
sem_unlink -- remove a named semaphore
SYNOPSIS
#include <semaphore.h>
int sem_unlink(const char *name);
DESCRIPTION
The named semaphore named name is removed. If the semaphore is in use by
other processes, then name is immediately disassociated with the sema-phore, semaphore,
phore, but the semaphore itself will not be removed until all references
to it have been closed. Subsequent calls to sem_open() using name will
refer to or create a new semaphore named name.
If successful, `sem_unlink()` will return 0. Otherwise, -1 is returned and
errno is set, and the state of the semaphore is unchanged.
ERRORS
sem_unlink() succeeds unless:
[EACCES] Permission is denied to be remove the semaphore.
[ENAMETOOLONG] name exceeded SEM_NAME_LEN characters.
[ENOENT] The named semaphore does not exist.
↳ macOS Manual Pages | sem_unlink()

fwrite doesn't fail when disk is full?

here is a test program I wrote
int main( int argc, const char* argv[] )
{
const char name[1024] = "/dev/shm/test_file";
off_t len = atol(argv[argc - 1]);
char buf[1024];
FILE * f = fopen(name, "w");
for (int i = 0; i < len; i++) {
int ret = fwrite(buf, 1024, 1, f);
if (ret != 1) {
printf("disk full\n");
}
}
if ( fclose(f) != 0)
printf("failed to close\n");
return 0;
}
I tried to fill the /dev/shm to almost full
tmpfs 36G 36G 92K 100% /dev/shm
and ran
$ ./a.out 93
failed to close
my glibc
$ /lib/libc.so.6
GNU C Library stable release version 2.12, by Roland McGrath et al.
the kernel version is 2.6.32-642.13.1.el6.x86_64
I understand that this behavior is caused by fwrite try to cache the data in memory. (I tried setvbuf(NULL...) and fwrite immediately return failure). But this seems a little different from the definition
The fwrite() function shall return the number of elements successfully
written, which may be less than nitems if a write error is
encountered. If size or nitems is 0, fwrite() shall return 0 and the
state of the stream remains unchanged. Otherwise, if a write error
occurs, the error indicator for the stream shall be set, [CX] [Option
Start] and errno shall be set to indicate the error. [Option End]
The data was not successfully written to disk however its return value is 1. no errno set.
In this test case, the fclose catch the failure. But it could be caught by even a ftell function which is quite confusing.
I am wondering if this happens to all versions of glibc and would this be consider a bug.
The data was not successfully written to disk
The standard doesn't talk about the disk. It talks about data being successfully written to the stream (which it has been).
I am wondering if this happens to all versions of glibc
Most likely.
and would this be consider a bug.
It's a bug in your interpretation of the requirements on fwrite.

Runtime error : Segmentation fault with libtommath and libtomcrypt

I am trying to run sample rsa/dsa code using libtomcrypt.
I have installed LibTomMath first as make install, as a result following files are created.
/usr/lib/libtommath.a
/usr/include/tommath.h
After that I installed libtomcrypt with LibTomMath as external library
CFLAGS="-DLTM_DESC -DUSE_LTM -I/usr/include" EXTRALIBS="/usr/lib/libtommath.a " make install
As a result following file is created
/usr/lib/libtomcrypt.a
I am not getting any error while running following command
CFLAGS="-DLTM_DESC -DUSE_LTM -I/usr/include" EXTRALIBS="/usr/lib/libtommath.a " make test
I have gone through this document libtomcrypt_installation and libtomcrypt_resolved to successfully compile using
gcc -DLTM_DESC rsa_make_key_example.c -o rsa -ltomcrypt
or
gcc rsa_make_key_example.c -o rsa -ltomcrypt
no compile error. However when I try to run, I got following error.
./rsa
LTC_ARGCHK 'ltc_mp.name != NULL' failure on line 34 of file src/pk/rsa/rsa_make_key.c
Aborted
Here is my sample rsa code
#include <tomcrypt.h>
#include <stdio.h>
int main(void) {
# ifdef USE_LTM
ltc_mp = ltm_desc;
# elif defined (USE_TFM)
ltc_mp = tfm_desc;
# endif
rsa_key key;
int err;
register_prng(&sprng_desc);
if ((err = rsa_make_key(NULL, find_prng("sprng"), 1024/8, 65537,&key)) != CRYPT_OK) {
printf("make_key error: %s\n", error_to_string(err));
return -1;
}
/* use the key ... */
return 0;
}
Here is my sample dsa code
#include <tomcrypt.h>
#include <stdio.h>
int main(void) {
# ifdef USE_LTM
ltc_mp = ltm_desc;
# elif defined (USE_TFM)
ltc_mp = tfm_desc;
# endif
int err;
register_prng(&sprng_desc);
dsa_key key;
if ((err = dsa_make_key(NULL, find_prng("sprng"), 20, 128,&key)) != CRYPT_OK) {
printf("make_key error: %s\n", error_to_string(err));
return -1;
}
/* use the key ... */
return 0;
}
Here is how I have compiled it successfully,
gcc dsa_make_key_example.c -o dsa -ltomcrypt
When I try to run the code , I am getting following error .
./dsa
segmentation fault
EDIT 1:
I investigated further and found the reason for segmentation fault
#ifdef LTC_MPI
#include <stdarg.h>
int ltc_init_multi(void **a, ...)
{
...
...
if (mp_init(cur) != CRYPT_OK) ---> This line causes segmentation fault
Where am I making mistakes ? How to resolve this problem to run these programs successfully?
I am using linux , gcc. Any help/link will be highly appreciated. Thanks in advance.
It's been a year or so since this was asked, but I have some component of an answer, and a workaround.
The reason mp_init fails is that the "math_descriptor" is uninitialized. mp_init is a defined as
#define mp_init(a) ltc_mp.init(a)
where ltc_mp is a global struct (of type ltc_math_descriptor) that holds pointers to the math routines.
There are several implementations of the math routines available, and a user can choose which they want. For whatever reason, there does not seem to be a default math implementation chosen for certain builds of libtomcrypt. Thus, the init member of ltc_mp is null, and we get the SIGSEGV.
Here is a manual workaround:
You can make your desired ltc_math_descriptor struct available to your main() routine by #defineing one of
LTM_DESC -- built-in math lib
TFM_DESC -- an external fast math package
GMP_DESC -- presumably a GNU MultiPrecision implementation?
Before #include <tomcrypt.h> (or by using -D on the command-line).
Whichever you choose, a corresponding object will be declared:
extern const ltc_math_descriptor ltm_desc;
extern const ltc_math_descriptor tfm_desc;
extern const ltc_math_descriptor gmp_desc;
To use it, manually copy it to the global math descriptor:
E.g., in my case, for the local math imlpementation,
ltc_mp = ltm_desc;
Now libtomcrypt works.

Using an old device file for char device driver

I have two questions as I'm trying device drivers as a beginner.
I created one module , loaded it, it dynamically took major number 251 say. Number of minor devices is kept 1 only i.e minor number 0. For testing , I tried echo and cat on the device file (created using mknod) and it works as expected. Now if I unload the module but don't remove /dev entry and again load the module with same major number and try writing/reading to same device file which was used previously, kernel crashes. I know we shouldn't do this but just want to understand what happens in this scenario which causes this crash. I think something that VFS does.
When I do cat on device file, the read keeps on happening indefinitely. why? To stop that needed to use offset manipulation. This looks to be because buffer length is coming as 32768 as default to read?
EDIT: further in this I added one ioctl function as below, then I'm getting error regarding the storage class of init and cleanup function, which work well if no ioctl is defined. Not getting the link between ioctl and the init/cleanup functions' storage class. Updated code is posted. Errors are below:
/home/diwakar/Documents/my_modules/first_test_module/flowTest.c:95:12: error: invalid storage class for function ‘flow_init’
/home/diwakar/Documents/my_modules/first_test_module/flowTest.c: In function ‘flow_init’:
/home/diwakar/Documents/my_modules/first_test_module/flowTest.c:98:2: warning: ISO C90 forbids mixed declarations and code [-Wdeclaration-after-statement]
/home/diwakar/Documents/my_modules/first_test_module/flowTest.c: In function ‘flow_ioctl’:
/home/diwakar/Documents/my_modules/first_test_module/flowTest.c:112:13: error: invalid storage class for function ‘flow_terminate’
/home/diwakar/Documents/my_modules/first_test_module/flowTest.c:119:1: error: invalid storage class for function ‘__inittest’
/home/diwakar/Documents/my_modules/first_test_module/flowTest.c:119:1: warning: ‘alias’ attribute ignored [-Wattributes]
/home/diwakar/Documents/my_modules/first_test_module/flowTest.c:120:1: error: invalid storage class for function ‘__exittest’
/home/diwakar/Documents/my_modules/first_test_module/flowTest.c:120:1: warning: ISO C90 forbids mixed declarations and code [-Wdeclaration-after-statement]
/home/diwakar/Documents/my_modules/first_test_module/flowTest.c:120:1: warning: ‘alias’ attribute ignored [-Wattributes]
/home/diwakar/Documents/my_modules/first_test_module/flowTest.c:120:1: error: expected declaration or statement at end of input
/home/diwakar/Documents/my_modules/first_test_module/flowTest.c: At top level:
/home/diwakar/Documents/my_modules/first_test_module/flowTest.c:73:13: warning: ‘flow_ioctl’ defined but not used [-Wunused-function]
Below is the code:
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <asm/uaccess.h>
#include <linux/cdev.h>
#include <linux/kdev_t.h>
#include <linux/errno.h>
#include <linux/ioctl.h>
#define SUCCESS 0
#define BUF_LEN 80
#define FLOWTEST_MAGIC 'f'
#define FLOW_QUERY _IOR(FLOWTEST_MAGIC,1,int)
MODULE_LICENSE("GPL");
int minor_num=0,i;
int num_devices=1;
int fopen=0,counter=0,ioctl_test;
static struct cdev ms_flow_cd;
static char c;
///// Open , close and rest of the things
static int flow_open(struct inode *f_inode, struct file *f_file)
{
printk(KERN_ALERT "flowtest device: OPEN\n");
return SUCCESS;
}
static ssize_t flow_read(struct file *f_file, char __user *buf, size_t
len, loff_t *off)
{
printk(KERN_INFO "flowtest Driver: READ()\nlength len=%d, Offset = %d\n",len,*off);
/* Check to avoid the infinitely printing on screen. Return 1 on first read, and 0 on subsequent read */
if(*off==1)
return 0;
printk(KERN_INFO "Copying...\n");
copy_to_user(buf,&c,1);
printk(KERN_INFO "Copied : %s\n",buf);
*off = *off+1;
return 1; // Return 1 on first read
}
static ssize_t flow_write(struct file *f_file, const char __user *buf,
size_t len, loff_t *off)
{
printk(KERN_INFO "flowtest Driver: WRITE()\n");
if (copy_from_user(&c,buf+len-2,1) != 0)
return -EFAULT;
else
{
printk(KERN_INFO "Length len = %d\n\nLast character written is - %c\n",len,*(buf+len-2));
return len;
}
}
static int flow_close(struct inode *i, struct file *f)
{
printk(KERN_INFO "ms_tty Device: CLOSE()\n");
return 0;
}
///* ioctl commands *///
static long flow_ioctl (struct file *filp,unsigned int cmd, unsigned long arg)
{
switch(cmd) {
case FLOW_QUERY:
ioctl_test=51;
return ioctl_test;
default:
return -ENOTTY;
}
///////////////////File operations structure below/////////////////////////
struct file_operations flow_fops = {
.owner = THIS_MODULE,
.llseek = NULL,
.read = flow_read,
.write = flow_write,
.unlocked_ioctl = flow_ioctl,
.open = flow_open,
.release = flow_close
};
static int flow_init(void)
{
printk(KERN_ALERT "Here with flowTest module ... loading...\n");
int result=0;
dev_t dev=0;
result = alloc_chrdev_region(&dev, minor_num,
num_devices,"mod_flowtest"); // allocate major number dynamically.
i=MAJOR(dev);
printk(KERN_ALERT "Major allocated = %d",i);
cdev_init(&ms_flow_cd,&flow_fops);
cdev_add(&ms_flow_cd,dev,1);
return 0;
}
static void flow_terminate(void)
{
dev_t devno=MKDEV(i,0); // wrap major/minor numbers in a dev_t structure , to pass for deassigning.
printk(KERN_ALERT "Going out... exiting...\n");
unregister_chrdev_region(devno,num_devices); //remove entry from the /proc/devices
}
module_init(flow_init);
module_exit(flow_terminate);
1- You're missing cdev_del() in your cleanup function. Which means the device stays registered, but the functions to handle it are unloaded, thus the crash. Also, cdev_add probably fails on the next load, but you don't know because you're not checking return values.
2- It looks ok... you modify offset, return the correct number of bytes, and then return 0 if offset is 1, which indicates EOF. But you should really check for *off >= 1.
EDIT-
The length passed into your read handler function comes all the way from user-land read(). If the user opens the device file and calls read(fd, buf, 32768);, that just means the user wants to read up to 32768 bytes of data. That length gets passed all the way to your read handler. If you don't have 32768 bytes of data to supply, you supply what you have, and return the length. Now, the user code isn't sure if that's the end of the file or not, so it tries for another 32768 read. You really have no data now, so you return 0, which tells the user code that it has hit EOF, so it stops.
In summary, what you're seeing as some sort of default value at the read handler is just the block size that the utility cat uses to read anything. If you want to see a different number show up at your read function, try using dd instead, since it lets you specify the block size.
dd if=/dev/flowtest of=/dev/null bs=512 count=1
In addition, this should read one block and stop, since you're specifying count=1. If you omit count=1, it will look more like cat, and try to read until EOF.
For 2, make sure you start your module as a char device when using mknod.
mknod /dev/you_device c major_number minor_number

Resources