Skip site navigation (1)Skip section navigation (2)
Date:      Thu, 21 May 2020 20:18:38 +0000 (UTC)
From:      Emmanuel Vadot <manu@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org
Subject:   svn commit: r361343 - in head/sys/compat/linuxkpi/common: include/linux src
Message-ID:  <202005212018.04LKIcJ7045750@repo.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: manu
Date: Thu May 21 20:18:38 2020
New Revision: 361343
URL: https://svnweb.freebsd.org/changeset/base/361343

Log:
  linuxkpi: Add rcu_work functions
  
  The rcu_work function helps to queue some work after waiting for a grace
  period.
  This is needed by DRM drivers.
  
  Sponsored-by: The FreeBSD Foundation
  Reviewed by:	hselasky
  Differential Revision:	https://reviews.freebsd.org/D24942

Modified:
  head/sys/compat/linuxkpi/common/include/linux/workqueue.h
  head/sys/compat/linuxkpi/common/src/linux_work.c

Modified: head/sys/compat/linuxkpi/common/include/linux/workqueue.h
==============================================================================
--- head/sys/compat/linuxkpi/common/include/linux/workqueue.h	Thu May 21 19:46:11 2020	(r361342)
+++ head/sys/compat/linuxkpi/common/include/linux/workqueue.h	Thu May 21 20:18:38 2020	(r361343)
@@ -72,6 +72,13 @@ struct work_struct {
 	atomic_t state;
 };
 
+struct rcu_work {
+	struct work_struct work;
+	struct rcu_head rcu;
+
+	struct workqueue_struct *wq;
+};
+
 #define	DECLARE_WORK(name, fn)						\
 	struct work_struct name;					\
 	static void name##_init(void *arg)				\
@@ -112,6 +119,9 @@ do {									\
 	TASK_INIT(&(work)->work_task, 0, linux_work_fn, (work));	\
 } while (0)
 
+#define INIT_RCU_WORK(_work, _fn) \
+	INIT_WORK(&(_work)->work, (_fn))
+
 #define	INIT_WORK_ONSTACK(work, fn) \
 	INIT_WORK(work, fn)
 
@@ -192,6 +202,12 @@ do {									\
 #define	flush_work(work) \
 	linux_flush_work(work)
 
+#define	queue_rcu_work(wq, rwork) \
+	linux_queue_rcu_work(wq, rwork)
+
+#define	flush_rcu_work(rwork) \
+	linux_flush_rcu_work(rwork)
+
 #define	flush_delayed_work(dwork) \
 	linux_flush_delayed_work(dwork)
 
@@ -237,5 +253,7 @@ extern bool linux_flush_delayed_work(struct delayed_wo
 extern bool linux_work_pending(struct work_struct *);
 extern bool linux_work_busy(struct work_struct *);
 extern struct work_struct *linux_current_work(void);
+extern bool linux_queue_rcu_work(struct workqueue_struct *wq, struct rcu_work *rwork);
+extern bool linux_flush_rcu_work(struct rcu_work *rwork);
 
 #endif					/* _LINUX_WORKQUEUE_H_ */

Modified: head/sys/compat/linuxkpi/common/src/linux_work.c
==============================================================================
--- head/sys/compat/linuxkpi/common/src/linux_work.c	Thu May 21 19:46:11 2020	(r361342)
+++ head/sys/compat/linuxkpi/common/src/linux_work.c	Thu May 21 20:18:38 2020	(r361343)
@@ -31,6 +31,7 @@ __FBSDID("$FreeBSD$");
 #include <linux/wait.h>
 #include <linux/compat.h>
 #include <linux/spinlock.h>
+#include <linux/rcupdate.h>
 
 #include <sys/kernel.h>
 
@@ -153,6 +154,53 @@ linux_queue_work_on(int cpu __unused, struct workqueue
 	default:
 		return (false);		/* already on a queue */
 	}
+}
+
+/*
+ * Callback func for linux_queue_rcu_work
+ */
+static void
+rcu_work_func(struct rcu_head *rcu)
+{
+	struct rcu_work *rwork;
+
+	rwork = container_of(rcu, struct rcu_work, rcu);
+	linux_queue_work_on(WORK_CPU_UNBOUND, rwork->wq, &rwork->work);
+}
+
+/*
+ * This function queue a work after a grace period
+ * If the work was already pending it returns false,
+ * if not it calls call_rcu and returns true.
+ */
+bool
+linux_queue_rcu_work(struct workqueue_struct *wq, struct rcu_work *rwork)
+{
+
+	if (!linux_work_pending(&rwork->work)) {
+		rwork->wq = wq;
+		linux_call_rcu(RCU_TYPE_REGULAR, &rwork->rcu, rcu_work_func);
+		return (true);
+	}
+	return (false);
+}
+
+/*
+ * This function waits for the last execution of a work and then
+ * flush the work.
+ * It returns true if the work was pending and we waited, it returns
+ * false otherwise.
+ */
+bool
+linux_flush_rcu_work(struct rcu_work *rwork)
+{
+
+	if (linux_work_pending(&rwork->work)) {
+		linux_rcu_barrier(RCU_TYPE_REGULAR);
+		linux_flush_work(&rwork->work);
+		return (true);
+	}
+	return (linux_flush_work(&rwork->work));
 }
 
 /*



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?202005212018.04LKIcJ7045750>