Return-Path: Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1751478AbdH3N1U (ORCPT ); Wed, 30 Aug 2017 09:27:20 -0400 Received: from mx1.redhat.com ([209.132.183.28]:50970 "EHLO mx1.redhat.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1751302AbdH3N1S (ORCPT ); Wed, 30 Aug 2017 09:27:18 -0400 DMARC-Filter: OpenDMARC Filter v1.3.2 mx1.redhat.com 7BA70C0587E9 Authentication-Results: ext-mx08.extmail.prod.ext.phx2.redhat.com; dmarc=none (p=none dis=none) header.from=redhat.com Authentication-Results: ext-mx08.extmail.prod.ext.phx2.redhat.com; spf=fail smtp.mailfrom=joe.lawrence@redhat.com Date: Wed, 30 Aug 2017 09:27:16 -0400 From: Joe Lawrence To: Josh Poimboeuf Cc: live-patching@vger.kernel.org, linux-kernel@vger.kernel.org, Jessica Yu , Jiri Kosina , Miroslav Benes , Petr Mladek , Chris J Arges Subject: Re: [PATCH v4 1/3] livepatch: add (un)patch callbacks Message-ID: <20170830132716.6tdjkfafpuay36cs@redhat.com> References: <1503688202-12121-1-git-send-email-joe.lawrence@redhat.com> <1503688202-12121-2-git-send-email-joe.lawrence@redhat.com> <20170829154902.sw7r4k3ofo7hv4ev@treble> <20170829195912.m7wdoymu7fjmoioi@treble> MIME-Version: 1.0 Content-Type: text/plain; charset=us-ascii Content-Disposition: inline In-Reply-To: <20170829195912.m7wdoymu7fjmoioi@treble> User-Agent: Mutt/1.6.2-neo (2016-08-08) X-Greylist: Sender IP whitelisted, not delayed by milter-greylist-4.5.16 (mx1.redhat.com [10.5.110.32]); Wed, 30 Aug 2017 13:27:18 +0000 (UTC) Sender: linux-kernel-owner@vger.kernel.org List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Length: 6301 Lines: 133 On Tue, Aug 29, 2017 at 02:59:12PM -0500, Josh Poimboeuf wrote: > On Tue, Aug 29, 2017 at 03:22:06PM -0400, Joe Lawrence wrote: > > On 08/29/2017 11:49 AM, Josh Poimboeuf wrote: > > > On Fri, Aug 25, 2017 at 03:10:00PM -0400, Joe Lawrence wrote: > > >> +Test 6 > > >> +------ > > >> + > > >> +Test a scenario where a vmlinux pre-patch callback returns a non-zero > > >> +status (ie, failure): > > >> + > > >> +- load target module > > >> +- load livepatch -ENODEV > > >> +- unload target module > > >> + > > >> +First load a target module: > > >> + > > >> + % insmod samples/livepatch/livepatch-callbacks-mod.ko > > >> + [ 80.740520] livepatch_callbacks_mod: livepatch_callbacks_mod_init > > >> + > > >> +Load the livepatch module, setting its 'pre_patch_ret' value to -19 > > >> +(-ENODEV). When its vmlinux pre-patch callback executed, this status > > >> +code will propagate back to the module-loading subsystem. The result is > > >> +that the insmod command refuses to load the livepatch module: > > >> + > > >> + % insmod samples/livepatch/livepatch-callbacks-demo.ko pre_patch_ret=-19 > > >> + [ 82.747326] livepatch: enabling patch 'livepatch_callbacks_demo' > > >> + [ 82.747743] livepatch: 'livepatch_callbacks_demo': initializing unpatching transition > > >> + [ 82.747767] livepatch_callbacks_demo: pre_patch_callback: vmlinux > > >> + [ 82.748237] livepatch: pre-patch callback failed for object 'vmlinux' > > >> + [ 82.748637] livepatch: failed to enable patch 'livepatch_callbacks_demo' > > >> + [ 82.749059] livepatch: 'livepatch_callbacks_demo': canceling transition, unpatching > > >> + [ 82.749060] livepatch: 'livepatch_callbacks_demo': completing unpatching transition > > >> + [ 82.749177] livepatch_callbacks_demo: post_unpatch_callback: livepatch_callbacks_mod -> [MODULE_STATE_LIVE] Normal state > > >> + [ 82.749868] livepatch: 'livepatch_callbacks_demo': unpatching complete > > >> + [ 82.765809] insmod: ERROR: could not insert module samples/livepatch/livepatch-callbacks-demo.ko: No such device > > >> + > > >> + % rmmod samples/livepatch/livepatch-callbacks-mod.ko > > >> + [ 84.774238] livepatch_callbacks_mod: livepatch_callbacks_mod_exit > > > > > > First off, this documentation is very nice because it clarifies all the > > > callback scenarios and edge cases. > > > > > > The above situation still seems a little odd to me. If I understand > > > correctly, the target module was never patched, and its pre_patch > > > callback was never called. But its post_unpatch callback *was* called. > > > That doesn't seem right. > > > > Ah, this does look to be a bug. > > > > > Maybe we should change the condition a little bit. Currently it's: > > > > > > No post-patch, pre-unpatch, or post-unpatch callbacks will be executed > > > for a given klp_object if its pre-patch callback returned non-zero > > > status. > > > > > > I think that might have been my idea, but seeing the above case makes it > > > clear that it's not quite right. > > > > It could have been correct if the code differentiated between a > > never-run pre_patch_status of 0 (by kzalloc) and a successful > > pre_patch_status of 0 (by callback return), I think. > > > > > Maybe it should instead be: > > > > > > No post-patch, pre-unpatch, or post-unpatch callbacks will be executed > > > for a given klp_object if the object failed to patch, due to a failed > > > pre_patch callback or for any other reason. > > > > > > If the object did successfully patch, but the patch transition never > > > started for some reason (e.g., if another object failed to patch), > > > only the post-unpatch callback will be called. > > > > That description sounds correct... > > > > > So then, instead of tracking whether the pre-patch callback succeeded, > > > we just need to track whether the object was patched (which we already > > > do, with obj->patched). > > > > > > What do you think? > > > > I think this would only work if there was a sticky > > "obj->was_ever_patched" variable. We moved the post-unpatch-callback to > > the very end of klp_complete_transition()... by that point, obj->patched > > will have already been cleared by klp_unpatch_objects. > > > > We could maybe move obj->patched assignments out to encapsulate the pre > > and post callbacks... but I would need to think about that a while. It > > seems pretty clear and symmetric as it is today (immediately set in > > klp_(un)patch_object(). > > > > Perhaps a more careful checking of obj->pre_patch_callback_status is all > > we need? (I can't think of anything more succinct than adding a > > obj->pre_patch_callback_done variable to the mix.) > > Makes sense. I think you're right that obj->patched wouldn't work. > > But there's one more weird case I didn't mention. If the patch has a > post-unpatch callback, but it doesn't have a pre-patch callback, then > 'obj->pre_patch_callback_done' would never get set and the post-unpatch > callback would never get called, even if the patch was successful. Interesting case. I didn't code anything up, but the idea was that the other callbacks would only run iff pre_patch_done && status == 0 || !pre_patch_callback. But the following suggestion is clearer IMHO ... > So instead of 'obj->pre_patch_callback_done', how about > 'obj->callbacks_enabled'? > > It could be set in the following cases: > > a) if the object has a pre_patch callback, set obj->callbacks_enabled > after the pre_patch callback succeeds; > > b) else, if the patch does *not* have a pre_patch callback, set > obj->callbacks_enabled after klp_patch_object() succeeds. > > And the variable would need to be cleared after the post_unpatch > callback was run. > > It's a bit complicated, but that seems to be the most logicial behavior > as far as I can tell. > > Thoughts? What if we flip it around as "callbacks_disabled"? By default, kzalloc would init as false. It would only be set to true if the pre-patch callback is provided and if it returns failure. Would that reduce the number of conditions when we need to set this var? Also, as you noted, I think it would need to reset/cleared after the post-patch callback. (For the livepatch-already-loaded cases.) -- Joe