lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <80ab3182-5a17-7434-9007-33eb1da46d85@colorfullife.com>
Date:   Mon, 20 Apr 2020 17:35:20 +0200
From:   Manfred Spraul <manfred@...orfullife.com>
To:     Matthew Wilcox <willy@...radead.org>,
        linux-fsdevel@...r.kernel.org, linux-kernel@...r.kernel.org,
        Andrew Morton <akpm@...ux-foundation.org>
Subject: Re: [PATCH] ipc: Convert ipcs_idr to XArray

Hi Matthew,

On 3/26/20 4:14 PM, Matthew Wilcox wrote:
> From: "Matthew Wilcox (Oracle)" <willy@...radead.org>
>
> The XArray has better loops than the IDR has, removing the need to
> open-code them.  We also don't need to call idr_destroy() any more.
> Allocating the ID is a little tricky due to needing to get 'seq'
> correct.  Open-code a variant of __xa_alloc() which lets us set the
> ID and the seq before depositing the pointer in the array.
>
> Signed-off-by: Matthew Wilcox <willy@...radead.org>
> ---

> -		max_idx = max(ids->in_use*3/2, ipc_min_cycle);
> -		max_idx = min(max_idx, ipc_mni);
> -
> -		/* allocate the idx, with a NULL struct kern_ipc_perm */
> -		idx = idr_alloc_cyclic(&ids->ipcs_idr, NULL, 0, max_idx,
> -					GFP_NOWAIT);
> -
> -		if (idx >= 0) {
> -			/*
> -			 * idx got allocated successfully.
> -			 * Now calculate the sequence number and set the
> -			 * pointer for real.
> -			 */
> -			if (idx <= ids->last_idx) {
> +		min_idx = ids->next_idx;
> +		new->seq = ids->seq;
> +
> +		/* Modified version of __xa_alloc */
> +		do {
> +			xas.xa_index = min_idx;
> +			xas_find_marked(&xas, max_idx, XA_FREE_MARK);
> +			if (xas.xa_node == XAS_RESTART && min_idx > 0) {
>   				ids->seq++;
>   				if (ids->seq >= ipcid_seq_max())
>   					ids->seq = 0;
> +				new->seq = ids->seq;
> +				xas.xa_index = 0;
> +				min_idx = 0;
> +				xas_find_marked(&xas, max_idx, XA_FREE_MARK);
>   			}

Is is nessary to have that many details of xarray in ipc/util?

This function is not performance critical.

The core requirement is that ipc_obtain_object_check() must scale.

Would it be possible to use something like

     xa_alloc(,entry=NULL,)

     new->seq = ...

     xa_store(,entry=new,);

> -			ids->last_idx = idx;
> -
> -			new->seq = ids->seq;
> -			/* no need for smp_wmb(), this is done
> -			 * inside idr_replace, as part of
> -			 * rcu_assign_pointer
> -			 */

Could you leave the memory barrier comments in the code?

The rcu_assign_pointer() is the first hands-off from semget() or msgget().

Before the rcu_assign_pointer, e.g. semop() calls would return -EINVAL;

After the rcu_assign_pointer, semwhatever() must work - and e.g. the 
permission checks are lockless.

> -			idr_replace(&ids->ipcs_idr, new, idx);
> -		}
> +			if (xas.xa_node == XAS_RESTART)
> +				xas_set_err(&xas, -ENOSPC);
> +			else
> +				new->id = (new->seq << ipcmni_seq_shift()) +
> +					xas.xa_index;

Setting new->id should remain at the end, outside any locking:

The variable has no special protection, access is only allowed after 
proper locking, thus no need to have the initialization in the middle.

What is crucial is that the final value of new->seq is visible to all 
cpus before a storing the pointer.
> +			xas_store(&xas, new);
> +			xas_clear_mark(&xas, XA_FREE_MARK);
> +		} while (__xas_nomem(&xas, GFP_KERNEL));
> +

Just for my curiosity:

If the xas is in an invalid state, then xas_store() will not store anything.
Thus the loop will not store "new" multiple times, it will be stored 
only once.

@@ -472,7 +487,7 @@ void ipc_rmid(struct ipc_ids *ids, struct 
kern_ipc_perm *ipcp)
>   			idx--;
>   			if (idx == -1)
>   				break;
> -		} while (!idr_find(&ids->ipcs_idr, idx));
> +		} while (!xa_load(&ids->ipcs, idx));
>   		ids->max_idx = idx;
>   	}
>   }

Is there an xa_find_last() function?

It is outside of any hot path, I have a patch that does a binary search 
with idr_get_next().

If there is no xa_find_last(), then I would rebase that patch.


--

     Manfred

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ