[C Linux Kernel] Completion opnieuw initen

Pagina: 1
Acties:

Onderwerpen


Acties:
  • 0 Henk 'm!

  • Enfer
  • Registratie: Februari 2004
  • Laatst online: 04-09 19:14
Hi,

Ik zit met een klein probleempje.. Ik ben bezig een device driver voor linux te schrijven voor een character device. Er zijn bepaalde lezers die input kunnen lezen zodra schrijvers iets wegschrijven in het (virtuele device).

Echter wil ik graag dat lezers alleen input gaan lezen als dat er ook echt is, vandaar dat ik een completion gebruik, zodat een schrijver de lezers "wakker" maakt als er werkt te doen is.

Zo gezegd zo gedaan, kom je op het volgende stukje uit: (includes en inits daargelaten)
C:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
#include <linux/completion.h>

struct completion messagesAvailable; //voor lezers om te slapen als er geen berichten zijn

/File operation functies
int rn_open(struct inode *inode, struct file *filp){
    return 0;
}

ssize_t rn_read(struct file *filp, char __user *userBuff, size_t count, loff_t *offp){
    printk( KERN_INFO  "waiting for: %p\n", &messagesAvailable );
    wait_for_completion( &messagesAvailable);   

    copy_to_user( userBuff, "b", 1);
    (*offp) += 1;

    return 1;
}

ssize_t rn_write(struct file *filp, const char __user *userBuff, size_t count, loff_t *offp){
    complete_all( &messagesAvailable);
    printk( KERN_INFO  "inited complete for: %p\n", &messagesAvailable );
    return count;
}

//deze functies worden doorgelinked door gebruikers van het device
struct file_operations fops =
{
    .open = rn_open,
    .release = rn_release,
    .read = rn_read,
    .write = rn_write,
};

// Module ingeladen door de kernel
static int hello_init(void)
{
    int result;

    printk( KERN_ALERT "Kernelmodule completion: init_module() \n" );

    init_completion(&messagesAvailable);

    result  = register_chrdev(0, "completion", &fops);

    if( result < 0) {
        printk(KERN_WARNING "Error creating device\n");
        return result;
    }
    
    device = result;

    return 0;
}


Een schrijver maakt nu alle lezers wakker als er iets geschreven wordt. Echter vervolgens gaat een lezer weer iets lezen, maar wacht hij niet meer op de completion.

Nu ben ik er al achtergekomen dat dit komt omdat je de completion opnieuw moet initialiseren, echter zit daar ook mijn probleem.
Zodra ik de completion opnieuw initialiseer nadat de schrijver een complete_all() gedaan heeft(zo dus):
C:
1
2
complete_all( &messagesAvailable);
init_completion( &messagesAvailable );

krijgen mijn lezers niet meer het bericht dat ze verder mogen lezen.

Ik heb een beetje lopen zoeken op internet, maar er zijn maar weinig cases waar ik complete_all echt gebruikt zie worden, terwijl dit mij op deze plek wel de oplossing lijkt.

Kunnen jullie me vertellen waar/hoe ik mijn completion opnieuw moet initen, of dat ik toch maar beter een ander concurrency-hulpmiddel kan gebruiken? :P

[ Voor 11% gewijzigd door Enfer op 10-08-2010 17:51 ]


Acties:
  • 0 Henk 'm!

  • Elijan9
  • Registratie: Februari 2004
  • Laatst online: 10-09 21:08
Dit klinkt mij meer alsof je blocking IO opnieuw wilt uitvinden, of mis ik iets?
Verder moet je misschien nog voorkomen dat de lezers al opnieuw in rn_read komen voordat de rn_write klaar is met complete_all, maar misschien is dat al wel geregeld via complete_all?

War is when the young and stupid are tricked by the old and bitter into killing each other. - Niko Bellic


Acties:
  • 0 Henk 'm!

  • Enfer
  • Registratie: Februari 2004
  • Laatst online: 04-09 19:14
Jup, het voorbeeld is natuurlijk vereenvoudigd maar het principe van completion is:
als je wait doet ga je wachten totdat je een signal krijgt (complete of complete_all), bij een complete maakt de schrijver 1 iemand wakker, bij een complete_all maakt de schrijver alle wachtenden wakker..

Wat betreft blocking IO, de bedoeling is dat als er een schrijver een bericht stuurt, dat alle wachtenden (0...N) wakker gemaakt worden. Dat kan nu 1x, maar na de complete_all is die completion onbruikbaar, en init_completion aanroepen reset alles waardoor de wachters uberhaupt nooit het signal van complete_all binnenkrijgen..

Acties:
  • 0 Henk 'm!

  • Sappie
  • Registratie: September 2000
  • Laatst online: 14-08 16:46

Sappie

De Parasitaire Capaciteit!

Is het niet zo dat er een verschil bestaat tussen init_completion en INIT_COMPLETION? Als ik me niet vergis dient de laatste gebruikt te worden om de completion te herinitialiseren.

De init_completion call doet volgens mij namelijk wat meer dan de upper-case variant; het reset ook de onderliggende waitqueue (wat dan weer een soort linked list is). Ik ken de onderliggende implementatie van een completion niet, maar kan me voorstellen dat op deze manier de threads die wachten op completion niet meer ge-complete worden.

Specs | Audioscrobbler


Acties:
  • 0 Henk 'm!

  • Soultaker
  • Registratie: September 2000
  • Laatst online: 02:44
Een completion is bedoelt als one time event. Een beetje als een condition variable die niet gereset wordt na het waiten. Als je 'm meer dan eens initialiseert ben je verkeerd bezig, zeker als je dat doet terwijl er nog readers op aan het wachten zijn.

Ik denk dat je hier dus beter een ander soort synchronisatieprimitieve kunt gebruiken. Als elke schrijfctie correspondeert met precies één leesactie, dan kun je een semaphore gebruiken. Als na elke schrijfactie alle readers iets mogen lezen, is een condition variable (wait_event()/wake_up_all()) een betere optie.

In elk geval is een completion in dit geval de verkeerde datastructuur voor wat je wil bereiken.

  • Enfer
  • Registratie: Februari 2004
  • Laatst online: 04-09 19:14
Sappie schreef op woensdag 11 augustus 2010 @ 19:17:
Is het niet zo dat er een verschil bestaat tussen init_completion en INIT_COMPLETION? Als ik me niet vergis dient de laatste gebruikt te worden om de completion te herinitialiseren.

De init_completion call doet volgens mij namelijk wat meer dan de upper-case variant; het reset ook de onderliggende waitqueue (wat dan weer een soort linked list is). Ik ken de onderliggende implementatie van een completion niet, maar kan me voorstellen dat op deze manier de threads die wachten op completion niet meer ge-complete worden.
Klopt, echter heb ik beide varianten geprobeerd, maar bij beide varianten blijft het probleem bestaan. INIT_blabla zet idd van een completion de "done" var weer op 0, terwijl bij init_completion inderdaad de waitqueue (oid) gereset hebt, dat klopt :)
Soultaker schreef op woensdag 11 augustus 2010 @ 20:20:
Een completion is bedoelt als one time event. Een beetje als een condition variable die niet gereset wordt na het waiten. Als je 'm meer dan eens initialiseert ben je verkeerd bezig, zeker als je dat doet terwijl er nog readers op aan het wachten zijn.

Ik denk dat je hier dus beter een ander soort synchronisatieprimitieve kunt gebruiken. Als elke schrijfctie correspondeert met precies één leesactie, dan kun je een semaphore gebruiken. Als na elke schrijfactie alle readers iets mogen lezen, is een condition variable (wait_event()/wake_up_all()) een betere optie.

In elk geval is een completion in dit geval de verkeerde datastructuur voor wat je wil bereiken.
Thanks, ik zal direct even kijken naar de condition_variabele met wait e.d, daar kan ik wat mee :Y)

Echter over je middelste opmerking, na een schrijfactie precies 1 leesactie, is het dan niet veiliger om een completion te gebruiken met "gewoon" complete? Van een completion ben je er zeker dat het FIFO is, van semaphore weet ik dat zo niet. Zo verklein je de kans op starvation?

[ Voor 27% gewijzigd door Enfer op 12-08-2010 11:06 ]


Acties:
  • 0 Henk 'm!

  • H!GHGuY
  • Registratie: December 2002
  • Niet online

H!GHGuY

Try and take over the world...

Soultaker schreef op woensdag 11 augustus 2010 @ 20:20:
Een completion is bedoelt als one time event. Een beetje als een condition variable die niet gereset wordt na het waiten. Als je 'm meer dan eens initialiseert ben je verkeerd bezig, zeker als je dat doet terwijl er nog readers op aan het wachten zijn.

Ik denk dat je hier dus beter een ander soort synchronisatieprimitieve kunt gebruiken. Als elke schrijfctie correspondeert met precies één leesactie, dan kun je een semaphore gebruiken. Als na elke schrijfactie alle readers iets mogen lezen, is een condition variable (wait_event()/wake_up_all()) een betere optie.

In elk geval is een completion in dit geval de verkeerde datastructuur voor wat je wil bereiken.
Als ik me niet vergis worden semaphores in kernel land afgeraden. Als ik me niet vergis staan ze zelfs op de lijst van gewenste removals... Veel kernel developers raden aan om mutexes te gebruiken ipv semaphoren.

Maar wait_event() en wake_up_all() is inderdaad de correcte interface om te gebruiken voor dit soort dingen.

ASSUME makes an ASS out of U and ME


Acties:
  • 0 Henk 'm!

  • Enfer
  • Registratie: Februari 2004
  • Laatst online: 04-09 19:14
H!GHGuY schreef op vrijdag 13 augustus 2010 @ 12:58:
[...]


Als ik me niet vergis worden semaphores in kernel land afgeraden. Als ik me niet vergis staan ze zelfs op de lijst van gewenste removals... Veel kernel developers raden aan om mutexes te gebruiken ipv semaphoren.

Maar wait_event() en wake_up_all() is inderdaad de correcte interface om te gebruiken voor dit soort dingen.
Ermm, is een mutex niet gewoon een semaphore met size 1? Volgens mij wordt een mutex gedeclareerd als:
C:
1
struct semaphore test;

En geinit met
C:
1
DECLARE_MUTEX(test);

Acties:
  • 0 Henk 'm!

  • H!GHGuY
  • Registratie: December 2002
  • Niet online

H!GHGuY

Try and take over the world...

Kijk maar even in kernel/semaphore.c en kernel/mutex.c.

De implementaties verschillen wel degelijk en zijn zelfs niet in termen van elkaar geimplementeerd.

Bovendien is een binaire semaphoor en een mutex niet hetzelfde. Een mutex heeft een owner-thread, waarbij lock en unlock door dezelfde thread moet gebeuren, terwijl dit bij een semaphoor niet zo is.

definieren gebeurt trouwens met
C:
1
2
3
4
5
6
7
DEFINE_MUTEX(mutexname);
// of
DECLARE_MUTEX(mutexname);
void some_init(void)
{
  mutex_init(mutexname);
}

[ Voor 94% gewijzigd door H!GHGuY op 13-08-2010 17:39 ]

ASSUME makes an ASS out of U and ME


Acties:
  • 0 Henk 'm!

  • farlane
  • Registratie: Maart 2000
  • Laatst online: 12:40
Raar, ook ik meende gezien te hebben ( in driver code ) dat de mutex functies deprecated waren en in semaphore termen werden geimplementeerd.

Als ik het goed begrijp moet dat in een single threaded omgeving dan ook mogelijk zijn?

Somniferous whisperings of scarlet fields. Sleep calling me and in my dreams i wander. My reality is abandoned (I traverse afar). Not a care if I never everwake.


Acties:
  • 0 Henk 'm!

  • H!GHGuY
  • Registratie: December 2002
  • Niet online

H!GHGuY

Try and take over the world...

farlane schreef op vrijdag 13 augustus 2010 @ 23:04:
Raar, ook ik meende gezien te hebben ( in driver code ) dat de mutex functies deprecated waren en in semaphore termen werden geimplementeerd.

Als ik het goed begrijp moet dat in een single threaded omgeving dan ook mogelijk zijn?
In single-threaded heb je helemaal niets nodig... (Tenzij misschien wat memory barriers op architecturen met memory re-ordering).

Mutex zal zeker niet deprecated worden, gezien de real-time patches en co. Mocht je me zo'n driver kunnen tonen, please do so ;)

ASSUME makes an ASS out of U and ME


Acties:
  • 0 Henk 'm!

  • farlane
  • Registratie: Maart 2000
  • Laatst online: 12:40
H!GHGuY schreef op zaterdag 14 augustus 2010 @ 11:45:
In single-threaded heb je helemaal niets nodig... (Tenzij misschien wat memory barriers op architecturen met memory re-ordering).
Ugh ... volgende keer zal ik m'n brein weer aanzetten :D
Mutex zal zeker niet deprecated worden, gezien de real-time patches en co. Mocht je me zo'n driver kunnen tonen, please do so ;)
Zoekende

Somniferous whisperings of scarlet fields. Sleep calling me and in my dreams i wander. My reality is abandoned (I traverse afar). Not a care if I never everwake.


Acties:
  • 0 Henk 'm!

  • farlane
  • Registratie: Maart 2000
  • Laatst online: 12:40
Beter laat dan nooit : de situatie was iets anders ben ik nu achter : init_MUTEX was een macro om sem_init( .., 1 ), maar die macro werd gedeprecated omdat er ook een 'echte' mutex is.

Zie ook bv hier : http://www.cs.fsu.edu/~ba...ude/linux/semaphore.h#L39

Somniferous whisperings of scarlet fields. Sleep calling me and in my dreams i wander. My reality is abandoned (I traverse afar). Not a care if I never everwake.

Pagina: 1