LINUX.ORG.RU

rwsem, только не семафор

 , , ,


0

2

Доброго времени суток.

В ядре Linux есть такой замечательный синхронизационный примитив, как rwsem (семафор читателей-писателей). Поскольку это семафор, количество операций down() и up() должно быть сбалансировано, т. е. up() нужно сделать столько раз, сколько было сделано down().

Вопрос: как «обойти» требование сбалансированности? Я хочу, чтобы любой поток имел возможность единовременно разлочить семафор. Дело в том, что счётчик семафора у меня уже есть (количество зарезервированных блоков в ФС), а от rwsem мне нужна только обёртка над счётчиком.

В голову приходит разве что потрясающе грязный вариант вида while (rwsem_is_locked(&foo)) up_read(&foo);.

Или же, если счётчик уже есть, можно обойтись другим примитивом вместо rwsem?

★★★★★

Последнее исправление: intelfx (всего исправлений: 1)

Опишите причину проблемы более подробно. Кто блокирует, с каким правом (чтение/запись) и случай, при котором возникает проблема. Зачем требуется разблокировать то, что поток не блокировал? Очень большая вероятность неверной архитектуры.

anonymous
()
Ответ на: комментарий от anonymous

Так и думал.

Всё происходит в драйвере ФС. Идея в том, что есть некоторый счётчик зарезервированных блоков на диске. Обычно он равен нулю, т. е. все блоки или свободны, или заняты данными. Если некоторый поток хочет аллоцировать место на диске, он сначала оценивает сверху требуемое количество и резервирует его (увеличивает счётчик на N), и только потом аллоцирует по мере необходимости. В итоге часть зарезервированных блоков переходит в разряд аллоцированных, а остальные возвращаются к свободным. Сам счётчик сейчас под спинлоком. Сложность в том, что зарезервировать место может один поток, а освободить неиспользованное — другой и вообще по частям.

А сделать нужно вот что: дождаться того момента, когда счётчик станет равен нулю, и зарезервировать всё свободное на этот момент место (атомарно).

Я хотел, чтобы все потоки, которые в штатном режиме резервируют место, захватывали семафор в режиме read, а то, что я сейчас пишу — захватывало его же в режиме write и ждало, пока читателей не станет.

Желание разблокировать то, что поток не блокировал, возникает как раз потому что

зарезервировать место может один поток, а освободить неиспользованное — другой и вообще по частям.

intelfx ★★★★★
() автор топика
Последнее исправление: intelfx (всего исправлений: 1)

а, извини, не прочитал вопрос в теме

Или же, если счётчик уже есть, можно обойтись другим примитивом вместо rwsem?

anonymous
()
Ответ на: комментарий от Pavval

Мне нужно ждать, пока счётчик не станет равным нулю, а потом — атомарно, пока он ещё ноль — записать туда что-то очень большое (зарезервировать все блоки) и опционально подвесить остальные потоки, чтобы они не получили -ENOSPC.

Крутиться и поллить счётчик на манер спинлока совершенно не вариант, т. к. коммит транзакции легко может занимать секунд десять.

intelfx ★★★★★
() автор топика
Ответ на: комментарий от kvap

Минуточку. А как это поможет?

(во-первых, заблокировать — это down().)

Ну получу я заблокированный семафор. Что с ним дальше делать?

intelfx ★★★★★
() автор топика
Ответ на: комментарий от intelfx

Вначале делаем up() до единицы. Потом поток, которому нужно изменить значение счётчика, делает down() до нуля и получает эксклюзивный доступ до счётчика. После того, как он закончил работу со счётчиком, он разблокирует его с помощью up(). Ещё раз намекаю, чего гуглить: mutex.

kvap
()
Ответ на: комментарий от kvap

Это ты описал простой мьютекс. Я знаю, что это такое, но он, во-первых, здесь не подходит (есть ещё много потоков, которым всем вместе нужен неэксклюзивный доступ), и во-вторых, через rwsem не реализовывается.

intelfx ★★★★★
() автор топика
Последнее исправление: intelfx (всего исправлений: 1)
Ответ на: комментарий от tailgunner

Вопрос, ваще-то, про «как сделать», а не «как оптимизировать». Ну и предложенный мной грязный хак имеет гонку между освобождением семафора последним читателем и захватом семафора новым читателем.

intelfx ★★★★★
() автор топика
Ответ на: комментарий от intelfx

подожди, а если писатель изъявил желание захватить rwsem, то разве читатели могут продолжать захватывать rwsem?

эта фраза что значит?

Writers get priority; as soon as a writer tries to enter the critical section, no readers will be allowed in until all writers have completed their work.

anonymous
()
Ответ на: комментарий от intelfx

Вопрос, ваще-то, про «как сделать», а не «как оптимизировать»

Насколько я понял, использование единого лока решает задачу, так что вопрос «как сделать» не стоит. У тебя уже есть результаты профилирования? Нет?

tailgunner ★★★★★
()
Ответ на: комментарий от tailgunner

Единый лок — это значит разрешать только одному потоку одновременно резервировать место? Это меняет текущее поведение ФС в сильно худшую сторону.

intelfx ★★★★★
() автор топика
Ответ на: комментарий от anonymous

Писателей здесь нет.

  • исходное состояние — читатель1 захватил семафор и зарезервировал место;
  • читатель2 захватил семафор;
  • читатель1 освободил всё зарезервированное место и собрался отпустить семафор;
  • читатель2 зарезервировал место;
  • читатель1 отпустил семафор «за обоих».

Нужно как-то сделать операцию с семафором и операцию изменения счётчика атомарными относительно друг друга, т. е. чистый rwsem тут вообще не работает.

intelfx ★★★★★
() автор топика
Ответ на: комментарий от intelfx

Единый лок — это значит разрешать только одному потоку одновременно резервировать место?

Да.

Это меняет текущее поведение ФС в сильно худшую сторону.

Не то, чтобы я тебе не верил, но с чего бы в «сильно»? То, что происходит под спинлоком, обычно делается очень быстро и не занимается I/O. Вдобавок, «список свободных блоков» намекает, что затронуты только операции write (не все) и ftruncate. Короче, у тебя есть данные профайлера или только предположения?

И да, требование хитрого rwsem для описанной задачи выглядит странно - лично я не понимаю, чем не хватит обычного коллбека, вызываемого при достижении «того момента, когда счётчик станет равен нулю».

tailgunner ★★★★★
()
Ответ на: комментарий от intelfx

когда счётчик станет равен нулю, и зарезервировать всё свободное на этот момент место (атомарно)

эквивалентно

Единый лок — это значит разрешать только одному потоку одновременно резервировать место? Это меняет текущее поведение ФС в сильно худшую сторону.

anonymous
()
Ответ на: комментарий от anonymous

Это особый случай. В отсутствие такого потока остальные должны уметь резервировать место одновременно.

intelfx ★★★★★
() автор топика
Последнее исправление: intelfx (всего исправлений: 1)
Ответ на: комментарий от tailgunner

Нее. Сейчас под спинлоком только изменяется счётчик (по факту там не один счётчик, а несколько взаимосвязанных переменных). А я хочу запихнуть под семафор весь жизненный цикл транзакции от резервирования места до освобождения не пригодившегося. Ну и да, это copy-on-write, поэтому затронуты вообще все операции.

intelfx ★★★★★
() автор топика
Последнее исправление: intelfx (всего исправлений: 1)
Ответ на: комментарий от anonymous

Нет, я пытаюсь реализовать FITRIM ioctl в ФС reiser4.

intelfx ★★★★★
() автор топика
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.