From owner-svn-src-head@freebsd.org Tue Aug 8 19:36:35 2017 Return-Path: Delivered-To: svn-src-head@mailman.ysv.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:1900:2254:206a::19:1]) by mailman.ysv.freebsd.org (Postfix) with ESMTP id 3DACADCBDCF; Tue, 8 Aug 2017 19:36:35 +0000 (UTC) (envelope-from mav@FreeBSD.org) Received: from repo.freebsd.org (repo.freebsd.org [IPv6:2610:1c1:1:6068::e6a:0]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (Client did not present a certificate) by mx1.freebsd.org (Postfix) with ESMTPS id 1A0BE7091B; Tue, 8 Aug 2017 19:36:35 +0000 (UTC) (envelope-from mav@FreeBSD.org) Received: from repo.freebsd.org ([127.0.1.37]) by repo.freebsd.org (8.15.2/8.15.2) with ESMTP id v78JaY8X074052; Tue, 8 Aug 2017 19:36:34 GMT (envelope-from mav@FreeBSD.org) Received: (from mav@localhost) by repo.freebsd.org (8.15.2/8.15.2/Submit) id v78JaY4m074051; Tue, 8 Aug 2017 19:36:34 GMT (envelope-from mav@FreeBSD.org) Message-Id: <201708081936.v78JaY4m074051@repo.freebsd.org> X-Authentication-Warning: repo.freebsd.org: mav set sender to mav@FreeBSD.org using -f From: Alexander Motin Date: Tue, 8 Aug 2017 19:36:34 +0000 (UTC) To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org Subject: svn commit: r322272 - head/sys/compat/linuxkpi/common/src X-SVN-Group: head X-SVN-Commit-Author: mav X-SVN-Commit-Paths: head/sys/compat/linuxkpi/common/src X-SVN-Commit-Revision: 322272 X-SVN-Commit-Repository: base MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-BeenThere: svn-src-head@freebsd.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: SVN commit messages for the src tree for head/-current List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Tue, 08 Aug 2017 19:36:35 -0000 Author: mav Date: Tue Aug 8 19:36:34 2017 New Revision: 322272 URL: https://svnweb.freebsd.org/changeset/base/322272 Log: Fix few issues of LinuxKPI workqueue. LinuxKPI workqueue wrappers reported "successful" cancellation for works already completed in normal way. This change brings reported status and real cancellation fact into sync. This required for drm-next operation. Reviewed by: hselasky (earlier version) Sponsored by: iXsystems, Inc. Differential Revision: https://reviews.freebsd.org/D11904 Modified: head/sys/compat/linuxkpi/common/src/linux_work.c Modified: head/sys/compat/linuxkpi/common/src/linux_work.c ============================================================================== --- head/sys/compat/linuxkpi/common/src/linux_work.c Tue Aug 8 18:31:40 2017 (r322271) +++ head/sys/compat/linuxkpi/common/src/linux_work.c Tue Aug 8 19:36:34 2017 (r322272) @@ -214,7 +214,7 @@ linux_work_fn(void *context, int pending) [WORK_ST_TIMER] = WORK_ST_EXEC, /* delayed work w/o timeout */ [WORK_ST_TASK] = WORK_ST_EXEC, /* call callback */ [WORK_ST_EXEC] = WORK_ST_IDLE, /* complete callback */ - [WORK_ST_CANCEL] = WORK_ST_IDLE, /* complete cancel */ + [WORK_ST_CANCEL] = WORK_ST_EXEC, /* failed to cancel */ }; struct work_struct *work; struct workqueue_struct *wq; @@ -236,6 +236,7 @@ linux_work_fn(void *context, int pending) switch (linux_update_state(&work->state, states)) { case WORK_ST_TIMER: case WORK_ST_TASK: + case WORK_ST_CANCEL: WQ_EXEC_UNLOCK(wq); /* call work function */ @@ -266,13 +267,14 @@ linux_delayed_work_timer_fn(void *arg) [WORK_ST_IDLE] = WORK_ST_IDLE, /* NOP */ [WORK_ST_TIMER] = WORK_ST_TASK, /* start queueing task */ [WORK_ST_TASK] = WORK_ST_TASK, /* NOP */ - [WORK_ST_EXEC] = WORK_ST_TASK, /* queue task another time */ - [WORK_ST_CANCEL] = WORK_ST_IDLE, /* complete cancel */ + [WORK_ST_EXEC] = WORK_ST_EXEC, /* NOP */ + [WORK_ST_CANCEL] = WORK_ST_TASK, /* failed to cancel */ }; struct delayed_work *dwork = arg; switch (linux_update_state(&dwork->work.state, states)) { case WORK_ST_TIMER: + case WORK_ST_CANCEL: linux_delayed_work_enqueue(dwork); break; default: @@ -290,10 +292,10 @@ linux_cancel_work_sync(struct work_struct *work) { static const uint8_t states[WORK_ST_MAX] __aligned(8) = { [WORK_ST_IDLE] = WORK_ST_IDLE, /* NOP */ - [WORK_ST_TIMER] = WORK_ST_IDLE, /* idle */ - [WORK_ST_TASK] = WORK_ST_IDLE, /* idle */ - [WORK_ST_EXEC] = WORK_ST_IDLE, /* idle */ - [WORK_ST_CANCEL] = WORK_ST_IDLE, /* idle */ + [WORK_ST_TIMER] = WORK_ST_TIMER, /* can't happen */ + [WORK_ST_TASK] = WORK_ST_IDLE, /* cancel and drain */ + [WORK_ST_EXEC] = WORK_ST_IDLE, /* too late, drain */ + [WORK_ST_CANCEL] = WORK_ST_IDLE, /* cancel and drain */ }; struct taskqueue *tq; @@ -302,7 +304,13 @@ linux_cancel_work_sync(struct work_struct *work) switch (linux_update_state(&work->state, states)) { case WORK_ST_IDLE: + case WORK_ST_TIMER: return (0); + case WORK_ST_EXEC: + tq = work->work_queue->taskqueue; + if (taskqueue_cancel(tq, &work->work_task, NULL) != 0) + taskqueue_drain(tq, &work->work_task); + return (0); default: tq = work->work_queue->taskqueue; if (taskqueue_cancel(tq, &work->work_task, NULL) != 0) @@ -343,23 +351,29 @@ linux_cancel_delayed_work(struct delayed_work *dwork) { static const uint8_t states[WORK_ST_MAX] __aligned(8) = { [WORK_ST_IDLE] = WORK_ST_IDLE, /* NOP */ - [WORK_ST_TIMER] = WORK_ST_CANCEL, /* cancel */ - [WORK_ST_TASK] = WORK_ST_CANCEL, /* cancel */ - [WORK_ST_EXEC] = WORK_ST_CANCEL, /* cancel */ - [WORK_ST_CANCEL] = WORK_ST_CANCEL, /* cancel */ + [WORK_ST_TIMER] = WORK_ST_CANCEL, /* try to cancel */ + [WORK_ST_TASK] = WORK_ST_CANCEL, /* try to cancel */ + [WORK_ST_EXEC] = WORK_ST_EXEC, /* NOP */ + [WORK_ST_CANCEL] = WORK_ST_CANCEL, /* NOP */ }; struct taskqueue *tq; switch (linux_update_state(&dwork->work.state, states)) { case WORK_ST_TIMER: - if (linux_cancel_timer(dwork, 0)) + case WORK_ST_CANCEL: + if (linux_cancel_timer(dwork, 0)) { + atomic_cmpxchg(&dwork->work.state, + WORK_ST_CANCEL, WORK_ST_IDLE); return (1); + } /* FALLTHROUGH */ case WORK_ST_TASK: - case WORK_ST_EXEC: tq = dwork->work.work_queue->taskqueue; - if (taskqueue_cancel(tq, &dwork->work.work_task, NULL) == 0) + if (taskqueue_cancel(tq, &dwork->work.work_task, NULL) == 0) { + atomic_cmpxchg(&dwork->work.state, + WORK_ST_CANCEL, WORK_ST_IDLE); return (1); + } /* FALLTHROUGH */ default: return (0); @@ -376,10 +390,10 @@ linux_cancel_delayed_work_sync(struct delayed_work *dw { static const uint8_t states[WORK_ST_MAX] __aligned(8) = { [WORK_ST_IDLE] = WORK_ST_IDLE, /* NOP */ - [WORK_ST_TIMER] = WORK_ST_IDLE, /* idle */ - [WORK_ST_TASK] = WORK_ST_IDLE, /* idle */ - [WORK_ST_EXEC] = WORK_ST_IDLE, /* idle */ - [WORK_ST_CANCEL] = WORK_ST_IDLE, /* idle */ + [WORK_ST_TIMER] = WORK_ST_IDLE, /* cancel and drain */ + [WORK_ST_TASK] = WORK_ST_IDLE, /* cancel and drain */ + [WORK_ST_EXEC] = WORK_ST_IDLE, /* too late, drain */ + [WORK_ST_CANCEL] = WORK_ST_IDLE, /* cancel and drain */ }; struct taskqueue *tq; @@ -389,7 +403,13 @@ linux_cancel_delayed_work_sync(struct delayed_work *dw switch (linux_update_state(&dwork->work.state, states)) { case WORK_ST_IDLE: return (0); + case WORK_ST_EXEC: + tq = dwork->work.work_queue->taskqueue; + if (taskqueue_cancel(tq, &dwork->work.work_task, NULL) != 0) + taskqueue_drain(tq, &dwork->work.work_task); + return (0); case WORK_ST_TIMER: + case WORK_ST_CANCEL: if (linux_cancel_timer(dwork, 1)) { /* * Make sure taskqueue is also drained before @@ -468,6 +488,7 @@ linux_work_pending(struct work_struct *work) switch (atomic_read(&work->state)) { case WORK_ST_TIMER: case WORK_ST_TASK: + case WORK_ST_CANCEL: return (1); default: return (0); @@ -486,7 +507,6 @@ linux_work_busy(struct work_struct *work) case WORK_ST_IDLE: return (0); case WORK_ST_EXEC: - case WORK_ST_CANCEL: tq = work->work_queue->taskqueue; return (taskqueue_poll_is_busy(tq, &work->work_task)); default: