Баг, который тут описан, был найден в драйвере ядра linux-2.6.37 с помощью проекта Linux Driver Verification Program.
Драйвер предназначен, как в его коде написано, для работы «radios with Silicon Labs Si470x FM Radio Receivers». Но, видимо, не работает, поскольку функция read скорее всего приводит к зависанию. На операциях read и write держится большая часть полезной работы драйверов.
Сама ошибка заключается в повторной блокировки одного и того же mutex’а. Для тех, кто не знает, что такое mutex рекомендую — почитать вот это. Вкратце: если мы последовательно попытаемся захватить один и тот же мютекс два раза, то получим deadlock. Иначе говоря, программа будет бесконечно ждать сама себя, что крайне неприятно.
Проблемный кусок кода.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
<strong><span style="color: #800000;"> 441 mutex_lock(&radio->lock);</span></strong> 442 if ((radio->registers[SYSCONFIG1] & SYSCONFIG1_RDS) == 0) 443 si470x_rds_on(radio); 444 445 /* block if no new data available */ 446 while (radio->wr_index == radio->rd_index) { 447 if (file->f_flags & O_NONBLOCK) { 448 retval = -EWOULDBLOCK; 449 goto done; 450 } 451 if (wait_event_interruptible(radio->read_queue, 452 radio->wr_index != radio->rd_index) < 0) { 453 retval = -EINTR; 454 goto done; 455 } 456 } 457 458 /* calculate block count from byte count */ 459 count /= 3; 460 461 /* copy RDS block out of internal buffer and to user buffer */ <strong><span style="color: #800000;"> 462 mutex_lock(&radio->lock);</span></strong> |
Мютекс нигде не освобождается между строчками 441-462. Почему сделан такой вывод? Очевидно, что явного вызова mutex_unlock в вышеописанном коде нет. Допустим даже, что мютекс может разблокироваться в функции si470_rds_on (вызывается в строчке 443). Но условие в строчке 442 вполне может не выполниться (иначе зачем его туда поставили ) и мы пойдем дальше. А дальше кроме как wait_event_interruptible никакие функции не вызываются. wait_event_interruptible — представитель API ядра. Ее задача заключается в ожидании выполнения некоторого условия (не совсем, interruptible — говорит о том что ожидание может быть прервано) . В данном случае это условие: radio->wr_index != radio->rd_index. То есть в условии не вызываются никакие посторонние функции, которые могли бы разлочить мютекс. Можно конечно предположить, что мютекс разлочит кто-нибудь еще. Но таких сложностей в драйвере нет и работа с мютексами там написана достаточно прозрачно.
В версии ядра 2.6.36.3 первого mutex_lock’а нет. Давайте посмотрим, что на самом деле хотели сделать разработчики. Ниже представлено сравнение участков кода нашего драйвера из двух версий ядра : 2.6.36.3 слева и справа 2.6.37.
Видно, что в версии 2.6.37 пару mutex_lock/mutex_unlock вынесли в fops_read. А убрать старый mutex_lock из fops_read забыли. Теперь все ясно. Поможем разработчикам и напишем патч.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
diff --git a/drivers/media/radio/si470x/radio-si470x-common.c b/drivers/media/radio/si470x/radio-si470x-common.c index ac76dfe..e50452b 100644 --- a/drivers/media/radio/si470x/radio-si470x-common.c +++ b/drivers/media/radio/si470x/radio-si470x-common.c @@ -459,7 +459,6 @@ static ssize_t si470x_fops_read(struct file *file, char __user *buf, count /= 3; /* copy RDS block out of internal buffer and to user buffer */ - mutex_lock(&radio->lock); while (block_count < count) { if (radio->rd_index == radio->wr_index) break; -- |
Продолжение в Linux Kernel Mailing List или на странице багов, найденных с помощью LDV.