2011-07-31 9 views
2

access_okが成功したにもかかわらず、__put_userが例外を引き起こすように、次のコード(read charドライバの実装)では、wait_event_interruptibleの間にMMU TLBエントリが変更される可能性がありますか?Linuxカーネル、ユーザ空間のバッファ、access_okを実行して待機して競合状態を作成しますか?

ユーザバッファをロックして、リクエストの間有効であるようにすることはできますか?

access_okのチェックを繰り返したら、wait_event_interruptibleの返品は安全ですか?

ssize_t mydriver_pkt_read(struct file* filp, char __user* const buff, size_t count, loff_t* offp) 
{ 
    struct mydriver_pkt_private* priv; 
    volatile unsigned short* iobase; 
    unsigned next; 
    char __user* p = buff; 

    if (count <= 0) return -EINVAL; 
    if (!access_ok(VERIFY_WRITE, buff, count)) return -EFAULT; 

    priv = (struct mydriver_pkt_private*)filp->private_data; 
    iobase = priv->iobase; 

    next = priv->retained; 
    if ((next & PKTBUF_FLAG_NOTEMPTY) == 0) { 
    next = ioread16(iobase); 
    if ((next & PKTBUF_FLAG_NOTEMPTY) == 0) { // no data, start blocking read 
     iowrite16(1, iobase); // enable interrupts 
     if (wait_event_interruptible(priv->wait_for_ringbuffer, (priv->retained & PKTBUF_FLAG_NOTEMPTY))) 
      return -ERESTARTSYS; 
     next = priv->retained; 
    } 
    } 

    while (count > 0) { 
    __put_user((char)next, p); 
    p++; 
    count--; 
    next = ioread16(iobase); 
    if ((next & PKTBUF_FLAG_STARTPKT) || !(next & PKTBUF_FLAG_NOTEMPTY)) { 
     priv->retained = next; 
     return (p - buff); 
    } 
    } 

    /* discard remainder of packet */ 
    do { 
    next = ioread16(iobase); 
    } while ((next & PKTBUF_FLAG_NOTEMPTY) && !(next & PKTBUF_FLAG_STARTPKT)); 
    priv->retained = next; 
    return (p - buff); 
} 

独占オープンコード:

int mydriver_pkt_open(struct inode* inode, struct file* filp) 
{ 
    struct mydriver_pkt_private* priv; 

    priv = container_of(inode->i_cdev, struct mydriver_pkt_private, cdevnode); 

    if (atomic_cmpxchg(&priv->inuse, 0, 1)) 
    return -EBUSY; 

    nonseekable_open(inode, filp); 

    filp->private_data = priv; 
    return 0; 
} 
+0

ところで、ドライバをセマフォで保護したい場合があります。 2つのプロセスが1回の読み取りで競合するとどうなりますか?それは、私が彼らがデバイス側の割り込みを無効にしてスリープ状態にするような方法で競争できるように私に見える... – bdonlan

+0

@bdonlan:これは組み込みシステムです:1つのプロセッサ、1つのハードウェアスレッド。また、デバイスのインスタンスは1つしかありません(オープン時に実行、編集を参照)。私は 'fork'やスレッドを使って同時にリクエストを得ることは可能だと思いますが、ユーザモードのアプリケーションはマルチスレッドではありません。私はおそらくそれをどうにかスレッドセーフにする必要があります。 –

+0

ああ、排他的に開いていれば、それは問題ではないと思います。 – bdonlan

答えて

3

あなたが開催されたmm_semセマフォを持っている場合を除き、ページテーブルは、異なるプロセッサからページをアンマップと同じプロセスの他のスレッドによって(随時変更、またはによってできますページ再利用プロセスからの退去)。あなたは寝る必要はありません。たとえあなたがプリエンプションを無効にしても、TLBのシュートダウン割り込みが到着できる限り、それは起こり得る。また、たとえ明示的なTLBフラッシュがなくてもページテーブルの更新が反映されることがあるように、SMPマシンを使用している場合は割り込みが無効になっていても発生する可能性があります。

access_ok()は、アドレス範囲does not overlap with kernel spaceのみをチェックします。したがって、たとえブロックしても、ページテーブルのエントリがアクセスを許可するかどうかはわかりませんが、結果は変わりません。アクセスが拒否された場合、__put_user()-EFAULTを返します。これはユーザ空間に伝播する必要があります(つまり、-EFAULTでエラーになります)。

put_user()__put_user()の唯一の違いは、put_user()access_ok()チェックを実行することだけです。したがって、ループで使用している場合は、access_ok()を事前に実行し、__put_user()を使用するのがおそらく適切です。

関連する問題