[rh7,v2] cgroup: rework reference acquisition for cgroup_find_inode

Submitted by Andrey Zhadchenko on Nov. 11, 2020, 7:32 a.m.

Details

Message ID 1605079948-106595-1-git-send-email-andrey.zhadchenko@virtuozzo.com
State New
Series "cgroup: rework reference acquisition for cgroup_find_inode"
Headers show

Commit Message

Andrey Zhadchenko Nov. 11, 2020, 7:32 a.m.
Use more generic igrab instead of atomic inc. Move cgroup_hash_del to eviction
stage to avoid deadlock.

Signed-off-by: Andrey Zhadchenko <andrey.zhadchenko@virtuozzo.com>
---

v2: adjusted function call order in cgroup_evict_inode to match existing code

 kernel/cgroup.c | 25 ++++++++-----------------
 1 file changed, 8 insertions(+), 17 deletions(-)

Patch hide | download patch | download mbox

diff --git a/kernel/cgroup.c b/kernel/cgroup.c
index 27d7a5e..8c2cef8 100644
--- a/kernel/cgroup.c
+++ b/kernel/cgroup.c
@@ -1522,21 +1522,10 @@  static struct inode *cgroup_find_inode(unsigned long fh[2], char take_ref)
 	struct inode *ret = NULL;
 
 	spin_lock(&cgroup_inode_table_lock);
-	item = cgroup_find_item_no_lock(fh);
 
-	/*
-	 * If we need to increase refcount, we should be aware of possible
-	 * deadlock. Another thread may have started deleting this inode:
-	 * iput->iput_final->cgroup_delete_inode->cgroup_hash_del
-	 * If we just call igrab, it will try to take i_lock and this will
-	 * result in deadlock, because deleting thread has already taken it
-	 * and waits on cgroup_inode_table_lock to find inode in hashtable.
-	 *
-	 * If i_count is zero, someone is deleting it -> skip.
-	 */
-	if (take_ref && item)
-		if (!atomic_inc_not_zero(&item->inode->i_count))
-			item = NULL;
+	item = cgroup_find_item_no_lock(fh);
+	if (item && take_ref && !igrab(item->inode))
+		item = NULL;
 
 	spin_unlock(&cgroup_inode_table_lock);
 
@@ -1634,15 +1623,17 @@  static const struct export_operations cgroup_export_ops = {
 	.fh_to_dentry	= cgroup_fh_to_dentry,
 };
 
-static int cgroup_delete_inode(struct inode *inode)
+static void cgroup_evict_inode(struct inode *inode)
 {
+	truncate_inode_pages_final(&inode->i_data);
+	clear_inode(inode);
 	cgroup_hash_del(inode);
-	return generic_delete_inode(inode);
 }
 
 static const struct super_operations cgroup_ops = {
 	.statfs = simple_statfs,
-	.drop_inode = cgroup_delete_inode,
+	.drop_inode = generic_delete_inode,
+	.evict_inode = cgroup_evict_inode,
 	.show_options = cgroup_show_options,
 #ifdef CONFIG_VE
 	.show_path = cgroup_show_path,

Comments

Kirill Tkhai Nov. 12, 2020, 2:29 p.m.
Hi, Andrey,

On 11.11.2020 10:32, Andrey Zhadchenko wrote:
> Use more generic igrab instead of atomic inc. Move cgroup_hash_del to eviction
> stage to avoid deadlock.
> 
> Signed-off-by: Andrey Zhadchenko <andrey.zhadchenko@virtuozzo.com>
> ---
> 
> v2: adjusted function call order in cgroup_evict_inode to match existing code
> 
>  kernel/cgroup.c | 25 ++++++++-----------------
>  1 file changed, 8 insertions(+), 17 deletions(-)
> 
> diff --git a/kernel/cgroup.c b/kernel/cgroup.c
> index 27d7a5e..8c2cef8 100644
> --- a/kernel/cgroup.c
> +++ b/kernel/cgroup.c
> @@ -1522,21 +1522,10 @@ static struct inode *cgroup_find_inode(unsigned long fh[2], char take_ref)
>  	struct inode *ret = NULL;
>  
>  	spin_lock(&cgroup_inode_table_lock);
> -	item = cgroup_find_item_no_lock(fh);
>  
> -	/*
> -	 * If we need to increase refcount, we should be aware of possible
> -	 * deadlock. Another thread may have started deleting this inode:
> -	 * iput->iput_final->cgroup_delete_inode->cgroup_hash_del
> -	 * If we just call igrab, it will try to take i_lock and this will
> -	 * result in deadlock, because deleting thread has already taken it
> -	 * and waits on cgroup_inode_table_lock to find inode in hashtable.
> -	 *
> -	 * If i_count is zero, someone is deleting it -> skip.
> -	 */
> -	if (take_ref && item)
> -		if (!atomic_inc_not_zero(&item->inode->i_count))
> -			item = NULL;
> +	item = cgroup_find_item_no_lock(fh);
> +	if (item && take_ref && !igrab(item->inode))
> +		item = NULL;

Here you call igrab() under cgroup_inode_table_lock, so the check for (inode->i_state & I_FREEING|I_WILL_FREE)
is done under that lock and i_lock.

But clear_inode() sets "inode->i_state = I_FREEING | I_CLEAR" without any lock.
This place does not look obvious. Isn't there some problem? 

>  
>  	spin_unlock(&cgroup_inode_table_lock);
>  
> @@ -1634,15 +1623,17 @@ static const struct export_operations cgroup_export_ops = {
>  	.fh_to_dentry	= cgroup_fh_to_dentry,
>  };
>  
> -static int cgroup_delete_inode(struct inode *inode)
> +static void cgroup_evict_inode(struct inode *inode)
>  {
> +	truncate_inode_pages_final(&inode->i_data);
> +	clear_inode(inode);
>  	cgroup_hash_del(inode);
> -	return generic_delete_inode(inode);
>  }
>  
>  static const struct super_operations cgroup_ops = {
>  	.statfs = simple_statfs,
> -	.drop_inode = cgroup_delete_inode,
> +	.drop_inode = generic_delete_inode,
> +	.evict_inode = cgroup_evict_inode,
>  	.show_options = cgroup_show_options,
>  #ifdef CONFIG_VE
>  	.show_path = cgroup_show_path,
>
Andrey Zhadchenko Nov. 12, 2020, 3:08 p.m.
On Thu, 12 Nov 2020 17:29:21 +0300
Kirill Tkhai <ktkhai@virtuozzo.com> wrote:

Hi, Kirill,

> Hi, Andrey,
> 
> On 11.11.2020 10:32, Andrey Zhadchenko wrote:
> > Use more generic igrab instead of atomic inc. Move cgroup_hash_del
> > to eviction stage to avoid deadlock.
> > 
> > Signed-off-by: Andrey Zhadchenko <andrey.zhadchenko@virtuozzo.com>
> > ---
> > 
> > v2: adjusted function call order in cgroup_evict_inode to match
> > existing code
> > 
> >  kernel/cgroup.c | 25 ++++++++-----------------
> >  1 file changed, 8 insertions(+), 17 deletions(-)
> > 
> > diff --git a/kernel/cgroup.c b/kernel/cgroup.c
> > index 27d7a5e..8c2cef8 100644
> > --- a/kernel/cgroup.c
> > +++ b/kernel/cgroup.c
> > @@ -1522,21 +1522,10 @@ static struct inode
> > *cgroup_find_inode(unsigned long fh[2], char take_ref) struct inode
> > *ret = NULL; 
> >  	spin_lock(&cgroup_inode_table_lock);
> > -	item = cgroup_find_item_no_lock(fh);
> >  
> > -	/*
> > -	 * If we need to increase refcount, we should be aware of
> > possible
> > -	 * deadlock. Another thread may have started deleting this
> > inode:
> > -	 * iput->iput_final->cgroup_delete_inode->cgroup_hash_del
> > -	 * If we just call igrab, it will try to take i_lock and
> > this will
> > -	 * result in deadlock, because deleting thread has already
> > taken it
> > -	 * and waits on cgroup_inode_table_lock to find inode in
> > hashtable.
> > -	 *
> > -	 * If i_count is zero, someone is deleting it -> skip.
> > -	 */
> > -	if (take_ref && item)
> > -		if (!atomic_inc_not_zero(&item->inode->i_count))
> > -			item = NULL;
> > +	item = cgroup_find_item_no_lock(fh);
> > +	if (item && take_ref && !igrab(item->inode))
> > +		item = NULL;  
> 
> Here you call igrab() under cgroup_inode_table_lock, so the check for
> (inode->i_state & I_FREEING|I_WILL_FREE) is done under that lock and
> i_lock.
> 
> But clear_inode() sets "inode->i_state = I_FREEING | I_CLEAR" without
> any lock. This place does not look obvious. Isn't there some problem? 
> 

iput_final() sets inode to I_FREEING under i_lock, release it and calls
evict(), which calls cgroup_evict_inode() via
inode->i_sb->s_op->evict().

If I get it right, this seems to be fine. As soon as iput_final starts
working igrab shouldn't be able to take ref on this inode.

> >  
> >  	spin_unlock(&cgroup_inode_table_lock);
> >  
> > @@ -1634,15 +1623,17 @@ static const struct export_operations
> > cgroup_export_ops = { .fh_to_dentry	= cgroup_fh_to_dentry,
> >  };
> >  
> > -static int cgroup_delete_inode(struct inode *inode)
> > +static void cgroup_evict_inode(struct inode *inode)
> >  {
> > +	truncate_inode_pages_final(&inode->i_data);
> > +	clear_inode(inode);
> >  	cgroup_hash_del(inode);
> > -	return generic_delete_inode(inode);
> >  }
> >  
> >  static const struct super_operations cgroup_ops = {
> >  	.statfs = simple_statfs,
> > -	.drop_inode = cgroup_delete_inode,
> > +	.drop_inode = generic_delete_inode,
> > +	.evict_inode = cgroup_evict_inode,
> >  	.show_options = cgroup_show_options,
> >  #ifdef CONFIG_VE
> >  	.show_path = cgroup_show_path,
> >   
>
Kirill Tkhai Nov. 12, 2020, 3:17 p.m.
On 12.11.2020 18:08, Andrey Zhadchenko wrote:
> On Thu, 12 Nov 2020 17:29:21 +0300
> Kirill Tkhai <ktkhai@virtuozzo.com> wrote:
> 
> Hi, Kirill,
> 
>> Hi, Andrey,
>>
>> On 11.11.2020 10:32, Andrey Zhadchenko wrote:
>>> Use more generic igrab instead of atomic inc. Move cgroup_hash_del
>>> to eviction stage to avoid deadlock.
>>>
>>> Signed-off-by: Andrey Zhadchenko <andrey.zhadchenko@virtuozzo.com>
>>> ---
>>>
>>> v2: adjusted function call order in cgroup_evict_inode to match
>>> existing code
>>>
>>>  kernel/cgroup.c | 25 ++++++++-----------------
>>>  1 file changed, 8 insertions(+), 17 deletions(-)
>>>
>>> diff --git a/kernel/cgroup.c b/kernel/cgroup.c
>>> index 27d7a5e..8c2cef8 100644
>>> --- a/kernel/cgroup.c
>>> +++ b/kernel/cgroup.c
>>> @@ -1522,21 +1522,10 @@ static struct inode
>>> *cgroup_find_inode(unsigned long fh[2], char take_ref) struct inode
>>> *ret = NULL; 
>>>  	spin_lock(&cgroup_inode_table_lock);
>>> -	item = cgroup_find_item_no_lock(fh);
>>>  
>>> -	/*
>>> -	 * If we need to increase refcount, we should be aware of
>>> possible
>>> -	 * deadlock. Another thread may have started deleting this
>>> inode:
>>> -	 * iput->iput_final->cgroup_delete_inode->cgroup_hash_del
>>> -	 * If we just call igrab, it will try to take i_lock and
>>> this will
>>> -	 * result in deadlock, because deleting thread has already
>>> taken it
>>> -	 * and waits on cgroup_inode_table_lock to find inode in
>>> hashtable.
>>> -	 *
>>> -	 * If i_count is zero, someone is deleting it -> skip.
>>> -	 */
>>> -	if (take_ref && item)
>>> -		if (!atomic_inc_not_zero(&item->inode->i_count))
>>> -			item = NULL;
>>> +	item = cgroup_find_item_no_lock(fh);
>>> +	if (item && take_ref && !igrab(item->inode))
>>> +		item = NULL;  
>>
>> Here you call igrab() under cgroup_inode_table_lock, so the check for
>> (inode->i_state & I_FREEING|I_WILL_FREE) is done under that lock and
>> i_lock.
>>
>> But clear_inode() sets "inode->i_state = I_FREEING | I_CLEAR" without
>> any lock. This place does not look obvious. Isn't there some problem? 
>>
> 
> iput_final() sets inode to I_FREEING under i_lock, release it and calls
> evict(), which calls cgroup_evict_inode() via
> inode->i_sb->s_op->evict().

What is about inodes in LRU?

Say, iput_final() moves inode to LRU with zero counter. I_FREEING is not
set there. Later prune_icache_sb() operates with that inode with zero counter.

Doesn't dispose_list()->evict()->cgroup_evict_inode() race with cgroup_find_inode()?

> If I get it right, this seems to be fine. As soon as iput_final starts
> working igrab shouldn't be able to take ref on this inode.
> 
>>>  
>>>  	spin_unlock(&cgroup_inode_table_lock);
>>>  
>>> @@ -1634,15 +1623,17 @@ static const struct export_operations
>>> cgroup_export_ops = { .fh_to_dentry	= cgroup_fh_to_dentry,
>>>  };
>>>  
>>> -static int cgroup_delete_inode(struct inode *inode)
>>> +static void cgroup_evict_inode(struct inode *inode)
>>>  {
>>> +	truncate_inode_pages_final(&inode->i_data);
>>> +	clear_inode(inode);
>>>  	cgroup_hash_del(inode);
>>> -	return generic_delete_inode(inode);
>>>  }
>>>  
>>>  static const struct super_operations cgroup_ops = {
>>>  	.statfs = simple_statfs,
>>> -	.drop_inode = cgroup_delete_inode,
>>> +	.drop_inode = generic_delete_inode,
>>> +	.evict_inode = cgroup_evict_inode,
>>>  	.show_options = cgroup_show_options,
>>>  #ifdef CONFIG_VE
>>>  	.show_path = cgroup_show_path,
>>>   
>>
>
Pavel Tikhomirov Nov. 12, 2020, 3:39 p.m.
On 11/12/20 6:17 PM, Kirill Tkhai wrote:
> On 12.11.2020 18:08, Andrey Zhadchenko wrote:
>> On Thu, 12 Nov 2020 17:29:21 +0300
>> Kirill Tkhai <ktkhai@virtuozzo.com> wrote:
>>
>> Hi, Kirill,
>>
>>> Hi, Andrey,
>>>
>>> On 11.11.2020 10:32, Andrey Zhadchenko wrote:
>>>> Use more generic igrab instead of atomic inc. Move cgroup_hash_del
>>>> to eviction stage to avoid deadlock.
>>>>
>>>> Signed-off-by: Andrey Zhadchenko <andrey.zhadchenko@virtuozzo.com>
>>>> ---
>>>>
>>>> v2: adjusted function call order in cgroup_evict_inode to match
>>>> existing code
>>>>
>>>>   kernel/cgroup.c | 25 ++++++++-----------------
>>>>   1 file changed, 8 insertions(+), 17 deletions(-)
>>>>
>>>> diff --git a/kernel/cgroup.c b/kernel/cgroup.c
>>>> index 27d7a5e..8c2cef8 100644
>>>> --- a/kernel/cgroup.c
>>>> +++ b/kernel/cgroup.c
>>>> @@ -1522,21 +1522,10 @@ static struct inode
>>>> *cgroup_find_inode(unsigned long fh[2], char take_ref) struct inode
>>>> *ret = NULL;
>>>>   	spin_lock(&cgroup_inode_table_lock);
>>>> -	item = cgroup_find_item_no_lock(fh);
>>>>   
>>>> -	/*
>>>> -	 * If we need to increase refcount, we should be aware of
>>>> possible
>>>> -	 * deadlock. Another thread may have started deleting this
>>>> inode:
>>>> -	 * iput->iput_final->cgroup_delete_inode->cgroup_hash_del
>>>> -	 * If we just call igrab, it will try to take i_lock and
>>>> this will
>>>> -	 * result in deadlock, because deleting thread has already
>>>> taken it
>>>> -	 * and waits on cgroup_inode_table_lock to find inode in
>>>> hashtable.
>>>> -	 *
>>>> -	 * If i_count is zero, someone is deleting it -> skip.
>>>> -	 */
>>>> -	if (take_ref && item)
>>>> -		if (!atomic_inc_not_zero(&item->inode->i_count))
>>>> -			item = NULL;
>>>> +	item = cgroup_find_item_no_lock(fh);
>>>> +	if (item && take_ref && !igrab(item->inode))
>>>> +		item = NULL;
>>>
>>> Here you call igrab() under cgroup_inode_table_lock, so the check for
>>> (inode->i_state & I_FREEING|I_WILL_FREE) is done under that lock and
>>> i_lock.
>>>
>>> But clear_inode() sets "inode->i_state = I_FREEING | I_CLEAR" without
>>> any lock. This place does not look obvious. Isn't there some problem?
>>>
>>
>> iput_final() sets inode to I_FREEING under i_lock, release it and calls
>> evict(), which calls cgroup_evict_inode() via
>> inode->i_sb->s_op->evict().
> 
> What is about inodes in LRU?

I thought iput_final:drop is always true for cgroupfs. And there is no lru.

> 
> Say, iput_final() moves inode to LRU with zero counter. I_FREEING is not
> set there. Later prune_icache_sb() operates with that inode with zero counter.
> 
> Doesn't dispose_list()->evict()->cgroup_evict_inode() race with cgroup_find_inode()?
> 
>> If I get it right, this seems to be fine. As soon as iput_final starts
>> working igrab shouldn't be able to take ref on this inode.
>>
>>>>   
>>>>   	spin_unlock(&cgroup_inode_table_lock);
>>>>   
>>>> @@ -1634,15 +1623,17 @@ static const struct export_operations
>>>> cgroup_export_ops = { .fh_to_dentry	= cgroup_fh_to_dentry,
>>>>   };
>>>>   
>>>> -static int cgroup_delete_inode(struct inode *inode)
>>>> +static void cgroup_evict_inode(struct inode *inode)
>>>>   {
>>>> +	truncate_inode_pages_final(&inode->i_data);
>>>> +	clear_inode(inode);
>>>>   	cgroup_hash_del(inode);
>>>> -	return generic_delete_inode(inode);
>>>>   }
>>>>   
>>>>   static const struct super_operations cgroup_ops = {
>>>>   	.statfs = simple_statfs,
>>>> -	.drop_inode = cgroup_delete_inode,
>>>> +	.drop_inode = generic_delete_inode,
>>>> +	.evict_inode = cgroup_evict_inode,
>>>>   	.show_options = cgroup_show_options,
>>>>   #ifdef CONFIG_VE
>>>>   	.show_path = cgroup_show_path,
>>>>    
>>>
>>
>
Kirill Tkhai Nov. 12, 2020, 3:40 p.m.
On 12.11.2020 18:39, Pavel Tikhomirov wrote:
> 
> 
> On 11/12/20 6:17 PM, Kirill Tkhai wrote:
>> On 12.11.2020 18:08, Andrey Zhadchenko wrote:
>>> On Thu, 12 Nov 2020 17:29:21 +0300
>>> Kirill Tkhai <ktkhai@virtuozzo.com> wrote:
>>>
>>> Hi, Kirill,
>>>
>>>> Hi, Andrey,
>>>>
>>>> On 11.11.2020 10:32, Andrey Zhadchenko wrote:
>>>>> Use more generic igrab instead of atomic inc. Move cgroup_hash_del
>>>>> to eviction stage to avoid deadlock.
>>>>>
>>>>> Signed-off-by: Andrey Zhadchenko <andrey.zhadchenko@virtuozzo.com>
>>>>> ---
>>>>>
>>>>> v2: adjusted function call order in cgroup_evict_inode to match
>>>>> existing code
>>>>>
>>>>>   kernel/cgroup.c | 25 ++++++++-----------------
>>>>>   1 file changed, 8 insertions(+), 17 deletions(-)
>>>>>
>>>>> diff --git a/kernel/cgroup.c b/kernel/cgroup.c
>>>>> index 27d7a5e..8c2cef8 100644
>>>>> --- a/kernel/cgroup.c
>>>>> +++ b/kernel/cgroup.c
>>>>> @@ -1522,21 +1522,10 @@ static struct inode
>>>>> *cgroup_find_inode(unsigned long fh[2], char take_ref) struct inode
>>>>> *ret = NULL;
>>>>>       spin_lock(&cgroup_inode_table_lock);
>>>>> -    item = cgroup_find_item_no_lock(fh);
>>>>>   -    /*
>>>>> -     * If we need to increase refcount, we should be aware of
>>>>> possible
>>>>> -     * deadlock. Another thread may have started deleting this
>>>>> inode:
>>>>> -     * iput->iput_final->cgroup_delete_inode->cgroup_hash_del
>>>>> -     * If we just call igrab, it will try to take i_lock and
>>>>> this will
>>>>> -     * result in deadlock, because deleting thread has already
>>>>> taken it
>>>>> -     * and waits on cgroup_inode_table_lock to find inode in
>>>>> hashtable.
>>>>> -     *
>>>>> -     * If i_count is zero, someone is deleting it -> skip.
>>>>> -     */
>>>>> -    if (take_ref && item)
>>>>> -        if (!atomic_inc_not_zero(&item->inode->i_count))
>>>>> -            item = NULL;
>>>>> +    item = cgroup_find_item_no_lock(fh);
>>>>> +    if (item && take_ref && !igrab(item->inode))
>>>>> +        item = NULL;
>>>>
>>>> Here you call igrab() under cgroup_inode_table_lock, so the check for
>>>> (inode->i_state & I_FREEING|I_WILL_FREE) is done under that lock and
>>>> i_lock.
>>>>
>>>> But clear_inode() sets "inode->i_state = I_FREEING | I_CLEAR" without
>>>> any lock. This place does not look obvious. Isn't there some problem?
>>>>
>>>
>>> iput_final() sets inode to I_FREEING under i_lock, release it and calls
>>> evict(), which calls cgroup_evict_inode() via
>>> inode->i_sb->s_op->evict().
>>
>> What is about inodes in LRU?
> 
> I thought iput_final:drop is always true for cgroupfs. And there is no lru.

That is OK.
 
>>
>> Say, iput_final() moves inode to LRU with zero counter. I_FREEING is not
>> set there. Later prune_icache_sb() operates with that inode with zero counter.
>>
>> Doesn't dispose_list()->evict()->cgroup_evict_inode() race with cgroup_find_inode()?
>>
>>> If I get it right, this seems to be fine. As soon as iput_final starts
>>> working igrab shouldn't be able to take ref on this inode.
>>>
>>>>>         spin_unlock(&cgroup_inode_table_lock);
>>>>>   @@ -1634,15 +1623,17 @@ static const struct export_operations
>>>>> cgroup_export_ops = { .fh_to_dentry    = cgroup_fh_to_dentry,
>>>>>   };
>>>>>   -static int cgroup_delete_inode(struct inode *inode)
>>>>> +static void cgroup_evict_inode(struct inode *inode)
>>>>>   {
>>>>> +    truncate_inode_pages_final(&inode->i_data);
>>>>> +    clear_inode(inode);
>>>>>       cgroup_hash_del(inode);
>>>>> -    return generic_delete_inode(inode);
>>>>>   }
>>>>>     static const struct super_operations cgroup_ops = {
>>>>>       .statfs = simple_statfs,
>>>>> -    .drop_inode = cgroup_delete_inode,
>>>>> +    .drop_inode = generic_delete_inode,
>>>>> +    .evict_inode = cgroup_evict_inode,
>>>>>       .show_options = cgroup_show_options,
>>>>>   #ifdef CONFIG_VE
>>>>>       .show_path = cgroup_show_path,
>>>>>    
>>>>
>>>
>>
>
Pavel Tikhomirov Nov. 25, 2020, 8:04 a.m.
(also to @vvs) We should probably add `Fixes: ("cgroup: add 
export_operations to cgroup super block")` to the commit description, so 
that it can be merged on next rebase to the original patch if needed.

Reviewed-by: Pavel Tikhomirov <ptikhomirov@virtuozzo.com>

On 11/11/20 10:32 AM, Andrey Zhadchenko wrote:
> Use more generic igrab instead of atomic inc. Move cgroup_hash_del to eviction
> stage to avoid deadlock.
> 
> Signed-off-by: Andrey Zhadchenko <andrey.zhadchenko@virtuozzo.com>
> ---
> 
> v2: adjusted function call order in cgroup_evict_inode to match existing code
> 
>   kernel/cgroup.c | 25 ++++++++-----------------
>   1 file changed, 8 insertions(+), 17 deletions(-)
> 
> diff --git a/kernel/cgroup.c b/kernel/cgroup.c
> index 27d7a5e..8c2cef8 100644
> --- a/kernel/cgroup.c
> +++ b/kernel/cgroup.c
> @@ -1522,21 +1522,10 @@ static struct inode *cgroup_find_inode(unsigned long fh[2], char take_ref)
>   	struct inode *ret = NULL;
>   
>   	spin_lock(&cgroup_inode_table_lock);
> -	item = cgroup_find_item_no_lock(fh);
>   
> -	/*
> -	 * If we need to increase refcount, we should be aware of possible
> -	 * deadlock. Another thread may have started deleting this inode:
> -	 * iput->iput_final->cgroup_delete_inode->cgroup_hash_del
> -	 * If we just call igrab, it will try to take i_lock and this will
> -	 * result in deadlock, because deleting thread has already taken it
> -	 * and waits on cgroup_inode_table_lock to find inode in hashtable.
> -	 *
> -	 * If i_count is zero, someone is deleting it -> skip.
> -	 */
> -	if (take_ref && item)
> -		if (!atomic_inc_not_zero(&item->inode->i_count))
> -			item = NULL;
> +	item = cgroup_find_item_no_lock(fh);
> +	if (item && take_ref && !igrab(item->inode))
> +		item = NULL;
>   
>   	spin_unlock(&cgroup_inode_table_lock);
>   
> @@ -1634,15 +1623,17 @@ static const struct export_operations cgroup_export_ops = {
>   	.fh_to_dentry	= cgroup_fh_to_dentry,
>   };
>   
> -static int cgroup_delete_inode(struct inode *inode)
> +static void cgroup_evict_inode(struct inode *inode)
>   {
> +	truncate_inode_pages_final(&inode->i_data);
> +	clear_inode(inode);
>   	cgroup_hash_del(inode);
> -	return generic_delete_inode(inode);
>   }
>   
>   static const struct super_operations cgroup_ops = {
>   	.statfs = simple_statfs,
> -	.drop_inode = cgroup_delete_inode,
> +	.drop_inode = generic_delete_inode,
> +	.evict_inode = cgroup_evict_inode,
>   	.show_options = cgroup_show_options,
>   #ifdef CONFIG_VE
>   	.show_path = cgroup_show_path,
>