Skip to content
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

Open
wants to merge 3 commits into
base: Oleg.Khokhlov
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions 06-TimeManagement/Makefile
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
6 changes: 6 additions & 0 deletions 06-TimeManagement/chkpatch.sh
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
216 changes: 216 additions & 0 deletions 06-TimeManagement/omod6_kern_time.c
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);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1542096137 sec curr abs time from Epoch (year:2018)

IMO it's better to format absolute time stamp as human readable string aka YY.MM.DD hh:mm:ss

Copy link
Author

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 ?

Copy link
Contributor

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!

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++) {
Copy link
Contributor

Choose a reason for hiding this comment

The 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?

Copy link
Author

@OlegH-ua OlegH-ua Nov 14, 2018

Choose a reason for hiding this comment

The 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");

8 changes: 8 additions & 0 deletions 06-TimeManagement/omod6_kern_time.chklog
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$
76 changes: 76 additions & 0 deletions 06-TimeManagement/omod6_kern_time.log
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$
30 changes: 30 additions & 0 deletions 06-TimeManagement/omod6_kern_time2.chklog
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$
35 changes: 35 additions & 0 deletions 06-TimeManagement/omod6_kern_time2.log
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$