There is a typical problem to be solved as mentioned in the below small note:-
>
> ------------------------------ ------------------------------ -------------
> static void t_fun(unsigned long t_arg)
> {
> struct my_dat *data = (struct my_dat *)t_arg;
> atomic_inc(&counter_bh);
> pr_info("In BH: counter_th = %d, counter_bh = %d, jiffies=%ld, %ld\n",
> atomic_read(&counter_th), atomic_read(&counter_bh),
> data->jiffies, jiffies);
> kfree(data);
> }
>
> static irqreturn_t my_interrupt(int irq, void *dev_id)
> {
> struct tasklet_struct *t;
> struct my_dat *data;
>
> data = (struct my_dat *)kmalloc(sizeof(struct my_dat), GFP_ATOMIC);
> t = &data->tsk;
> data->jiffies = jiffies;
>
> tasklet_init(t, t_fun, (unsigned long)data);
>
> atomic_inc(&counter_th);
> tasklet_schedule(t);
> mdelay(delay); /* hoke up a delay to try to cause pileup */
> return IRQ_NONE; /* we return IRQ_NONE because we are just observing */
> }
>
This part of code initializes a new tasklet everytime on the IRQ. Hence each time the processor services the IRQ there is a new tasklet is getting created however the function remains the same. Note:- In traditional drivers usually a single tasklet is used and scheduled multiple times using the IRQ
> module_init(my_generic_init);
> module_exit(my_generic_exit);
>
> ------------------------------ ------------------------------ -------------
>
> Above solution will not miss any bottom halves.
> Can somebody explains me why above solution is working ?
> What is so special about dynamic tasklets ?
The solution works because the new tasklet is created everytime with the same callback function. Note that the function should be fully re-entrant for such use-cases which is done using the local variable - arg/data in this case.
Reference :- http://lists.kernelnewbies.org/pipermail/kernelnewbies/2018-June/019197.html
To solve this problem, a solution is provided using dynamically allocated tasklet as below and explanation is provided as necessary with the code.
>
> ------------------------------
> static void t_fun(unsigned long t_arg)
> {
> struct my_dat *data = (struct my_dat *)t_arg;
> atomic_inc(&counter_bh);
> pr_info("In BH: counter_th = %d, counter_bh = %d, jiffies=%ld, %ld\n",
> atomic_read(&counter_th), atomic_read(&counter_bh),
> data->jiffies, jiffies);
> kfree(data);
> }
>
> static irqreturn_t my_interrupt(int irq, void *dev_id)
> {
> struct tasklet_struct *t;
> struct my_dat *data;
>
> data = (struct my_dat *)kmalloc(sizeof(struct my_dat), GFP_ATOMIC);
> t = &data->tsk;
> data->jiffies = jiffies;
>
> tasklet_init(t, t_fun, (unsigned long)data);
>
> atomic_inc(&counter_th);
> tasklet_schedule(t);
> mdelay(delay); /* hoke up a delay to try to cause pileup */
> return IRQ_NONE; /* we return IRQ_NONE because we are just observing */
> }
>
This part of code initializes a new tasklet everytime on the IRQ. Hence each time the processor services the IRQ there is a new tasklet is getting created however the function remains the same. Note:- In traditional drivers usually a single tasklet is used and scheduled multiple times using the IRQ
> module_init(my_generic_init);
> module_exit(my_generic_exit);
>
> ------------------------------
>
> Above solution will not miss any bottom halves.
> Can somebody explains me why above solution is working ?
> What is so special about dynamic tasklets ?
The solution works because the new tasklet is created everytime with the same callback function. Note that the function should be fully re-entrant for such use-cases which is done using the local variable - arg/data in this case.
Reference :- http://lists.kernelnewbies.org/pipermail/kernelnewbies/2018-June/019197.html

No comments:
Post a Comment