Re: [PATCH] libceph: Always remove requests from ceph_osdc_handle_map() structures

Raphael Zimmer <[email protected]>
Newsgroups org.kernel.vger.ceph-devel
Message-ID <[email protected]>
On 30.06.26 6:06 PM, Raphael Zimmer wrote:
> On 29.06.26 6:43 PM, Viacheslav Dubeyko wrote:
>> On Mon, 2026-06-29 at 09:12 -0700, Viacheslav Dubeyko wrote:
>>> CC: [email protected]
>>>
>>> On Wed, 2026-06-17 at 15:13 +0200, Raphael Zimmer wrote:
>>>> ceph_osdc_handle_map() creates two local stack variables that track
>>>> (linger) requests that need to be resent. For requests, this is an
>>>> rb_root, and for linger requests, a list_head. Subsequently,
>>>> handle_one_map() is called, which calls scan_requests() for each
>>>> osd.
>>>> In scan_requests, the new target is calculated for each (linger)
>>>> request, and, in case of CALC_TARGET_NEED_RESEND, they are inserted
>>>> into
>>>> the previously created list/rbtree. All requests in these
>>>> structures
>>>> are
>>>> kicked off to their new target osd after processing all osdmaps. If
>>>> an
>>>> error occurs during processing, e.g., because an osdmap is
>>>> corrupted,
>>>> ceph_osdc_handle_map() jumps to the bad label and doesn't call
>>>> kick_requests(). Therefore, the (linger) requests are still
>>>> inserted
>>>> in
>>>> the list/rbtree, which now doesn't have a head/root anymore. This
>>>> results in requests containing invalid pointers to stack memory.
>>>> Subsequently, this can have multiple effects, ranging from not
>>>> being
>>>> able to insert linger requests again because they still have prev
>>>> and
>>>> next pointers or triggering a BUG_ON() assertion when trying to
>>>> insert a
>>>> non-empty rb_node again, to overwriting stack memory.
>>>>
>>>> This patch fixes the issue by making sure all (linger) requests are
>>>> removed from the list/rbtree in case the osdmap processing returns
>>>> an
>>>> error. This is accomplished by adding logic that is executed after
>>>> a
>>>> jump to the bad label performing the removal similar to
>>>> kick_requests.
>>>>
>>>> Fixes: 5aea3dcd5021 ("libceph: a major OSD client update")
>>>> Fixes: 922dab613417 ("libceph, rbd: ceph_osd_linger_request,
>>>> watch/notify v2")
>>>> Signed-off-by: Raphael Zimmer <[email protected]>
>>>> ---
>>>>  net/ceph/osd_client.c | 21 +++++++++++++++++++++
>>>>  1 file changed, 21 insertions(+)
>>>>
>>>> diff --git a/net/ceph/osd_client.c b/net/ceph/osd_client.c
>>>> index 5fc79c29aab0..a4d3cbfd27a3 100644
>>>> --- a/net/ceph/osd_client.c
>>>> +++ b/net/ceph/osd_client.c
>>>> @@ -4129,6 +4129,26 @@ static void kick_requests(struct
>>>> ceph_osd_client *osdc,
>>>>  	}
>>>>  }
>>>>  
>>>> +static void clear_resend_requests(struct ceph_osd_client *osdc,
>>
>> This function is small enough. So, we can declare the function as
>> static inline.
>>
>> As far as I can see, osdc parameter hasn't been used in function at
>> all. I assume that it is not necessary.
>>
> 
> Yes, you’re right, I will fix this.
> 
>>>> +				  struct rb_root *need_resend,
>>>> +				  struct list_head
>>>> *need_resend_linger)
>>>> +{
>>>> +	struct ceph_osd_linger_request *lreq, *nlreq;
>>>> +	struct rb_node *n;
>>>> +
>>>> +	for (n = rb_first(need_resend); n; ) {
>>
>> This for() declaration looks really weird. I think you need to
>> implement something like this:
>>
>> n = rb_first(need_resend);
>>
>> while (n) {
>>     <execute logic>
>> };
>>
> 
> I have to admit that I took this from kick_requests(). For me, the
> question is rather whether we should remove this duplicate code.
> However, I didn’t want to modify kick_requests(), and it does much more
> than just this removal in an intertwined way. If we stay with this
> solution, I wouldn’t really want to introduce yet another kind of loop.
> 
>>>> +		struct ceph_osd_request *req =
>>>> +		    rb_entry(n, struct ceph_osd_request, r_node);
>>
>> I think you need to move the struct ceph_osd_request *req into
>> declaration section, then you will be able to have assignment on one
>> line:
>>
>> req = rb_entry(n, struct ceph_osd_request, r_node);
>>
> 
> This would be possible. Again, kick_requests() also uses this pattern.
> Moreover, it seems to be consistently used in the surrounding functions,
> e.g. scan_requests() or for a ceph_pg_pool_info in set_pool_was_full().
> To preserve consistency, I would prefer to either modify it everywhere
> or not at all.
> 
>>>> +
>>>> +		n = rb_next(n);
>>>> +		erase_request(need_resend, req);
>>
>> The erase_request() has BUG_ON() for empty node:
>>
>> BUG_ON(RB_EMPTY_NODE(&t->r_node));
>>
>> Are we sure that we will have not empty node always?
>>
> 
> We got this node from rb_first()/rb_entry() directly beforehand.
> Therefore, it should never be empty.
> 
> 
>> I think that we need to call the erase_request() at first and, then, to
>> call the rb_next(). It sounds more reasonable for my taste.
>>
> 
> If we call erase_request() first, the request will be erased from the
> rbtree, and we can’t call rb_next() on it anymore.
> 
>> I am guessing... Should we re-link these requests to osdc->homeless_osd
>> so they can be retried? These requests are orphaned and it will be
>> never resent. It sounds like any waiters will hang until timeout.
>>
> 
> I’m not sure. The problem occurs if the osdmap is corrupted in a way
> that a processing error occurs after at least once successfully calling
> scan_requests() from handle_one_map(). That means we will have an
> updated osdmap afterwards, but the requests will still be linked to the
> osds they were previously linked to. If the processing fails because the
> osdmap was corrupted, the other parts of it may also have been
> corrupted, and the old mapping would still be correct for some or all
> requests. Therefore, it could be a reasonable choice to leave the
> mapping as it is, until another osdmap arrives that is valid. However,
> if it was not completely corrupted, remapping to the homeless_osd could
> also be the better choice. If the changed mapping has not been corrupted
> at all, it could even be best to simply call kick_requests() and resend
> these requests to their new osds. In this case, we wouldn't need to
> introduce this new function here at all.
> 
> What do you think about it? Would you still argue for linking it to the
> homeless_osd or prefer one of the other solutions?
> 

Hi,
do you have any opinion on the open points by now so that I can create a
new version of this patch incorporating them?

Best regards,
Raphael
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.