From patchwork Thu Dec 19 17:24:41 2013 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: David Herrmann X-Patchwork-Id: 3383641 Return-Path: X-Original-To: patchwork-linux-fbdev@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork2.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.19.201]) by patchwork2.web.kernel.org (Postfix) with ESMTP id 44584C0D4A for ; Thu, 19 Dec 2013 17:26:23 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id D895A2018E for ; Thu, 19 Dec 2013 17:26:21 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 6F1CC20154 for ; Thu, 19 Dec 2013 17:26:20 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1753704Ab3LSRZr (ORCPT ); Thu, 19 Dec 2013 12:25:47 -0500 Received: from mail-ea0-f174.google.com ([209.85.215.174]:52746 "EHLO mail-ea0-f174.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1753200Ab3LSRZq (ORCPT ); Thu, 19 Dec 2013 12:25:46 -0500 Received: by mail-ea0-f174.google.com with SMTP id b10so622425eae.19 for ; Thu, 19 Dec 2013 09:25:44 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20120113; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=FMWdPVRDM5nWd8CM/6OhjUmoAIToMEUQ5EFphJway90=; b=ykt8KnlueCyku0zakXPU914ObiZdWrQX4+d4G1C48W0wSFC5TQINkJy51kiLXcvoiW rq9u+VMiMfrKYV3BJbDRnV7Hh8U6+3z4vgy4KzKNDAz+fRK7QGfOnTH0ZphyIywxh3s3 cBJe8uzv6MUlrLIaaesNP7t/rqkSHwTq6MvShNehk9cds8q4y0CIjww1ThoBIZ/62S2y 7apj7TCFn5vHZn9uVy8GB0QzimqHvuVSzq8RsddbRmgfzYeBnnbp9mYOIUgd7sHJNS73 wZ5JNgdH0UNG/FQgnhTBnlNqC8XRmIPEsfTdapALW93fLSNDDtBBcAdcByxqpw81BoaN o8tw== X-Received: by 10.15.77.134 with SMTP id p6mr1110217eey.0.1387473944540; Thu, 19 Dec 2013 09:25:44 -0800 (PST) Received: from david-ub.localdomain (stgt-5f719d74.pool.mediaWays.net. [95.113.157.116]) by mx.google.com with ESMTPSA id m1sm11143544eeg.0.2013.12.19.09.25.42 for (version=TLSv1.2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Thu, 19 Dec 2013 09:25:43 -0800 (PST) From: David Herrmann To: linux-kernel@vger.kernel.org Cc: "H. Peter Anvin" , Thomas Gleixner , Ingo Molnar , , , David Herrmann , Subject: [PATCH v4] x86: sysfb: remove sysfb when probing real hw Date: Thu, 19 Dec 2013 18:24:41 +0100 Message-Id: <1387473881-26179-1-git-send-email-dh.herrmann@gmail.com> X-Mailer: git-send-email 1.8.5.1 In-Reply-To: <1387448038-8260-1-git-send-email-dh.herrmann@gmail.com> References: <1387448038-8260-1-git-send-email-dh.herrmann@gmail.com> Sender: linux-fbdev-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-fbdev@vger.kernel.org X-Spam-Status: No, score=-7.3 required=5.0 tests=BAYES_00, DKIM_ADSP_CUSTOM_MED, DKIM_SIGNED, FREEMAIL_FROM, RCVD_IN_DNSWL_HI, RP_MATCHES_RCVD, T_DKIM_INVALID, UNPARSEABLE_RELAY autolearn=unavailable version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on mail.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP With CONFIG_X86_SYSFB=y, probing real hw-drivers may result in resource-conflicts and drivers will refuse to load. A call to request_mem_region() will fail, if the region overlaps with the mem-region used by simplefb. The common desktop DRM drivers (intel, nouveau, radeon) are not affected as they don't reserve their resources, but some others do, including (nvidiafb, cirrus, ..). The problem is that we add an IORESOURCE_MEM to the simple-framebuffer platform-device during bootup but never release it. Probing simplefb on this platform-device is fine, but the handover to real-hw via remove_conflicting_framebuffers() will only unload the fbdev driver, but keep the platform-device around. Thus, if the real hw-driver calls request_mem_region() and friends on the same PCI region, we will get a resource conflict and most drivers refuse to load. Users will see errors like: "nvidiafb: cannot reserve video memory at " vesafb and efifb don't store any resources, so disabling CONFIG_X86_SYSFB and falling back to those drivers will avoid the bug. With sysfb enabled, we need to properly unload the simple-framebuffer devices before probing real hw-drivers. This patch adds sysfb_unregister() for that purpose. It can be called from any context (except from the platform-device ->probe and ->remove callback path), synchronously unloads any global sysfb and prevents new sysfbs from getting registered. Thus, you can call it even before any sysfb has been loaded. Note that for now we only do that for simple-framebuffer devices, as efi/vesa-framebuffer platform-drivers lack ->remove() callbacks. They're not affected by the resource-conflicts, so we can fix those later. This also changes remove_conflicting_framebuffer() to call this helper *before* trying its heuristic to remove conflicting drivers. This way, we unload sysfbs properly on any conflict. But to avoid dead-locks in register_framebuffer(), we must not call sysfb_unregister() for framebuffers probing on sysfb devices. Hence, we simply remove any aperture from simplefb and we're good to go. simplefb is unregistered by sysfb_unregister() now, so no reason to keep the apertures (on non-x86, there currently is no handover from simplefb, so we're fine. If it's added, they need to provide something like sysfb_unregister() too) Cc: # v3.11+ Signed-off-by: David Herrmann Reviewed-by: Takashi Iwai Tested-by: Takashi Iwai --- v4: - change #ifdef to CONFIG_X86_SYSFB again arch/x86/include/asm/sysfb.h | 6 ++++ arch/x86/kernel/sysfb.c | 68 ++++++++++++++++++++++++++++++++++++++++ arch/x86/kernel/sysfb_simplefb.c | 9 ++---- drivers/video/fbmem.c | 19 +++++++++++ drivers/video/simplefb.c | 8 ----- 5 files changed, 95 insertions(+), 15 deletions(-) diff --git a/arch/x86/include/asm/sysfb.h b/arch/x86/include/asm/sysfb.h index 2aeb3e2..0877cf1 100644 --- a/arch/x86/include/asm/sysfb.h +++ b/arch/x86/include/asm/sysfb.h @@ -11,6 +11,7 @@ * any later version. */ +#include #include #include #include @@ -59,6 +60,11 @@ struct efifb_dmi_info { int flags; }; +int __init sysfb_register(const char *name, int id, + const struct resource *res, unsigned int res_num, + const void *data, size_t data_size); +void sysfb_unregister(const struct apertures_struct *apert, bool primary); + #ifdef CONFIG_EFI extern struct efifb_dmi_info efifb_dmi_list[]; diff --git a/arch/x86/kernel/sysfb.c b/arch/x86/kernel/sysfb.c index 193ec2c..882dc7c 100644 --- a/arch/x86/kernel/sysfb.c +++ b/arch/x86/kernel/sysfb.c @@ -33,11 +33,79 @@ #include #include #include +#include #include #include #include #include +static DEFINE_MUTEX(sysfb_lock); +static struct platform_device *sysfb_dev; + +int __init sysfb_register(const char *name, int id, + const struct resource *res, unsigned int res_num, + const void *data, size_t data_size) +{ + struct platform_device *pd; + int ret = 0; + + mutex_lock(&sysfb_lock); + if (!sysfb_dev) { + pd = platform_device_register_resndata(NULL, name, id, + res, res_num, + data, data_size); + if (IS_ERR(pd)) + ret = PTR_ERR(pd); + else + sysfb_dev = pd; + } + mutex_unlock(&sysfb_lock); + + return ret; +} + +static bool sysfb_match(const struct apertures_struct *apert, bool primary) +{ + struct screen_info *si = &screen_info; + unsigned int i; + const struct aperture *a; + + if (!apert || primary) + return true; + + for (i = 0; i < apert->count; ++i) { + a = &apert->ranges[i]; + if (a->base >= si->lfb_base && + a->base < si->lfb_base + ((u64)si->lfb_size << 16)) + return true; + if (si->lfb_base >= a->base && + si->lfb_base < a->base + a->size) + return true; + } + + return false; +} + +/* + * Unregister the sysfb and prevent new sysfbs from getting registered. Can be + * called from any context except recursively or from sysfb_register(). + * Used by remove_conflicting_framebuffers() and friends. + */ +void sysfb_unregister(const struct apertures_struct *apert, bool primary) +{ + mutex_lock(&sysfb_lock); + if (!IS_ERR(sysfb_dev) && sysfb_dev) { + if (sysfb_match(apert, primary)) { + platform_device_unregister(sysfb_dev); + sysfb_dev = ERR_PTR(-EALREADY); + } + } else { + /* if !sysfb_dev, set err so no new sysfb is probed later */ + sysfb_dev = ERR_PTR(-EALREADY); + } + mutex_unlock(&sysfb_lock); +} + static __init int sysfb_init(void) { struct screen_info *si = &screen_info; diff --git a/arch/x86/kernel/sysfb_simplefb.c b/arch/x86/kernel/sysfb_simplefb.c index 86179d4..a760d47 100644 --- a/arch/x86/kernel/sysfb_simplefb.c +++ b/arch/x86/kernel/sysfb_simplefb.c @@ -64,7 +64,6 @@ __init bool parse_mode(const struct screen_info *si, __init int create_simplefb(const struct screen_info *si, const struct simplefb_platform_data *mode) { - struct platform_device *pd; struct resource res; unsigned long len; @@ -86,10 +85,6 @@ __init int create_simplefb(const struct screen_info *si, if (res.end <= res.start) return -EINVAL; - pd = platform_device_register_resndata(NULL, "simple-framebuffer", 0, - &res, 1, mode, sizeof(*mode)); - if (IS_ERR(pd)) - return PTR_ERR(pd); - - return 0; + return sysfb_register("simple-framebuffer", 0, &res, 1, mode, + sizeof(*mode)); } diff --git a/drivers/video/fbmem.c b/drivers/video/fbmem.c index 010d191..7dc0491 100644 --- a/drivers/video/fbmem.c +++ b/drivers/video/fbmem.c @@ -35,6 +35,9 @@ #include +#ifdef CONFIG_X86_SYSFB +#include +#endif /* * Frame buffer device initialization and setup routines @@ -1604,6 +1607,17 @@ static void do_remove_conflicting_framebuffers(struct apertures_struct *a, } } +static void remove_conflicting_sysfb(const struct apertures_struct *apert, + bool primary) +{ + if (!apert) + return; + +#ifdef CONFIG_X86_SYSFB + sysfb_unregister(apert, primary); +#endif +} + static int do_register_framebuffer(struct fb_info *fb_info) { int i; @@ -1742,6 +1756,8 @@ EXPORT_SYMBOL(unlink_framebuffer); void remove_conflicting_framebuffers(struct apertures_struct *a, const char *name, bool primary) { + remove_conflicting_sysfb(a, primary); + mutex_lock(®istration_lock); do_remove_conflicting_framebuffers(a, name, primary); mutex_unlock(®istration_lock); @@ -1762,6 +1778,9 @@ register_framebuffer(struct fb_info *fb_info) { int ret; + remove_conflicting_sysfb(fb_info->apertures, + fb_is_primary_device(fb_info)); + mutex_lock(®istration_lock); ret = do_register_framebuffer(fb_info); mutex_unlock(®istration_lock); diff --git a/drivers/video/simplefb.c b/drivers/video/simplefb.c index 210f3a0..9f4a0cf 100644 --- a/drivers/video/simplefb.c +++ b/drivers/video/simplefb.c @@ -209,14 +209,6 @@ static int simplefb_probe(struct platform_device *pdev) info->var.blue = params.format->blue; info->var.transp = params.format->transp; - info->apertures = alloc_apertures(1); - if (!info->apertures) { - framebuffer_release(info); - return -ENOMEM; - } - info->apertures->ranges[0].base = info->fix.smem_start; - info->apertures->ranges[0].size = info->fix.smem_len; - info->fbops = &simplefb_ops; info->flags = FBINFO_DEFAULT | FBINFO_MISC_FIRMWARE; info->screen_base = ioremap_wc(info->fix.smem_start,