Received: by 2002:a25:e74b:0:0:0:0:0 with SMTP id e72csp72996ybh; Fri, 17 Jul 2020 19:14:24 -0700 (PDT) X-Google-Smtp-Source: ABdhPJyMO6OFkJRbmA5+ptpbVqjzAb107dVVzfX3goWPzjPZ/2s2qTf4Ok7n4t+45QXpJsf9Jbtn X-Received: by 2002:a05:6402:3048:: with SMTP id bu8mr12386269edb.367.1595038464048; Fri, 17 Jul 2020 19:14:24 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1595038464; cv=none; d=google.com; s=arc-20160816; b=vzL+OPUCUgiO/+tGrw5G5LedzLfCC7eodBjrPWVZmXA/fOuPAwbAB6UXRvlpCGawBW kEmaIDsb5GPhm5JB2O37SgPjV1Jr6PafusphJsvIJKoCS4KCKY+V74hd27c//mIN5PRi pQ2jlc07hE5vsv1ox36kJm+OY/vnJ4uCjVetIEgLCYBS/wKWm0ldgPjW0iDhFidfjjCG c97P9ghTJg6iNa7LD2L+g1Oe7LFDd9WPM6rkfdF4JjVQUhECsEV1froaBti4RfwZPAFF nbevLD92kchvmsHVqxNH5X8ClDxiOi7hD/fc1DkTkRqe795Gf8KjumFdizzDxTDDPOva m73Q== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:sender:in-reply-to:content-disposition :mime-version:references:message-id:subject:cc:to:from:date :dkim-signature; bh=b6Lq/lewhmuN9xzWi3dHd0xI6Cg0i4KUL37C2Kg1dS8=; b=ICr86RGLs1KCpbNUt39zTmqrDkISlN3aoN2YYx8UjA8pQ9dO93cVaMEdWWr7jjz5XG vFAwWXUgk7H4UXfWaBzlUCc6WDVm43cJ+18NNmeOMb7tfMf2swfuaB+TOTD75GBPGkOg ClDCijQzn8Pp2FHAMJmpLnLylGAe4TbNWn4CWvEoIM1zMHZG1fnZpv5clFvb16RxMfrr Mqyj2uokArddUpdKmM6sG3soTVjFxe0yi5FG9V0YNdw0iVCHhDoWsLfj1nF5fo0jedSn Lodey/eBPqxxU4frIByiS4QHH7bIEF9klYlBfeI4Re5xg/jB6bBRhDce0nx828nSB/Bk gzMA== ARC-Authentication-Results: i=1; mx.google.com; dkim=fail header.i=@infradead.org header.s=casper.20170209 header.b=HophwgPL; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org Return-Path: Received: from vger.kernel.org (vger.kernel.org. [23.128.96.18]) by mx.google.com with ESMTP id mm15si5885258ejb.561.2020.07.17.19.13.37; Fri, 17 Jul 2020 19:14:24 -0700 (PDT) Received-SPF: pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) client-ip=23.128.96.18; Authentication-Results: mx.google.com; dkim=fail header.i=@infradead.org header.s=casper.20170209 header.b=HophwgPL; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1727912AbgGRCNI (ORCPT + 99 others); Fri, 17 Jul 2020 22:13:08 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:39610 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726665AbgGRCNI (ORCPT ); Fri, 17 Jul 2020 22:13:08 -0400 Received: from casper.infradead.org (casper.infradead.org [IPv6:2001:8b0:10b:1236::1]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 080E0C0619D2; Fri, 17 Jul 2020 19:13:08 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=infradead.org; s=casper.20170209; h=In-Reply-To:Content-Type:MIME-Version: References:Message-ID:Subject:Cc:To:From:Date:Sender:Reply-To: Content-Transfer-Encoding:Content-ID:Content-Description; bh=b6Lq/lewhmuN9xzWi3dHd0xI6Cg0i4KUL37C2Kg1dS8=; b=HophwgPLXdiolehFqCeYvePvXL mriM58eqE9tYcmpXbUWG10+WG8uvKd6NEuRdIlMGpFhQug0EDt90vDBzTAK9ytr9agSs9T6JwgQJA enP39pgqZ1bPQn7D7t+Z8nSKdTfPKa18YImWxT3eb0xI91O9SF+5zZZBXnvstFoLvEqZ8zMu/q09t yOMU0piavcn8/bXAmmrPHlQWBzKysq/eq3urOQKYfUxu7NbgW4Kr4unl4jmTiYDQzsWUojYpjD9A6 3x+xXINHbilI0enTEFRP5+/o4QC9Ee2KAI/TU7zj6Zk7a4fMvWVgOyJJ9H33/odet6gNXxM7nLxWw 9YOEbGMw==; Received: from willy by casper.infradead.org with local (Exim 4.92.3 #3 (Red Hat Linux)) id 1jwcLY-0002fS-VR; Sat, 18 Jul 2020 02:13:05 +0000 Date: Sat, 18 Jul 2020 03:13:04 +0100 From: Matthew Wilcox To: Eric Biggers Cc: linux-kernel@vger.kernel.org, linux-arch@vger.kernel.org, "Paul E . McKenney" , linux-fsdevel@vger.kernel.org, Akira Yokosawa , Alan Stern , Andrea Parri , Boqun Feng , Daniel Lustig , "Darrick J . Wong" , Dave Chinner , David Howells , Jade Alglave , Luc Maranget , Nicholas Piggin , Peter Zijlstra , Will Deacon Subject: Re: [PATCH] tools/memory-model: document the "one-time init" pattern Message-ID: <20200718021304.GS12769@casper.infradead.org> References: <20200717044427.68747-1-ebiggers@kernel.org> <20200717174750.GQ12769@casper.infradead.org> <20200718013839.GD2183@sol.localdomain> MIME-Version: 1.0 Content-Type: text/plain; charset=us-ascii Content-Disposition: inline In-Reply-To: <20200718013839.GD2183@sol.localdomain> Sender: linux-kernel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org On Fri, Jul 17, 2020 at 06:38:39PM -0700, Eric Biggers wrote: > On Fri, Jul 17, 2020 at 06:47:50PM +0100, Matthew Wilcox wrote: > > On Thu, Jul 16, 2020 at 09:44:27PM -0700, Eric Biggers wrote: > > > +If that doesn't apply, you'll have to implement one-time init yourself. > > > + > > > +The simplest implementation just uses a mutex and an 'inited' flag. > > > +This implementation should be used where feasible: > > > > I think some syntactic sugar should make it feasible for normal people > > to implement the most efficient version of this just like they use locks. > > Note that the cmpxchg version is not necessarily the "most efficient". > > If the one-time initialization is expensive, e.g. if it allocates a lot of > memory or if takes a long time, it could be better to use the mutex version so > that at most one task does it. Sure, but I think those are far less common than just allocating a single thing. > > How about something like this ... > > > > once.h: > > > > static struct init_once_pointer { > > void *p; > > }; > > > > static inline void *once_get(struct init_once_pointer *oncep) > > { ... } > > > > static inline bool once_store(struct init_once_pointer *oncep, void *p) > > { ... } > > > > --- foo.c --- > > > > struct foo *get_foo(gfp_t gfp) > > { > > static struct init_once_pointer my_foo; > > struct foo *foop; > > > > foop = once_get(&my_foo); > > if (foop) > > return foop; > > > > foop = alloc_foo(gfp); > > if (!once_store(&my_foo, foop)) { > > free_foo(foop); > > foop = once_get(&my_foo); > > } > > > > return foop; > > } > > > > Any kernel programmer should be able to handle that pattern. And no mutex! > > I don't think this version would be worthwhile. It eliminates type safety due > to the use of 'void *', and doesn't actually save any lines of code. Nor does > it eliminate the need to correctly implement the cmpxchg failure case, which is > tricky (it must free the object and get the new one) and will be rarely tested. You're missing the point. It prevents people from trying to optimise "can I use READ_ONCE() here, or do I need to use smp_rmb()?" The type safety is provided by the get_foo() function. I suppose somebody could play some games with _Generic or something, but there's really no need to. It's like using a list_head and casting to the container_of. > It also forces all users of the struct to use this helper function to access it. > That could be considered a good thing, but it's also bad because even with > one-time init there's still usually some sort of ordering of "initialization" > vs. "use". Just taking a random example I'm familiar with, we do one-time init > of inode::i_crypt_info when we open an encrypted file, so we guarantee it's set > for all I/O to the file, where we then simply access ->i_crypt_info directly. > We don't want the code to read like it's initializing ->i_crypt_info in the > middle of ->writepages(), since that would be wrong. Right, and I wouldn't use this pattern for that. You can't get to writepages without having opened the file, so just initialising the pointer in open is fine. > An improvement might be to make once_store() take the free function as a > parameter so that it would handle the failure case for you: > > struct foo *get_foo(gfp_t gfp) > { > static struct init_once_pointer my_foo; > struct foo *foop; > > foop = once_get(&my_foo); > if (!foop) { > foop = alloc_foo(gfp); > if (foop) > once_store(&my_foo, foop, free_foo); Need to mark once_store as __must_check to avoid the bug you have here: foop = once_store(&my_foo, foop, free_foo); Maybe we could use a macro for once_store so we could write: void *once_get(struct init_pointer_once *); int once_store(struct init_pointer_once *, void *); #define once_alloc(s, o_alloc, o_free) ({ \ void *__p = o_alloc; \ if (__p) { \ if (!once_store(s, __p)) { \ o_free(__p); \ __p = once_get(s); \ } \ } \ __p; \ }) --- struct foo *alloc_foo(gfp_t); void free_foo(struct foo *); struct foo *get_foo(gfp_t gfp) { static struct init_pointer_once my_foo; struct foo *foop; foop = once_get(&my_foo); if (!foop) foop = once_alloc(&my_foo, alloc_foo(gfp), free_foo); return foop; } That's pretty hard to misuse (I compile-tested it, and it works).