-
Notifications
You must be signed in to change notification settings - Fork 27
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Lesson06: Implement kernel module for test kernel time functions and timers #87
base: Oleg.Khokhlov
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
obj-m+=omod6_kern_time.o | ||
all: | ||
make -C /lib/modules/$(shell uname -r)/build/ M=$(PWD) modules | ||
clean: | ||
make -C /lib/modules/$(shell uname -r)/build/ M=$(PWD) clean |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
#!/bin/bash | ||
|
||
curdir=$(pwd) | ||
cd ~/kernel/linux | ||
./scripts/checkpatch.pl --no-tree --no-signoff --ignore LONG_LINE,LONG_LINE_COMMENT,LONG_LINE_STRING -f $curdir/$1 | ||
cd $curdir |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,216 @@ | ||
// SPDX-License-Identifier: AFL-3.0 | ||
/* | ||
* Copyright (C) 2018 | ||
* Author: Oleg Khokhlov <[email protected]> | ||
* | ||
* SysFS kernel module for test kernel timing functions | ||
* | ||
* doc links: | ||
* | ||
*/ | ||
|
||
#include <linux/init.h> | ||
#include <linux/module.h> | ||
#include <linux/kernel.h> | ||
#include <linux/device.h> | ||
#include <linux/err.h> | ||
#include <linux/uaccess.h> | ||
#include <linux/string.h> | ||
|
||
#include <linux/jiffies.h> | ||
#include <linux/ktime.h> | ||
#include <linux/timer.h> | ||
#include <linux/rtc.h> | ||
#include <linux/delay.h> | ||
|
||
|
||
|
||
//Internal Kernel API (Time Management): | ||
|
||
//1. Implement kernel module with API in sysfs or procfs, which is able to: | ||
// - return time (in seconds) passed since previous read; | ||
// - return absolute time of previous reading; | ||
// - periodically print some message to log, make period adjustable; | ||
|
||
// --------------- MODULE PARAMETERS DESCRIPTION ------------------- | ||
//int iParam; | ||
//module_param(iParam, int, 0); | ||
//MODULE_PARM_DESC(iParam, "iParam: ReturnCode"); | ||
|
||
//char *sParam = "Default"; | ||
//module_param(sParam, charp, 0); | ||
//MODULE_PARM_DESC(sParam, "sParam: string parameter"); | ||
|
||
//------------ GLOBAL MODULE DATA --------------------- | ||
static struct class *attr_class; | ||
|
||
#define SysFS_DIR_NAME "omod6" | ||
#define SysFS_ATTR_NAME "ktime" | ||
|
||
uint32_t ModCallCount; | ||
uint32_t LastCallTimeSec; | ||
uint32_t LastCall_dT; | ||
uint32_t LastCallTicks; | ||
|
||
uint32_t TimerInterval; | ||
uint32_t TimerCounter; | ||
uint32_t TimerStartTick; | ||
|
||
char LogMessage[256]; | ||
|
||
uint32_t GetTimeStamp(void) | ||
{ | ||
return (uint32_t) (ktime_to_ms(ktime_get_real()) / 1000); | ||
} | ||
|
||
uint32_t GetTickCount(void) | ||
{ | ||
return (uint32_t) (get_jiffies_64() * 1000 / HZ); | ||
} | ||
|
||
|
||
//--------------- SysFS read/write functions ---------------------- | ||
|
||
static ssize_t ktime_show(struct class *cl, | ||
struct class_attribute *attr, | ||
char *buf) | ||
{ | ||
int L; | ||
uint32_t TS, dT, T; | ||
struct rtc_time tm; | ||
|
||
T = GetTickCount(); | ||
TS = GetTimeStamp(); | ||
dT = TS - LastCallTimeSec; | ||
|
||
if (dT == 0) | ||
dT = LastCall_dT; | ||
else | ||
LastCall_dT = dT; | ||
|
||
LastCallTicks = T; | ||
|
||
ModCallCount++; | ||
|
||
sprintf(buf, "ktime (%u ticks, %u HZ):\n", (uint32_t)get_jiffies_64(), HZ); | ||
sprintf(strchr(buf, 0), "%u module call\n", ModCallCount); | ||
sprintf(strchr(buf, 0), "%u sec from last call\n", dT); | ||
rtc_time64_to_tm(TS, &tm); | ||
//https://ru.wikipedia.org/wiki/Time.h | ||
sprintf(strchr(buf, 0), "%u sec curr abs time from Epoch (%d.%02d.%02d, %02d:%02d:%02d)\n", TS, | ||
tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec); | ||
sprintf(strchr(buf, 0), "%u sec prev call abs time from Epoch\n", LastCallTimeSec); | ||
sprintf(strchr(buf, 0), "%u ms - Timer Interval\n", TimerInterval); | ||
sprintf(strchr(buf, 0), "%u times timer called\n", TimerCounter); | ||
|
||
LastCallTimeSec = TS; | ||
|
||
L = strlen(buf); | ||
return L; | ||
} | ||
|
||
// Kernel Timer object | ||
struct timer_list MyTimer; | ||
|
||
// | ||
// Kernel Timer Function. Run in interrupt context! Called only once after start/mod | ||
// | ||
void KernelTimerFunc(struct timer_list *t) | ||
{ | ||
uint32_t T, dT; | ||
|
||
TimerCounter++; | ||
T = GetTickCount(); | ||
dT = T - TimerStartTick; | ||
pr_info("omod6 timer[%u]: %s (delay = %u ms)\n", TimerCounter, LogMessage, dT); | ||
} | ||
|
||
|
||
static ssize_t ktime_store(struct class *cl, | ||
struct class_attribute *attr, | ||
const char *buf, size_t count) | ||
{ | ||
int interval = 0; | ||
char *cp; | ||
int n; | ||
|
||
if (sscanf(buf, "%d", &interval) == 1) { | ||
cp = strchr(buf, ' '); | ||
if (cp) { | ||
while (*cp == ' ') | ||
cp++; | ||
for (n = 0; n < sizeof(LogMessage) - 1; n++) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What would be if user enter the wrong/garbage string? Have you tested this case? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No, I haven't tested this case. But as for this code, it's made a priory safe by design. It copies only limited number of chars into message string and is protected against strange ascii codes, e.g. CR/LF, ESC, etc. Which are unwanted in system log. All other ascii chars are OK. This method of string copying was selected after I realized that the 'store' function gets the string from user space which is ended this '\n'. It was a piece of bad news for me. |
||
if (*cp < ' ') | ||
break; | ||
LogMessage[n] = *cp++; | ||
} | ||
LogMessage[n] = 0; | ||
} | ||
} | ||
|
||
pr_info("omod6 store (%d, \'%s\')\n", interval, LogMessage); | ||
|
||
TimerInterval = interval; | ||
|
||
// start, stop or reconfig timer | ||
if (interval == 0) { | ||
if (timer_pending(&MyTimer)) { | ||
del_timer(&MyTimer); | ||
pr_info("omod6 Timer disabled\n"); | ||
} | ||
} else { | ||
unsigned long expired = get_jiffies_64() + interval * HZ / 1000; | ||
mod_timer(&MyTimer, expired); | ||
TimerStartTick = GetTickCount(); | ||
pr_info("omod6 Timer started (interval=%u)\n", interval); | ||
} | ||
return count; | ||
} | ||
|
||
static struct class_attribute class_attr_ktime = { | ||
.attr = { .name = SysFS_ATTR_NAME, .mode = 0666 }, | ||
.show = ktime_show, | ||
.store = ktime_store | ||
}; | ||
|
||
|
||
static int __init omod_init(void) | ||
{ | ||
int ret; | ||
|
||
attr_class = class_create(THIS_MODULE, SysFS_DIR_NAME); | ||
if (attr_class == NULL) { | ||
pr_err("omod6: error creating sysfs class\n"); | ||
return -EEXIST; | ||
} | ||
|
||
ret = class_create_file(attr_class, &class_attr_ktime); | ||
if (ret) { | ||
pr_err("omod6: error creating sysfs class attribute\n"); | ||
class_destroy(attr_class); | ||
return ret; | ||
} | ||
|
||
timer_setup(&MyTimer, KernelTimerFunc, 0); | ||
|
||
pr_info("omod6 Kernel time function test module started.\n"); | ||
LastCallTimeSec = GetTimeStamp(); | ||
return 0; | ||
} | ||
|
||
static void __exit omod_exit(void) | ||
{ | ||
class_remove_file(attr_class, &class_attr_ktime); | ||
class_destroy(attr_class); | ||
|
||
pr_info("omod6 module normal shutdown.\n"); | ||
} | ||
|
||
module_init(omod_init); | ||
module_exit(omod_exit); | ||
|
||
MODULE_LICENSE("GPL"); | ||
MODULE_AUTHOR("Oleg Khokhlov"); | ||
MODULE_DESCRIPTION("OlegH Lesson06 module: test kernel time funcs"); | ||
MODULE_VERSION("0.2"); | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
bash$ | ||
bash$ ./chkpatch.sh omod6_kern_time.c | ||
total: 0 errors, 0 warnings, 204 lines checked | ||
|
||
/home/olegh/gl-kernel-training-2018/06-TimeManagement/omod6_kern_time.c has no obvious style problems and is ready for submission. | ||
|
||
NOTE: Ignored message types: LONG_LINE LONG_LINE_COMMENT LONG_LINE_STRING | ||
bash$ |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,76 @@ | ||
bash$ | ||
bash$ sudo dmesg -C | ||
bash$ sudo insmod omod6_kern_time.ko | ||
bash$ dmesg | ||
[164257.292896] omod6 Kernel time function test module started. | ||
bash$ | ||
bash$ cat /sys/class/omod6/ktime | ||
ktime (40995296 ticks, 250 HZ): | ||
1 module call | ||
24 sec from last call | ||
1542096090 sec curr abs time from Epoch (year:2018) | ||
1542096066 sec prev call abs time from Epoch | ||
0 ms - Timer Interval | ||
0 times timer called | ||
bash$ | ||
bash$ | ||
bash$ cat /sys/class/omod6/ktime | ||
ktime (40997586 ticks, 250 HZ): | ||
2 module call | ||
9 sec from last call | ||
1542096099 sec curr abs time from Epoch (year:2018) | ||
1542096090 sec prev call abs time from Epoch | ||
0 ms - Timer Interval | ||
0 times timer called | ||
bash$ | ||
bash$ | ||
bash$ echo "5000 Hello from timer" > /sys/class/omod6/ktime | ||
bash$ | ||
bash$ dmesg | ||
[164257.292896] omod6 Kernel time function test module started. | ||
[164312.443489] omod6 store (5000, 'Hello') | ||
[164312.443495] omod6 Timer started (interval=5000) | ||
bash$ | ||
bash$ dmesg | ||
[164257.292896] omod6 Kernel time function test module started. | ||
[164312.443489] omod6 store (5000, 'Hello') | ||
[164312.443495] omod6 Timer started (interval=5000) | ||
[164317.680785] omod6 timer[1]: Hello (delay = 5240 ms) | ||
bash$ | ||
bash$ | ||
bash$ cat /sys/class/omod6/ktime | ||
ktime (41007134 ticks, 250 HZ): | ||
3 module call | ||
38 sec from last call | ||
1542096137 sec curr abs time from Epoch (year:2018) | ||
1542096099 sec prev call abs time from Epoch | ||
5000 ms - Timer Interval | ||
1 times timer called | ||
bash$ | ||
bash$ | ||
bash$ echo "10000 Hello from timer2" > /sys/class/omod6/ktime | ||
bash$ echo "0 Hello from timer2" > /sys/class/omod6/ktime | ||
bash$ dmesg | ||
[164257.292896] omod6 Kernel time function test module started. | ||
[164312.443489] omod6 store (5000, 'Hello') | ||
[164312.443495] omod6 Timer started (interval=5000) | ||
[164317.680785] omod6 timer[1]: Hello (delay = 5240 ms) | ||
[164348.845400] omod6 store (10000, 'Hello') | ||
[164348.845406] omod6 Timer started (interval=10000) | ||
[164353.156345] omod6 store (0, 'Hello') | ||
[164353.156350] omod6 Timer disabled | ||
bash$ | ||
bash$ | ||
bash$ cat /sys/class/omod6/ktime | ||
ktime (41018014 ticks, 250 HZ): | ||
4 module call | ||
44 sec from last call | ||
1542096181 sec curr abs time from Epoch (year:2018) | ||
1542096137 sec prev call abs time from Epoch | ||
0 ms - Timer Interval | ||
1 times timer called | ||
bash$ | ||
bash$ | ||
bash$ sudo rmmod omod6_kern_time | ||
bash$ | ||
bash$ |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
bash$ | ||
bash$ ./chkpatch.sh omod6_kern_time.c | ||
WARNING: Prefer kstrto<type> to single variable sscanf | ||
#138: FILE: /home/olegh/gl-kernel-training-2018/06-TimeManagement/omod6_kern_time.c:138: | ||
+ if (sscanf(buf, "%d", &interval) == 1) { | ||
+ cp = strchr(buf, ' '); | ||
+ if (cp) { | ||
+ while (*cp == ' ') | ||
+ cp++; | ||
+ for (n = 0; n < sizeof(LogMessage) - 1; n++) { | ||
+ if (*cp < ' ') | ||
+ break; | ||
+ LogMessage[n] = *cp++; | ||
+ } | ||
+ LogMessage[n] = 0; | ||
+ } | ||
+ } | ||
|
||
total: 0 errors, 1 warnings, 222 lines checked | ||
|
||
NOTE: For some of the reported defects, checkpatch may be able to | ||
mechanically convert to the typical style using --fix or --fix-inplace. | ||
|
||
/home/olegh/gl-kernel-training-2018/06-TimeManagement/omod6_kern_time.c has style problems, please review. | ||
|
||
NOTE: Ignored message types: LONG_LINE LONG_LINE_COMMENT LONG_LINE_STRING | ||
|
||
NOTE: If any of the errors are false positives, please report | ||
them to the maintainer, see CHECKPATCH in MAINTAINERS. | ||
bash$ |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
bash$ | ||
bash$ | ||
bash$ sudo insmod omod6_kern_time.ko | ||
bash$ cat /sys/class/omod6/ktime | ||
ktime (50320923 ticks, 250 HZ): | ||
1 module call | ||
4 sec from last call | ||
1542138138 sec curr abs time from Epoch (2018.11.13, 19:42:18) | ||
1542138134 sec prev call abs time from Epoch | ||
0 ms - Timer Interval | ||
0 times timer called | ||
bash$ | ||
bash$ | ||
bash$ echo "2000 Hello from module timer!" > /sys/class/omod6/ktime | ||
bash$ | ||
bash$ cat /sys/class/omod6/ktime | ||
ktime (50325075 ticks, 250 HZ): | ||
2 module call | ||
17 sec from last call | ||
1542138155 sec curr abs time from Epoch (2018.11.13, 19:42:35) | ||
1542138138 sec prev call abs time from Epoch | ||
2000 ms - Timer Interval | ||
1 times timer called | ||
bash$ | ||
bash$ | ||
bash$ dmesg | ||
[201578.642960] omod6 Kernel time function test module started. | ||
[201593.096793] omod6 store (2000, 'Hello from module timer!') | ||
[201593.096800] omod6 Timer started (interval=2000) | ||
[201595.113490] omod6 timer[1]: Hello from module timer! (delay = 2020 ms) | ||
bash$ | ||
bash$ | ||
bash$ sudo rmmod omod6_kern_time | ||
bash$ | ||
bash$ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
IMO it's better to format absolute time stamp as human readable string aka YY.MM.DD hh:mm:ss
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good idea. I had been thinking about it when I was written the code but I decided that it's extraneous. Maybe you can recomend proper kernel function to convert epoch timestamp to struct time ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see you've managed to format the epoch time. Good job!