From patchwork Thu Dec 4 10:56:43 2014 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Takashi Iwai X-Patchwork-Id: 5437371 Return-Path: X-Original-To: patchwork-dri-devel@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork1.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.19.201]) by patchwork1.web.kernel.org (Postfix) with ESMTP id 07DB39F1D4 for ; Thu, 4 Dec 2014 10:56:51 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id 2475620353 for ; Thu, 4 Dec 2014 10:56:50 +0000 (UTC) Received: from gabe.freedesktop.org (gabe.freedesktop.org [131.252.210.177]) by mail.kernel.org (Postfix) with ESMTP id 06BEC2024D for ; Thu, 4 Dec 2014 10:56:49 +0000 (UTC) Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id 1FE856E12C; Thu, 4 Dec 2014 02:56:47 -0800 (PST) X-Original-To: dri-devel@lists.freedesktop.org Delivered-To: dri-devel@lists.freedesktop.org Received: from mx2.suse.de (cantor2.suse.de [195.135.220.15]) by gabe.freedesktop.org (Postfix) with ESMTP id 8F6706E377 for ; Thu, 4 Dec 2014 02:56:45 -0800 (PST) Received: from relay1.suse.de (charybdis-ext.suse.de [195.135.220.254]) by mx2.suse.de (Postfix) with ESMTP id DD847AD05; Thu, 4 Dec 2014 10:56:44 +0000 (UTC) From: Takashi Iwai To: dri-devel@lists.freedesktop.org Subject: [PATCH 2/2] drm: Implement nonblocking mode in drm_read() Date: Thu, 4 Dec 2014 11:56:43 +0100 Message-Id: <1417690603-1595-2-git-send-email-tiwai@suse.de> X-Mailer: git-send-email 2.1.3 In-Reply-To: <1417690603-1595-1-git-send-email-tiwai@suse.de> References: <1417690603-1595-1-git-send-email-tiwai@suse.de> X-BeenThere: dri-devel@lists.freedesktop.org X-Mailman-Version: 2.1.18 Precedence: list List-Id: Direct Rendering Infrastructure - Development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Errors-To: dri-devel-bounces@lists.freedesktop.org Sender: "dri-devel" X-Spam-Status: No, score=-4.2 required=5.0 tests=BAYES_00, RCVD_IN_DNSWL_MED, T_RP_MATCHES_RCVD, 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 drm_read() always blocks the read until an event comes up even if the file is opened in nonblocking mode. Also, there is a potential race between the wake_event_interruptible() call and the actual event retrieval in drm_dequeue_event(), and it may result in zero event read even in the blocking mode. This patch fixes these issues. The fixes are two folds: - Do drm_dequeue_event() at first, then call wait_event_interruptible() in a loop only if no event is pending. It's safe to call drm_dequeue_event() at first since it returns immediately if no event is found, and the function is protected properly by event_lock. - Add a f_flags check when drm_dequeue_event() fails and returns immediately in nonblocking mode. This is evaluated only at the first read. Note: originally this issue comes up while debugging the hangup of X at switching VT quickly. For example, running the below on KDE results in the stall of X: for i in $(seq 1 100); do chvt 1; chvt 7; done This was reproduced on various Intel machines. I took a stack trace of the hanging process, and it points to the blockage in drm_read(). This patch "fixes" such a bug. A drawback is that now it becomes more difficult to find out such a buggy code in the caller side. Signed-off-by: Takashi Iwai --- drivers/gpu/drm/drm_fops.c | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/drm/drm_fops.c b/drivers/gpu/drm/drm_fops.c index a82dc28d54f3..04833eaf51b3 100644 --- a/drivers/gpu/drm/drm_fops.c +++ b/drivers/gpu/drm/drm_fops.c @@ -515,13 +515,20 @@ ssize_t drm_read(struct file *filp, char __user *buffer, size_t total; ssize_t ret; - ret = wait_event_interruptible(file_priv->event_wait, - !list_empty(&file_priv->event_list)); - if (ret < 0) - return ret; - total = 0; - while (drm_dequeue_event(file_priv, total, count, &e)) { + for (;;) { + if (!drm_dequeue_event(file_priv, total, count, &e)) { + if (total) + break; + if (filp->f_flags & O_NONBLOCK) + return -EAGAIN; + ret = wait_event_interruptible(file_priv->event_wait, + !list_empty(&file_priv->event_list)); + if (ret < 0) + return ret; + continue; + } + if (copy_to_user(buffer + total, e->event, e->event->length)) { total = -EFAULT;