I have recently be reading up on how to write Linux Device Drivers and came across a Device Driver which basically "handles" IRQ1 (a keyboard event). The code itself is shown below:
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/interrupt.h>
#include <asm/io.h>
/* This function services keyboard interrupts */
irq_handler_t irq_handler (int irq, void *dev_id, struct pt_regs *regs) {
static unsigned char scancode;
/* Read keyboard status */
scancode = inb (0x60);
if ((scancode == 0x01) || (scancode == 0x81)) {
printk("You pressed Esc !\n");
}
return (irq_handler_t) IRQ_HANDLED;
}
/* Initialize the module and Register the IRQ handler */
static int __init keybrd_int_register(void) {
int result;
/* Request IRQ 1, the keyboard IRQ */
result = request_irq (
1,
(irq_handler_t) irq_handler,
IRQF_SHARED,
"keyboard_stats_irq",
(void *)(irq_handler)
);
if (result) {
printk(KERN_INFO "can't get shared interrupt for keyboard\n");
}
return result;
}
/* Remove the interrupt handler */
static void __exit keybrd_int_unregister(void) {
free_irq(1, (void *)(irq_handler)); /* can't pass NULL, this is a shared interrupt handler! */
}
MODULE_LICENSE ("GPL");
module_init(keybrd_int_register);
module_exit(keybrd_int_unregister);
I would like to understand whether this Device Driver is considered an "Interrupt Service Routine"? Correct me if I'm wrong, but the flow of events is as follows:
Press key on Keyboard
Keyboard Triggers IRQ1
Operating System looks up ALL the Interrupt Service Routines "registered" to IRQ1 (which includes the Device Driver we wrote above) and triggers the code in these Interrupt Service Routines (including the code in Device Driver above).
Does this mean tat the Device Driver IS the Interrupt Service Routine?
A follow up question would be, how would you actually write a real Keyboard Driver (not one that just naively prints out some value)? How do you tell the Operating System that a particular key was pressed and that it should inform the current running application that this key was pressed? What does the C code look like for this?
I am trying to write an interrupt handler for LS1021A external interrupt IRQ5 using NXP QorIQ SDK 2.0. I have created a kernel module to handle the interrupt but the reset_irq function is always returning -22 (Invalid argument).
This is my code:
static unsigned int irqNum;
static irq_handler_t irq5_handler(unsigned int irq, void *dev_id, struct pt_regs *regs){
printk(KERN_INFO "Interrupt received\n");
return (irq_handler_t) IRQ_HANDLED;
}
static int __init irq_init(void){
int result = 0;
irqNum = 201; // IRQ number of IRQ5
result = request_irq(irqNum, (irq_handler_t) irq5_handler, IRQF_TRIGGER_RISING, "irq5_handler", NULL);
printk(KERN_INFO "request_irq result is %d\n", result);
return result;
}
module_init(irq_init);
Is creating a kernel module the right approach to handle the interrupt?
Do I need to perform any actions like initializing the processor pin before requesting the interrupt (request_irq)?
I use a GPIO Expander like PCA9575.
But I don't know how to handle an interrupt from the expander..
From the gpio-pca953x.c, the following code is the ISR for the expander.
static irqreturn_t pca953x_irq_handler(int irq, void *devid)
{
struct pca953x_chip *chip = devid;
uint16_t pending;
uint16_t level;
pending = pca953x_irq_pending(chip);
if (!pending)
return IRQ_HANDLED;
do {
level = __ffs(pending);
handle_nested_irq(level + chip->irq_base);
pending &= ~(1 << level);
} while (pending);
return IRQ_HANDLED;
}
If Interrupt is activated from GPIO(Input) pin 3 of expander, this routine jumps into the GPIO pin 3 service routine from this ISR. Isn't it right?
If so, I have the following question..
How to register the GPIO Pin 3 service routine??
If not, How to handle the the GPIO Pin 3 service routine??
Thanks for your reading.
Is there a way to set CPU affinity for unbound workqueue's kthreads (those that named kthread/uXX:y)? Something like cpu mask for regular workqueues.
Is it a good idea to set it for each kthread using taskset?
Workqueue subsystem exports sysfs attribute for setting cpu affinity for unbound workers.
Code can be found in Workqueue.c:
5040 static ssize_t wq_unbound_cpumask_store(struct device *dev,
5041 struct device_attribute *attr, const char *buf, size_t count)
5042 {
5043 cpumask_var_t cpumask;
5044 int ret;
5045
5046 if (!zalloc_cpumask_var(&cpumask, GFP_KERNEL))
5047 return -ENOMEM;
5048
5049 ret = cpumask_parse(buf, cpumask);
5050 if (!ret)
5051 ret = workqueue_set_unbound_cpumask(cpumask);
5052
5053 free_cpumask_var(cpumask);
5054 return ret ? ret : count;
5055 }
5056
5057 static struct device_attribute wq_sysfs_cpumask_attr =
5058 __ATTR(cpumask, 0644, wq_unbound_cpumask_show,
5059 wq_unbound_cpumask_store)
So any user space application can write to sysfs descriptor to set unbound workqueue cpu mask.
I hope this answers your query.
I'd like to specify the cpu-affinity of a particular pthread. All the references I've found so far deal with setting the cpu-affinity of a process (pid_t) not a thread (pthread_t). I tried some experiments passing pthread_t's around and as expected they fail. Am I trying to do something impossible? If not, can you send a pointer please? Thanks a million.
This is a wrapper I've made to make my life easier. Its effect is that the calling thread gets "stuck" to the core with id core_id:
// core_id = 0, 1, ... n-1, where n is the system's number of cores
int stick_this_thread_to_core(int core_id) {
int num_cores = sysconf(_SC_NPROCESSORS_ONLN);
if (core_id < 0 || core_id >= num_cores)
return EINVAL;
cpu_set_t cpuset;
CPU_ZERO(&cpuset);
CPU_SET(core_id, &cpuset);
pthread_t current_thread = pthread_self();
return pthread_setaffinity_np(current_thread, sizeof(cpu_set_t), &cpuset);
}
Assuming linux:
The interface to setting the affinity is - as you've probably already discovered:
int sched_setaffinity(pid_t pid,size_t cpusetsize,cpu_set_t *mask);
Passing 0 as the pid, and it'll apply to the current thread only, or have other threads report their kernel pid with the linux-specific call pid_t gettid(void); and pass that in as the pid.
Quoting the man page
The affinity mask is actually a per-thread attribute that can be
adjusted independently for each of the
threads in a thread group. The value
returned from a call to gettid(2) can
be passed in the argument pid.
Specifying pid as 0 will set the
attribute for the calling thread, and
passing the value returned from a call
to getpid(2) will set the attribute
for the main thread of the thread
group. (If you are using the POSIX
threads API, then use
pthread_setaffinity_np (3) instead of
sched_setaffinity().)
//compilation: gcc -o affinity affinity.c -lpthread
#define _GNU_SOURCE
#include <sched.h> //cpu_set_t , CPU_SET
#include <pthread.h> //pthread_t
#include <stdio.h>
void *th_func(void * arg);
int main(void) {
pthread_t thread; //the thread
pthread_create(&thread,NULL,th_func,NULL);
pthread_join(thread,NULL);
return 0;
}
void *th_func(void * arg)
{
//we can set one or more bits here, each one representing a single CPU
cpu_set_t cpuset;
//the CPU we whant to use
int cpu = 2;
CPU_ZERO(&cpuset); //clears the cpuset
CPU_SET( cpu , &cpuset); //set CPU 2 on cpuset
/*
* cpu affinity for the calling thread
* first parameter is the pid, 0 = calling thread
* second parameter is the size of your cpuset
* third param is the cpuset in which your thread will be
* placed. Each bit represents a CPU
*/
sched_setaffinity(0, sizeof(cpuset), &cpuset);
while (1);
; //burns the CPU 2
return 0;
}
In POSIX environment you can use cpusets to control
which CPUs can be used by processes or pthreads.
This type of control is called CPU affinity.
The function 'sched_setaffinity' receives pthread IDs and
a cpuset as parameter.
When you use 0 in the first parameter, the calling thread
will be affected
Please find the below example program to cpu-affinity of a particular pthread.
Please add appropriate libs.
double waste_time(long n)
{
double res = 0;
long i = 0;
while (i <n * 200000) {
i++;
res += sqrt(i);
}
return res;
}
void *thread_func(void *param)
{
unsigned long mask = 1; /* processor 0 */
/* bind process to processor 0 */
if (pthread_setaffinity_np(pthread_self(), sizeof(mask),
&mask) <0) {
perror("pthread_setaffinity_np");
}
/* waste some time so the work is visible with "top" */
printf("result: %f\n", waste_time(2000));
mask = 2; /* process switches to processor 1 now */
if (pthread_setaffinity_np(pthread_self(), sizeof(mask),
&mask) <0) {
perror("pthread_setaffinity_np");
}
/* waste some more time to see the processor switch */
printf("result: %f\n", waste_time(2000));
}
int main(int argc, char *argv[])
{
pthread_t my_thread;
if (pthread_create(&my_thread, NULL, thread_func, NULL) != 0) {
perror("pthread_create");
}
pthread_exit(NULL);
}
Compile above program with -D_GNU_SOURCE flag.
The scheduler will change the cpu affinity as it sees fit; to set it persistently please see cpuset in /proc file system.
http://man7.org/linux/man-pages/man7/cpuset.7.html
Or you can write a short program that sets the cpu affinity periodically (every few seconds) with sched_setaffinity