ocfs2: Avoid livelock in ocfs2_readpage()
PATCH 1/1 ocfs2: Avoid livelock in ocfs2_readpage()
When someone writes to an inode, readers accessing the same inode via ocfs2_readpage() just busyloop trying to get ip_alloc_sem because do_generic_file_read() looks up the page again and retries ->readpage() when previous attempt failed with AOP_TRUNCATED_PAGE. When there are enough readers, they can occupy all CPUs and in non-preempt kernel the system is deadlocked because writer holding ip_alloc_sem is never run to release the semaphore. Fix the problem by making reader block on ip_alloc_sem to break the busy loop.
--- fs/ocfs2/aops.c | 8 ++++++++ 1 files changed, 8 insertions(+), 0 deletions(-)
fs/ocfs2/aops.c +8 -0
@@ -290,7 +290,15 @@ static int ocfs2_readpage(struct file *file, struct page *page) }if (down_read_trylock (&oi->ip_alloc_sem) ==0 ) { +/* +* Unlock the page and cycle ip_alloc_sem so that we don't +* busyloop waiting for ip_alloc_sem to unlock +*/ ret =AOP_TRUNCATED_PAGE ; +unlock_page (page); + unlock =0 ; +down_read (&oi->ip_alloc_sem); +up_read (&oi->ip_alloc_sem); goto out_inode_unlock; }
Signature · 1 line
1.7.1
PATCH 1/1 ocfs2: Avoid livelock in ocfs2_readpage()
Looks reasonable. Was this something someone ran into or was this reproduced only via a test workload?
Quoted 34 lines · Jan Kara
When someone writes to an inode, readers accessing the same inode via
ocfs2_readpage()just busyloop trying to getip_alloc_sembecausedo_generic_file_read()looks up the page again and retries ->readpage() when previous attempt failed withAOP_TRUNCATED_PAGE. When there are enough readers, they can occupy all CPUs and in non-preempt kernel the system is deadlocked because writer holdingip_alloc_semis never run to release the semaphore. Fix the problem by making reader block onip_alloc_semto break the busy loop.Signed-off-by Jan Kara<[email protected]>--- fs/ocfs2/aops.c | 8 ++++++++ 1 files changed, 8 insertions(+), 0 deletions(-)fs/ocfs2/aops.c +8
-0@@ -290,7 +290,15 @@ static int ocfs2_readpage(struct file *file, struct page *page) }if (down_read_trylock (&oi->ip_alloc_sem) ==0 ) { +/* +* Unlock the page and cycle ip_alloc_sem so that we don't +* busyloop waiting for ip_alloc_sem to unlock +*/ ret =AOP_TRUNCATED_PAGE ; +unlock_page (page); + unlock =0 ; +down_read (&oi->ip_alloc_sem); +up_read (&oi->ip_alloc_sem); goto out_inode_unlock; }
PATCH 1/1 ocfs2: Avoid livelock in ocfs2_readpage()
Quoted 2 lines · Sunil Mushran
Looks reasonable. Was this something someone ran into or was this reproduced only via a test workload?
One of our customers hit this when running installation in KVM whose
filesystem image was stored on OCFS2... Generally loading KVM guest with IO seemed to trigger this fairly regulary.
Honza
Quoted 36 lines · Jan Kara
Quoted 34 lines
When someone writes to an inode, readers accessing the same inode via
ocfs2_readpage()just busyloop trying to getip_alloc_sembecausedo_generic_file_read()looks up the page again and retries ->readpage() when previous attempt failed withAOP_TRUNCATED_PAGE. When there are enough readers, they can occupy all CPUs and in non-preempt kernel the system is deadlocked because writer holdingip_alloc_semis never run to release the semaphore. Fix the problem by making reader block onip_alloc_semto break the busy loop.Signed-off-by Jan Kara<[email protected]>--- fs/ocfs2/aops.c | 8 ++++++++ 1 files changed, 8 insertions(+), 0 deletions(-)fs/ocfs2/aops.c +8
-0@@ -290,7 +290,15 @@ static int ocfs2_readpage(struct file *file, struct page *page) }if (down_read_trylock (&oi->ip_alloc_sem) ==0 ) { +/* +* Unlock the page and cycle ip_alloc_sem so that we don't +* busyloop waiting for ip_alloc_sem to unlock +*/ ret =AOP_TRUNCATED_PAGE ; +unlock_page (page); + unlock =0 ; +down_read (&oi->ip_alloc_sem); +up_read (&oi->ip_alloc_sem); goto out_inode_unlock; }
Signature · 2 lines
Jan Kara <[email protected]> SUSE Labs, CR
Joel Becker
maintainerPATCH 1/1 ocfs2: Avoid livelock in ocfs2_readpage()
In reply to Jan KaraQuoted 23 lines · Jan Kara
When someone writes to an inode, readers accessing the same inode via
ocfs2_readpage()just busyloop trying to getip_alloc_sembecausedo_generic_file_read()looks up the page again and retries ->readpage() when previous attempt failed withAOP_TRUNCATED_PAGE. When there are enough readers, they can occupy all CPUs and in non-preempt kernel the system is deadlocked because writer holdingip_alloc_semis never run to release the semaphore. Fix the problem by making reader block onip_alloc_semto break the busy loop.Signed-off-by Jan Kara <[email protected]>--- fs/ocfs2/aops.c | 8 ++++++++ 1 files changed, 8 insertions(+), 0 deletions(-)fs/ocfs2/aops.c +1
-0@@ -290,7 +290,15 @@ static int ocfs2_readpage(struct file *file, struct page *page) }if (down_read_trylock (&oi->ip_alloc_sem) ==0 ) { +/*
fs/ocfs2/aops.c static int ocfs2_readpage(struct file *file, struct page *page) · line 303
Quoted 10 lines
fs/ocfs2/aops.c +7
-0+* Unlock the page and cycle ip_alloc_sem so that we don't +* busyloop waiting for ip_alloc_sem to unlock +*/ ret =AOP_TRUNCATED_PAGE ; +unlock_page (page); + unlock =0 ; +down_read (&oi->ip_alloc_sem); +up_read (&oi->ip_alloc_sem); goto out_inode_unlock; }
Question: First, is it safe to drop the page lock here?
Can all callers of readpage (not just g_f_a_r()) handle that?>
Signature · 7 lines
"But all my words come back to me In shades of mediocrity. Like emptiness in harmony I need someone to comfort me." http://www.jlbec.org/ jlbec at evilplan.org
PATCH 1/1 ocfs2: Avoid livelock in ocfs2_readpage()
Quoted 22 lines · Joel Becker
Quoted 21 lines · Jan Kara
When someone writes to an inode, readers accessing the same inode via
ocfs2_readpage()just busyloop trying to getip_alloc_sembecausedo_generic_file_read()looks up the page again and retries ->readpage() when previous attempt failed withAOP_TRUNCATED_PAGE. When there are enough readers, they can occupy all CPUs and in non-preempt kernel the system is deadlocked because writer holdingip_alloc_semis never run to release the semaphore. Fix the problem by making reader block onip_alloc_semto break the busy loop.Signed-off-by Jan Kara <[email protected]>--- fs/ocfs2/aops.c | 8 ++++++++ 1 files changed, 8 insertions(+), 0 deletions(-)fs/ocfs2/aops.c +0
-0@@ -290,7 +290,15 @@ static int ocfs2_readpage(struct file *file, struct page *page) }
Quoted 15 lines
fs/ocfs2/aops.c static int ocfs2_readpage(struct file *file, struct page *page) · line 303
Quoted 12 lines
fs/ocfs2/aops.c +8
-0if (down_read_trylock (&oi->ip_alloc_sem) ==0 ) { +/* +* Unlock the page and cycle ip_alloc_sem so that we don't +* busyloop waiting for ip_alloc_sem to unlock +*/ ret =AOP_TRUNCATED_PAGE ; +unlock_page (page); + unlock =0 ; +down_read (&oi->ip_alloc_sem); +up_read (&oi->ip_alloc_sem); goto out_inode_unlock; }Question: First, is it safe to drop the page lock here?
Can all callers of readpage (not justg_f_a_r()) handle that?
We do exactly the same thing in ocfs2_inode_lock_with_page() so we
definitely don't introduce a new problem. And every caller of readpage() is *supposed* to handle AOP_TRUNCATED_PAGE return value (because page reading can race with truncate) in which case page must be returned unlocked. So it should be safe. Admittedly I did not bother to check the call sites.
Honza
Signature · 2 lines
Jan Kara <[email protected]> SUSE Labs, CR
Joel Becker
maintainerPATCH 1/1 ocfs2: Avoid livelock in ocfs2_readpage()
In reply to Jan KaraQuoted 10 lines · Jan Kara
When someone writes to an inode, readers accessing the same inode via
ocfs2_readpage()just busyloop trying to getip_alloc_sembecausedo_generic_file_read()looks up the page again and retries ->readpage() when previous attempt failed withAOP_TRUNCATED_PAGE. When there are enough readers, they can occupy all CPUs and in non-preempt kernel the system is deadlocked because writer holdingip_alloc_semis never run to release the semaphore. Fix the problem by making reader block onip_alloc_semto break the busy loop.Signed-off-by Jan Kara <[email protected]>
This patch is now in the fixes branch of ocfs2.git.
Joel
Signature · 6 lines
Life's Little Instruction Book #30 "Never buy a house without a fireplace." http://www.jlbec.org/ jlbec at evilplan.org