待ち合わせ処理

待ち合わせ処理とは

 非同期による遅延処理 や後述する 割込み処理 と、元の処理において待ち合わせを行い、同期を取る為の仕組みがLinuxカーネルにおいて用意されています。照度センサドライバでは使用されていませんが、 代表的な物として、wait_eventやwait_for_completionがあるので、それぞれについて説明します。

wait_event の使い方

 wait_eventは、特定の条件が満たされるまでプロセスを停止する仕組みです。条件の変化を監視するのに使用可能です。wait_eventは、以下の通り定義されています。

kernel/mediatek/4.4/include/linux/wait.h

/**
 * wait_event - sleep until a condition gets true
 * @wq: the waitqueue to wait on
 * @condition: a C expression for the event to wait for
 *
 * The process is put to sleep (TASK_UNINTERRUPTIBLE) until the
 * @condition evaluates to true. The @condition is checked each time
 * the waitqueue @wq is woken up.
 *
 * wake_up() has to be called after changing any variable that could
 * change the result of the wait condition.
 */
#define wait_event(wq, condition)

第1引数には、struct wait_queue_head_t型のポインタを指定します。これは、init_waitqueue_headで事前に初期化しておきます。
第2引数には、停止を解除して再開する条件を指定します。

init_waitqueue_headは同ファイルに、以下の通り定義されています。

#define init_waitqueue_head(q)				\
	do {						\
		static struct lock_class_key __key;	\
							\
		__init_waitqueue_head((q), #q, &__key);	\
	} while (0)

その他、jiffiesによるtimeoutを指定できる関数等があります。

#define wait_event_timeout(wq, condition, timeout)

また、wait_eventによる停止中は、そのプロセスはスリープ状態となっているので、条件を満たす値にした時に通常、wake_upも合わせて呼び出します。

#define wake_up(x)			__wake_up(x, TASK_NORMAL, 1, NULL)

第1引数には、wait_eventにも使用したstruct wait_queue_head_t型のポインタを指定します。

 照度センサドライバでは使用されてないので、I2Cホストドライバである以下から使用例を記載します。

kernel/mediatek/4.4/drivers/i2c/busses/i2c-mtk.c

mt_i2c_do_transfer関数
 i2c->trans_stop = false;

 <省略>

 tmo = wait_event_timeout(i2c->wait, i2c->trans_stop, tmo);

mt_i2c_irq関数
 i2c->trans_stop = true;

 <省略>

 wake_up(&i2c->wait);

データ信号の送信をmt_i2c_do_transferで行った後に停止した上で、後述する 割り込み をmt_i2c_irqで受け取った後に、再開する為に使用されてます。

wait_for_completion の使い方

 wait_for_completionは、プロセスが他の処理の完了を待つ為の仕組みです。他の処理によりcomplete関数が呼び出されるまでプロセスを停止します。wait_for_completionは、以下の通り定義されています。

kernel/mediatek/4.4/kernel/sched/completion.c

/**
 * wait_for_completion: - waits for completion of a task
 * @x:  holds the state of this particular completion
 *
 * This waits to be signaled for completion of a specific task. It is NOT
 * interruptible and there is no timeout.
 *
 * See also similar routines (i.e. wait_for_completion_timeout()) with timeout
 * and interrupt capability. Also see complete().
 */
void __sched wait_for_completion(struct completion *x)

引数には、struct completion型のポインタを指定します。この値は以下の通り定義されている、init_completionやreinit_completionによって事前に初期化しておく必要があります。

kernel/mediatek/4.4/linux/completion.h

/**
 * init_completion - Initialize a dynamically allocated completion
 * @x:  pointer to completion structure that is to be initialized
 *
 * This inline function will initialize a dynamically created completion
 * structure.
 */
static inline void init_completion(struct completion *x)
/**
 * reinit_completion - reinitialize a completion structure
 * @x:  pointer to completion structure that is to be reinitialized
 *
 * This inline function should be used to reinitialize a completion structure so it can
 * be reused. This is especially important after complete_all() is used.
 */
static inline void reinit_completion(struct completion *x)

wait_for_completionの他に、jiffiesによるtimeoutを指定できる関数等があります。

unsigned long __sched
wait_for_completion_timeout(struct completion *x, unsigned long timeout)

wait_for_completion等による停止を解除して再開するには、以下のcomplete関数を用います。

/**
 * complete: - signals a single thread waiting on this completion
 * @x:  holds the state of this particular completion
 *
 * This will wake up a single thread waiting on this completion. Threads will be
 * awakened in the same order in which they were queued.
 *
 * See also complete_all(), wait_for_completion() and related routines.
 *
 * It may be assumed that this function implies a write memory barrier before
 * changing the task state if and only if any tasks are woken up.
 */
void complete(struct completion *x)

引数には、wait_for_completion等にも指定したstruct completion型のポインタを指定します。

 照度センサドライバでは使用されてないので、I2Cコアドライバである以下から使用例を記載します

kernel/mediatek/4.4/drivers/i2c/i2c-core.c

i2c_del_adapter関数
	init_completion(&adap->dev_released);
	device_unregister(&adap->dev);
	wait_for_completion(&adap->dev_released);

i2c_adapter_dev_release関数
    complete(&adap->dev_released);

device_unregister関数から非同期で実行されるi2c_adapter_dev_release関数について、完了した後に呼び出すcompleteのタイミングで同期を取った後に、再開する為に使用されてます。

最後に

 どちらを使用しても実現できることが多いですが、wait_eventは条件に基づいて待つ為、イベントに依存する待ち合わせに適しています。対して、wait_for_completionは特定のタスクが完了するのを待つのに使用され、非同期処理の終了との待ち合わせに適しています。

コメント

タイトルとURLをコピーしました