Project

General

Profile

Actions

Bug #64179

closed

client: check for caps issued before incrementing cap ref

Added by Dhairya Parmar 4 months ago. Updated 3 months ago.

Status:
Rejected
Priority:
High
Category:
Correctness/Safety
Target version:
% Done:

0%

Source:
Development
Tags:
Backport:
quincy,reef
Regression:
No
Severity:
3 - minor
Reviewed:
Affected Versions:
ceph-qa-suite:
Component(FS):
Client
Labels (FS):
Pull request ID:
Crash signature (v1):
Crash signature (v2):

Description

So this came to light when debugging cap leak issue in asyc I/O, while discussing this with xiubo today, we came to know that we do not actually check for the issued caps to the inode before incrementing the respective ref count for that inode in Client::get_cap_ref. The client lock does only make sure no multiple clients operate over the same inode, which leaves to MDS lock, does it guarantee the caps? I don't know. What i think is we should check for the caps issued before incrementing the ref count in `Client::get_cap_ref`

Actions #1

Updated by Venky Shankar 3 months ago

  • Assignee set to Dhairya Parmar
  • Target version set to v19.0.0
  • Backport set to quincy,reef
  • Severity changed from 2 - major to 3 - minor

Let's get an analysis done how get_cap_ref() is called for a cap that the client does not have.

Actions #2

Updated by Dhairya Parmar 3 months ago

Venky Shankar wrote:

Let's get an analysis done how get_cap_ref() is called for a cap that the client does not have.

This was more of a - if the MDS hasn't issued caps and the client is requesting for that cap ref then we must disallow this

Actions #3

Updated by Dhairya Parmar 3 months ago

in Client::get_caps, we check for the caps issued to the inode

int have = in->caps_issued(&implemented);

then

if ((have & need) == need) {
    int revoking = implemented & ~have;
    ldout(cct, 10) << "get_caps " << *in << " have " << ccap_string(have)
                << " need " << ccap_string(need) << " want " << ccap_string(want)
                << " revoking " << ccap_string(revoking)
                << dendl;
    if ((revoking & want) == 0) {
        *phave = need | (have & want);
        in->get_cap_ref(need);
        cap_hit();
        return 0;
    }
}

i.e. we only call get_cap_ref if the `need` set bits align with the caps issued, now if we check the Client::get_cap_ref or Inode::get_cap_ref code:

void Client::get_cap_ref(Inode *in, int cap)
{
  if ((cap & CEPH_CAP_FILE_BUFFER) &&
      in->cap_refs[CEPH_CAP_FILE_BUFFER] == 0) {
    ldout(cct, 5) << __func__ << " got first FILE_BUFFER ref on " << *in << dendl;
    in->iget();
  }
  if ((cap & CEPH_CAP_FILE_CACHE) &&
      in->cap_refs[CEPH_CAP_FILE_CACHE] == 0) {
    ldout(cct, 5) << __func__ << " got first FILE_CACHE ref on " << *in << dendl;
    in->iget();
  }
  in->get_cap_ref(cap);
}

void Inode::get_cap_ref(int cap)
{
  int n = 0;
  while (cap) {
    if (cap & 1) {
      int c = 1 << n;
      cap_refs[c]++;
      //cout << "inode " << *this << " get " << cap_string(c) << " " << (cap_refs[c]-1) << " -> " << cap_refs[c] << std::endl;
    }
    cap >>= 1;
    n++;
  }
}

We never check for the issued caps.

However nowhere in our code have we ever incremented ref count of a cap that was not issued/implemented but as a general practice we must.

Actions #4

Updated by Venky Shankar 3 months ago

Dhairya, is this really an issue after all? If the cap refs are accounted as expected, then is there a need to change that behaviour?

Actions #5

Updated by Dhairya Parmar 3 months ago

Venky Shankar wrote:

Dhairya, is this really an issue after all? If the cap refs are accounted as expected, then is there a need to change that behaviour?

its fine, as i said im seeing this more of a general practice to first verify and then increment.

Actions #6

Updated by Venky Shankar 3 months ago

  • Status changed from New to Rejected

Does not result in wide improvement in any sense.

Actions

Also available in: Atom PDF