Re: [PATCH v8] KEYS: Add a list for unreferenced keys

From: Jarkko Sakkinen
Date: Fri Apr 11 2025 - 21:31:22 EST


On Fri, Apr 11, 2025 at 11:37:25PM +0300, Jarkko Sakkinen wrote:
> On Fri, Apr 11, 2025 at 04:59:11PM +0100, David Howells wrote:
> > Jarkko Sakkinen <jarkko@xxxxxxxxxx> wrote:
> >
> > > + spin_lock_irqsave(&key_graveyard_lock, flags);
> > > + list_splice_init(&key_graveyard, &graveyard);
> > > + spin_unlock_irqrestore(&key_graveyard_lock, flags);
> >
> > I would wrap this bit in a check to see if key_graveyard is empty so that we
> > can avoid disabling irqs and taking the lock if the graveyard is empty.
>
> Can do, and does make sense.
>
> >
> > > + if (!refcount_inc_not_zero(&key->usage)) {
> >
> > Sorry, but eww. You're going to wangle the refcount twice on every key on the
> > system every time the gc does a pass. Further, in some cases inc_not_zero is
> > not the fastest op in the world.
>
> One could alternatively "test_bit(KEY_FLAG_FINAL_PUT, &key->flags)) &&
> !refcount_inc_not_zero(&key->usage))" without mb() on either side and

Refactoring the changes to key_put() would be (draft):

void key_put(struct key *key)
{
unsigned long flags;

if (!key)
return;

key_check(key);

if (!refcount_dec_and_test(&key->usage))
return;

local_irq_save(flags);

set_bit(KEY_FLAG_FINAL_PUT, &key->flags);

/* Remove user's key tracking and quota: */
if (test_bit(KEY_FLAG_IN_QUOTA, &key->flags)) {
spin_lock(&key->user->lock);
key->user->qnkeys--;
key->user->qnbytes -= key->quotalen;
spin_unlock(&key->user->lock);
}

spin_lock(&key_graveyard_lock);
list_add_tail(&key->graveyard_link, &key_graveyard);
spin_unlock(&key_graveyard_lock);

schedule_work(&key_gc_work);

local_irq_restore(flags);
}

And:

static bool key_get_gc(struct key *key)
{
return !test_bit(KEY_FLAG_FINAL_PUT, &key->flags)) && /* fast path */
refcount_inc_not_zero(&key->usage) /* slow path */
}

In the gc-loop:

if (!key_get_gc(&key)) {
key = NULL;
gc_state |= KEY_GC_REAP_AGAIN;
goto skip_dead_key;
}

[none yet compiled]

BR, Jarkko