Re: Litmus test for question from Al Viro

From: joel
Date: Sat Oct 03 2020 - 12:09:00 EST


On Thu, Oct 01, 2020 at 02:30:48PM -0700, Paul E. McKenney wrote:
> On Thu, Oct 01, 2020 at 12:15:29PM -0400, Alan Stern wrote:
> > On Wed, Sep 30, 2020 at 09:51:16PM -0700, Paul E. McKenney wrote:
> > > Hello!
> > >
> > > Al Viro posted the following query:
> > >
> > > ------------------------------------------------------------------------
> > >
> > > <viro> fun question regarding barriers, if you have time for that
> > > <viro> V->A = V->B = 1;
> > > <viro>
> > > <viro> CPU1:
> > > <viro> to_free = NULL
> > > <viro> spin_lock(&LOCK)
> > > <viro> if (!smp_load_acquire(&V->B))
> > > <viro> to_free = V
> > > <viro> V->A = 0
> > > <viro> spin_unlock(&LOCK)
> > > <viro> kfree(to_free)
> > > <viro>
> > > <viro> CPU2:
> > > <viro> to_free = V;
> > > <viro> if (READ_ONCE(V->A)) {
> > > <viro> spin_lock(&LOCK)
> > > <viro> if (V->A)
> > > <viro> to_free = NULL
> > > <viro> smp_store_release(&V->B, 0);
> > > <viro> spin_unlock(&LOCK)
> > > <viro> }
> > > <viro> kfree(to_free);
> > > <viro> 1) is it guaranteed that V will be freed exactly once and that
> > > no accesses to *V will happen after freeing it?
> > > <viro> 2) do we need smp_store_release() there? I.e. will anything
> > > break if it's replaced with plain V->B = 0?
> >
> > Here are my answers to Al's questions:
> >
> > 1) It is guaranteed that V will be freed exactly once. It is not
> > guaranteed that no accesses to *V will occur after it is freed, because
> > the test contains a data race. CPU1's plain "V->A = 0" write races with
> > CPU2's READ_ONCE; if the plain write were replaced with
> > "WRITE_ONCE(V->A, 0)" then the guarantee would hold. Equally well,
> > CPU1's smp_load_acquire could be replaced with a plain read while the
> > plain write is replaced with smp_store_release.
> >
> > 2) The smp_store_release in CPU2 is not needed. Replacing it with a
> > plain V->B = 0 will not break anything.
> >
> > Analysis: Apart from the kfree calls themselves, the only access to a
> > shared variable outside of a critical section is CPU2's READ_ONCE of
> > V->A. So let's consider two possibilities:
> >
> > 1: The READ_ONCE returns 0. Then CPU2 doesn't execute its critical
> > section and does kfree(V). However, the fact that the READ_ONCE got 0
> > means that CPU1 has already entered its critical section, has already
> > written to V->A (but with a plain write!) and therefore has already seen
> > V->B = 1 (because of the smp_load_acquire), and therefore will not free
> > V. This case shows that the ordering we require is for CPU1 to read
> > V->B before it writes V->A. The ordering can be enforced by using
> > either a load-acquire (as in the litmus test) or a store-release.
> >
> > 2: The READ_ONCE returns 1. Then CPU2 does execute its critical
> > section, and we can simply treat this case the same as if the critical
> > section was executed unconditionally. Whichever CPU runs its critical
> > section second will free V, and the other CPU won't try to access V
> > after leaving its own critical section (and thus won't access V after it
> > has been freed).
> >
> > > ------------------------------------------------------------------------
> > >
> > > Of course herd7 supports neither structures nor arrays

FWIW, I turned Al's example into a klitmus test which gave me the freedom to
modify it offline and use structures.

The code of the 2 threads look like this (full module enclosed later in the
email). I don't see any issues with the example above on either x86 or arm64
which I luckily have these days. Al or others could also modify the module
with any variations and test as well:

static void code0(struct v_struct* v,spinlock_t* l,int* out_0_r1) {

struct v_struct *r1; /* to_free */

r1 = NULL;
spin_lock(l);
if (!smp_load_acquire(&v->b))
r1 = v;
v->a = 0;
spin_unlock(l);

*out_0_r1 = !!r1;
}

static void code1(struct v_struct* v,spinlock_t* l,int* out_1_r1) {

struct v_struct *r1; /* to_free */

r1 = v;
if (READ_ONCE(v->a)) {
spin_lock(l);
if (v->a)
r1 = NULL;
smp_store_release(&v->b, 0);
spin_unlock(l);
}

*out_1_r1 = !!r1;
}

Results on both arm64 and x86:

Histogram (2 states)
19080852:>0:r1=1; 1:r1=0;
20919148:>0:r1=0; 1:r1=1;
No

Witnesses
Positive: 0, Negative: 40000000
Condition exists (0:r1=1 /\ 1:r1=1) is NOT validated
Hash=4a8c15603ffb5ab464195ea39ccd6382
Observation AL+test Never 0 40000000
Time AL+test 6.24

I guess I could do an alloc and free of v_struct. However, I just checked for
whether the to_free in Al's example could ever be NULL for both threads.

Here is the full test. To run it:
modprobe litmus nruns=200
cat /proc/litmus
rmmod

(The file_operations may need to be replaced trivially with proc_operations
for >5.4 kernels)

---8<-----------------------

diff --git a/Makefile b/Makefile
index 57ef8d1d5c24..493b7583f111 100644
--- a/Makefile
+++ b/Makefile
@@ -2,7 +2,7 @@
VERSION = 5
PATCHLEVEL = 4
SUBLEVEL = 68
-EXTRAVERSION =
+EXTRAVERSION = litmus
NAME = Kleptomaniac Octopus

# *DOCUMENTATION*
diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
index 9a3dda3a9d06..5af1bc2ab9a1 100644
--- a/drivers/misc/Makefile
+++ b/drivers/misc/Makefile
@@ -3,6 +3,7 @@
# Makefile for misc devices that really don't fit anywhere else.
#

+obj-m += mymodules/
obj-$(CONFIG_IBM_ASM) += ibmasm/
obj-$(CONFIG_IBMVMC) += ibmvmc.o
obj-$(CONFIG_AD525X_DPOT) += ad525x_dpot.o
diff --git a/drivers/misc/mymodules/Makefile b/drivers/misc/mymodules/Makefile
new file mode 100644
index 000000000000..1ebb67fbc9ba
--- /dev/null
+++ b/drivers/misc/mymodules/Makefile
@@ -0,0 +1,8 @@
+ccflags-y += -std=gnu99 -Wno-declaration-after-statement
+obj-m += litmus000.o
+
+all:
+ make -C /lib/modules/$(shell uname -r)/build/ M=$(PWD) modules
+
+clean:
+ make -C /lib/modules/$(shell uname -r)/build/ M=$(PWD) clean
diff --git a/drivers/misc/mymodules/README.txt b/drivers/misc/mymodules/README.txt
new file mode 100644
index 000000000000..2af0ca89349f
--- /dev/null
+++ b/drivers/misc/mymodules/README.txt
@@ -0,0 +1,25 @@
+Kernel modules produced by klitmus7
+
+REQUIREMEMTS
+ - kernel headers for compiling modules.
+ - commands insmod and rmmod for installing and removing kernel modules.
+
+COMPILING
+ Once kernel headers are installed, just type 'make'
+
+RUNNING
+ Run script 'run.sh' as root, e.g. as 'sudo sh run.sh'
+ Some parameters can be passed to the script by adding
+ key=value command line arguments.
+ Main arguments are as follows:
+ * size=<n> Tests operate on arrays of size <n>.
+ * nruns=<n> And are repeated <n> times.
+ * stride=<n> Arrays are scanned with stride <n>.
+ * avail=<n> Number of cores are devoted to tests.
+
+ If <avail> is the special value zero or exceeds <a>, the number of actually online cores,
+ then tests will occupy <a> cores.
+
+ By default the script runs as if called as:
+ sudo sh run.sh size=100000 nruns=10 stride=1 avail=0
+
diff --git a/drivers/misc/mymodules/litmus000.c b/drivers/misc/mymodules/litmus000.c
new file mode 100644
index 000000000000..7702e22c7126
--- /dev/null
+++ b/drivers/misc/mymodules/litmus000.c
@@ -0,0 +1,576 @@
+/****************************************************************************/
+/* the diy toolsuite */
+/* */
+/* Jade Alglave, University College London, UK. */
+/* Luc Maranget, INRIA Paris-Rocquencourt, France. */
+/* */
+/* This C source is a product of litmus7 and includes source that is */
+/* governed by the CeCILL-B license. */
+/****************************************************************************/
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/fs.h>
+#include <linux/proc_fs.h>
+#include <linux/seq_file.h>
+#include <linux/kthread.h>
+#include <linux/ktime.h>
+#include <linux/atomic.h>
+#include <linux/sysfs.h>
+#include <linux/sched.h>
+#include <linux/wait.h>
+#include <linux/slab.h>
+#include <linux/random.h>
+
+typedef u64 count_t;
+#define PCTR "llu"
+
+#ifndef WRITE_ONCE
+#define WRITE_ONCE(x,v) ({ ACCESS_ONCE((x)) = (v); })
+#endif
+#ifndef READ_ONCE
+#define READ_ONCE(x) ACCESS_ONCE((x))
+#endif
+
+#ifndef smp_store_release
+#define smp_store_release(p, v) \
+do { \
+ smp_mb(); \
+ WRITE_ONCE(*p, v); \
+} while (0)
+#endif
+
+#ifndef smp_load_acquire
+#define smp_load_acquire(p) \
+({ \
+ typeof(*p) ___p1 = READ_ONCE(*p); \
+ smp_mb(); \
+ ___p1; \
+})
+#endif
+
+#ifndef xchg_acquire
+#define xchg_acquire(x,v) xchg(x,v)
+#endif
+
+#ifndef xchg_release
+#define xchg_release(x,v) xchg(x,v)
+#endif
+
+#ifndef lockless_dereference
+#define lockless_dereference(p) \
+({ \
+ typeof(p) ____p1 = READ_ONCE(p); \
+ smp_read_barrier_depends(); \
+ ____p1; \
+})
+#endif
+
+#ifndef cond_resched_rcu_qs
+#define cond_resched_rcu_qs cpu_relax
+#endif
+
+/* Some constant divide (not available on ARMv7) */
+
+inline static u64 divBy10(u64 n) {
+ u64 q, r;
+ q = (n >> 1) + (n >> 2);
+ q = q + (q >> 4);
+ q = q + (q >> 8);
+ q = q + (q >> 16);
+ q = q >> 3;
+ r = n - q*10;
+ return q + ((r + 6) >> 4);
+}
+
+inline static u64 divBy1000(u64 n) {
+ u64 q, r, t;
+ t = (n >> 7) + (n >> 8) + (n >> 12);
+ q = (n >> 1) + t + (n >> 15) + (t >> 11) + (t >> 14);
+ q = q >> 9;
+ r = n - q*1000;
+ return q + ((r + 24) >> 10);
+}
+
+static int randmod(unsigned int m) {
+ unsigned int x ;
+ get_random_bytes(&x,sizeof(x));
+ return x % m ;
+}
+
+static void shuffle_array(int *t,int sz) {
+ for (int k = 0 ; k < sz-1; k++) {
+ int j = k + randmod(sz-k);
+ int tmp = t[k] ;
+ t[k] = t[j];
+ t[j] = tmp;
+ }
+}
+/**************/
+/* Parameters */
+/**************/
+
+static const int nthreads = 2;
+static unsigned int nruns = 10;
+static unsigned int size = 100000;
+static unsigned int stride = 1;
+static unsigned int avail = 0;
+static unsigned int ninst = 0;
+static int affincr = 0;
+
+module_param(nruns,uint,0644);
+module_param(size,uint,0644);
+module_param(stride,uint,0644);
+module_param(avail,uint,0644);
+module_param(ninst,uint,0644);
+module_param(affincr,int,0644);
+
+static char *name = "AL+test";
+module_param(name,charp,0444);
+
+static wait_queue_head_t *wq;
+static atomic_t done = ATOMIC_INIT(0);
+
+/************/
+/* Outcomes */
+/************/
+
+#define NOUTS 2
+typedef u64 outcome_t[NOUTS];
+
+static const int out_0_r1_f = 0 ;
+static const int out_1_r1_f = 1 ;
+
+typedef struct outs_t {
+ struct outs_t *next,*down ;
+ count_t c ;
+ u64 k ;
+ int show ;
+} outs_t ;
+
+
+static outs_t *alloc_outs(u64 k) {
+ outs_t *r = kmalloc(sizeof(*r),GFP_KERNEL) ;
+ if (r == NULL) return NULL ;
+ r->k = k ;
+ r->c = 0 ;
+ r->show = 0 ;
+ r->next = r->down = NULL ;
+ return r ;
+}
+
+static void free_outs(outs_t *p) {
+ if (p == NULL) return ;
+ free_outs(p->next) ;
+ free_outs(p->down) ;
+ kfree(p) ;
+}
+
+static outs_t *
+loop_add_outcome_outs(outs_t *p, u64 *k, int i, count_t c, int show) {
+ outs_t *r = p ;
+ if (p == NULL || k[i] < p->k) {
+ r = alloc_outs(k[i]) ;
+ if (r == NULL) return p ; /* simply ignore insert */
+ r->next = p ;
+ p = r ;
+ }
+ for ( ; ; ) {
+ outs_t **q ;
+ if (k[i] > p->k) {
+ q = &(p->next) ;
+ p = p->next ;
+ } else if (i <= 0) {
+ p->c += c ;
+ p->show = show || p->show ;
+ return r ;
+ } else {
+ i-- ;
+ q = &(p->down) ;
+ p = p->down ;
+ }
+ if (p == NULL || k[i] < p->k) {
+ outs_t *a = alloc_outs(k[i]) ;
+ if (a == NULL) return r ;
+ a->next = p ;
+ p = a ;
+ *q = a ;
+ }
+ }
+}
+
+typedef count_t cfun(outs_t *) ;
+
+static count_t count_scan(cfun *f,outs_t *p) {
+ count_t r = 0 ;
+ for ( ; p ; p = p->next) {
+ r += f(p) ;
+ if (p->down) {
+ r += count_scan(f,p->down) ;
+ }
+ }
+ return r ;
+}
+
+static count_t cshow(outs_t *p) {
+ if (p->show) return p->c ;
+ return 0 ;
+}
+
+static count_t count_show(outs_t *p) { return count_scan(cshow,p) ; }
+
+static count_t cnoshow(outs_t *p) {
+ if (!p->show) return p->c ;
+ return 0 ;
+}
+
+static count_t count_noshow(outs_t *p) { return count_scan(cnoshow,p); }
+
+static count_t cnstates(outs_t *p) {
+ if (p->c > 0) return 1 ;
+ return 0 ;
+}
+
+static count_t count_nstates(outs_t *p) { return count_scan(cnstates,p); }
+
+
+static outs_t *add_outcome_outs(outs_t *p,u64 *k,int show) {
+ return loop_add_outcome_outs(p,k,NOUTS-1,1,show);
+}
+
+static void do_dump_outs (struct seq_file *m,outs_t *p,u64 *o,int sz) {
+ for ( ; p ; p = p->next) {
+ o[sz-1] = p->k;
+ if (p->c > 0) {
+ seq_printf(m,"%-8"PCTR"%c>0:r1=%i; 1:r1=%i;\n",p->c,p->show ? '*' : ':',(int)o[out_0_r1_f],(int)o[out_1_r1_f]);
+ } else {
+ do_dump_outs(m,p->down,o,sz-1);
+ }
+ }
+}
+
+static void dump_outs(struct seq_file *m,outs_t *p) {
+ outcome_t buff;
+ do_dump_outs(m,p,buff,NOUTS);
+}
+
+static inline void barrier_wait(int id,int i,int *b) {
+ if ((i % nthreads) == id) {
+ WRITE_ONCE(*b,1);
+ smp_mb();
+ } else {
+ int _spin = 256;
+ for ( ; ; ) {
+ if (READ_ONCE(*b) != 0) return;
+ if (--_spin <= 0) return;
+ cpu_relax();
+ }
+ }
+}
+
+
+/****************/
+/* Affinity */
+/****************/
+
+static int *online;
+static int nonline;
+
+/****************/
+/* Test Context */
+/****************/
+
+struct v_struct {
+ int a;
+ int b;
+};
+
+typedef struct {
+/* Shared locations */
+ struct v_struct *v;
+ spinlock_t *l;
+/* Final contents of observed registers */
+ int *out_0_r1;
+ int *out_1_r1;
+/* For synchronisation */
+ int *barrier;
+} ctx_t ;
+
+static ctx_t **ctx;
+
+static void free_ctx(ctx_t *p) {
+ if (p == NULL) return;
+ if (p->v) kfree(p->v);
+ if (p->l) kfree(p->l);
+ if (p->out_0_r1) kfree(p->out_0_r1);
+ if (p->out_1_r1) kfree(p->out_1_r1);
+ if (p->barrier) kfree(p->barrier);
+ kfree(p);
+}
+
+static ctx_t *alloc_ctx(size_t sz) {
+ ctx_t *r = kzalloc(sizeof(*r),GFP_KERNEL);
+ if (!r) { return NULL; }
+ r->v = kmalloc(sizeof(r->v[0])*sz,GFP_KERNEL);
+ if (!r->v) { return NULL; }
+ r->l = kmalloc(sizeof(r->l[0])*sz,GFP_KERNEL);
+ if (!r->l) { return NULL; }
+ for (int _i=0 ; _i < sz ; _i++) spin_lock_init(&r->l[_i]);
+ r->out_0_r1 = kmalloc(sizeof(r->out_0_r1[0])*sz,GFP_KERNEL);
+ if (!r->out_0_r1) { return NULL; }
+ r->out_1_r1 = kmalloc(sizeof(r->out_1_r1[0])*sz,GFP_KERNEL);
+ if (!r->out_1_r1) { return NULL; }
+ r->barrier = kmalloc(sizeof(r->barrier[0])*sz,GFP_KERNEL);
+ if (!r->barrier) { return NULL; }
+ return r;
+}
+
+static void init_ctx(ctx_t *_a,size_t sz) {
+ for (int _i = 0 ; _i < sz ; _i++) {
+ _a->v[_i].a = 1;
+ _a->v[_i].b = 1;
+ _a->out_0_r1[_i] = -239487;
+ _a->out_1_r1[_i] = -239487;
+ _a->barrier[_i] = 0;
+ }
+}
+
+/***************/
+/* Litmus code */
+/***************/
+
+static void code0(struct v_struct* v,spinlock_t* l,int* out_0_r1) {
+
+ struct v_struct *r1; /* to_free */
+
+ r1 = NULL;
+ spin_lock(l);
+ if (!smp_load_acquire(&v->b))
+ r1 = v;
+ v->a = 0;
+ spin_unlock(l);
+
+ *out_0_r1 = !!r1;
+}
+
+static int thread0(void *_p) {
+ ctx_t *_a = (ctx_t *)_p;
+
+ smp_mb();
+ for (int _j = 0 ; _j < stride ; _j++) {
+ for (int _i = _j ; _i < size ; _i += stride) {
+ barrier_wait(0,_i,&_a->barrier[_i]);
+ code0(&_a->v[_i],&_a->l[_i],&_a->out_0_r1[_i]);
+ }
+ }
+ atomic_inc(&done);
+ smp_mb();
+ wake_up(wq);
+ smp_mb();
+ do_exit(0);
+}
+
+static void code1(struct v_struct* v,spinlock_t* l,int* out_1_r1) {
+
+ struct v_struct *r1; /* to_free */
+
+ r1 = v;
+ if (READ_ONCE(v->a)) {
+ spin_lock(l);
+ if (v->a)
+ r1 = NULL;
+ smp_store_release(&v->b, 0);
+ spin_unlock(l);
+ }
+
+ *out_1_r1 = !!r1;
+}
+
+static int thread1(void *_p) {
+ ctx_t *_a = (ctx_t *)_p;
+
+ smp_mb();
+ for (int _j = 0 ; _j < stride ; _j++) {
+ for (int _i = _j ; _i < size ; _i += stride) {
+ barrier_wait(1,_i,&_a->barrier[_i]);
+ code1(&_a->v[_i],&_a->l[_i],&_a->out_1_r1[_i]);
+ }
+ }
+ atomic_inc(&done);
+ smp_mb();
+ wake_up(wq);
+ smp_mb();
+ do_exit(0);
+}
+
+inline static int final_cond(int _out_0_r1,int _out_1_r1) {
+ switch (_out_0_r1) {
+ case 1:
+ switch (_out_1_r1) {
+ case 1:
+ return 1;
+ default:
+ return 0;
+ }
+ default:
+ return 0;
+ }
+}
+
+/********/
+/* Zyva */
+/********/
+
+static outs_t *zyva(void) {
+ ctx_t **c = ctx;
+ outs_t *outs = NULL;
+ const int nth = ninst * nthreads;
+ struct task_struct **th;
+
+ th = kzalloc(sizeof(struct task_struct *) * nth, GFP_KERNEL);
+ if (!th) return NULL;
+ for (int _k = 0 ; _k < nruns ; _k++) {
+ int _nth = 0;
+
+ for (int _ni = 0 ; _ni < ninst ; _ni++) init_ctx(c[_ni],size);
+ atomic_set(&done,0);
+ smp_mb();
+ for (int _ni = 0 ; _ni < ninst ; _ni++) {
+ th[_nth] = kthread_create(thread0,c[_ni],"thread0");
+ if (IS_ERR(th[_nth])) {kfree(th); return outs;}
+ _nth++;
+ th[_nth] = kthread_create(thread1,c[_ni],"thread1");
+ if (IS_ERR(th[_nth])) {kfree(th); return outs;}
+ _nth++;
+ }
+ if (affincr != 0) {
+ int _idx=0, _idx0=0, _incr=affincr > 0 ? affincr : 1;
+ if (affincr < 0) shuffle_array(online,nonline);
+ for (int _t = 0 ; _t < nth ; _t++) {
+ kthread_bind(th[_t],online[_idx]);
+ _idx += _incr;
+ if (_idx >= nonline) _idx = ++_idx0;
+ if (_idx >= nonline) _idx = _idx0 = 0;
+ }
+ }
+ for (int _t = 0 ; _t < nth ; _t++) wake_up_process(th[_t]);
+ wait_event_interruptible(*wq, atomic_read(&done) == nth);
+ smp_mb();
+ for (int _ni = 0 ; _ni < ninst ; _ni++) {
+ ctx_t *_a = c[_ni];
+ for (int _i = 0 ; _i < size ; _i++) {
+ outcome_t _o;
+ int _cond;
+ _cond = final_cond(_a->out_0_r1[_i],_a->out_1_r1[_i]);
+ _o[out_0_r1_f] = _a->out_0_r1[_i];
+ _o[out_1_r1_f] = _a->out_1_r1[_i];
+ outs = add_outcome_outs(outs,_o,_cond);
+ }
+ }
+ cond_resched();
+ }
+ kfree(th);
+ return outs;
+}
+
+static int do_it(struct seq_file *m) {
+ ktime_t time_start = ktime_get();
+ outs_t *outs = zyva();
+ ktime_t time_end = ktime_get();
+ seq_printf(m,"Test AL+test Allowed\n");
+ seq_printf(m,"Histogram (%"PCTR" states)\n",count_nstates(outs));
+ dump_outs(m,outs);
+ {
+ count_t pos=count_show(outs),neg=count_noshow(outs);
+ char *msg = "Sometimes";
+ u64 delta = ktime_to_ms(ktime_sub(time_end, time_start));
+ u64 sec = divBy1000(delta);
+ u64 cent = divBy10(delta-1000*sec + 5);
+ seq_printf(m,"%s\n\n",pos > 0 ? "Ok" : "No");
+ seq_printf(m,"Witnesses\nPositive: %"PCTR", Negative: %"PCTR"\n",pos,neg);
+ seq_printf(m,"Condition exists (0:r1=1 /\\ 1:r1=1) is %svalidated\n",pos > 0?"":"NOT ");
+ seq_printf(m,"%s\n","Hash=4a8c15603ffb5ab464195ea39ccd6382");
+ if (pos == 0) msg = "Never";
+ else if (neg == 0) msg = "Always";
+ seq_printf(m,"Observation AL+test %s %"PCTR" %"PCTR"\n",msg,pos,neg);
+ seq_printf(m,"Time AL+test %llu.%02llu\n\n",sec,cent);
+ }
+ free_outs(outs);
+ return 0;
+}
+
+static int
+litmus_proc_show(struct seq_file *m,void *v) {
+ if (ninst == 0 || ninst * nthreads > nonline) {
+ seq_printf(m,"%s: skipped\n","AL+test");
+ return 0;
+ } else {
+ return do_it(m);
+ }
+}
+
+static int
+litmus_proc_open(struct inode *inode,struct file *fp) {
+ return single_open(fp,litmus_proc_show,NULL);
+}
+
+static const struct file_operations litmus_proc_fops = {
+ .owner = THIS_MODULE,
+ .open = litmus_proc_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static int __init
+litmus_init(void) {
+ int err=0;
+ struct proc_dir_entry *litmus_pde = proc_create("litmus",0,NULL,&litmus_proc_fops);
+ if (litmus_pde == NULL) { return -ENOMEM; }
+ stride = stride == 0 ? 1 : stride;
+ nonline = num_online_cpus ();
+ online = kzalloc(sizeof(*online)*nonline,GFP_KERNEL);
+ if (online == NULL) goto clean_pde;
+ {
+ int cpu,_k;
+ _k=0; for_each_cpu(cpu,cpu_online_mask) online[_k++] = cpu;
+ }
+ if (avail == 0 || avail > nonline) avail = nonline;
+ if (ninst == 0) ninst = avail / nthreads ;
+
+ ctx = kzalloc(sizeof(ctx[0])*ninst,GFP_KERNEL);
+ if (ctx == NULL) { err = -ENOMEM ; goto clean_online; }
+ for (int _k=0 ; _k < ninst ; _k++) {
+ ctx[_k] = alloc_ctx(size);
+ if (ctx[_k] == NULL) { err = -ENOMEM; goto clean_ctx; }
+ }
+
+ wq = kzalloc(sizeof(*wq), GFP_KERNEL);
+ if (wq == NULL) { err = -ENOMEM; goto clean_ctx; }
+ init_waitqueue_head(wq);
+ return 0;
+clean_ctx:
+ for (int k=0 ; k < ninst ; k++) free_ctx(ctx[k]);
+ kfree(ctx);
+clean_online:
+ kfree(online);
+clean_pde:
+ remove_proc_entry("litmus",NULL);
+ return err;
+}
+
+static void __exit
+litmus_exit(void) {
+ for (int k=0 ; k < ninst ; k++) free_ctx(ctx[k]);
+ kfree(ctx);
+ kfree(online);
+ remove_proc_entry("litmus",NULL);
+}
+
+module_init(litmus_init);
+module_exit(litmus_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Luc");
+MODULE_DESCRIPTION("Litmus module");
diff --git a/drivers/misc/mymodules/run.sh b/drivers/misc/mymodules/run.sh
new file mode 100644
index 000000000000..c373bfc285db
--- /dev/null
+++ b/drivers/misc/mymodules/run.sh
@@ -0,0 +1,20 @@
+OPT="$*"
+date
+echo Compilation command: "/usr/local/google/home/joelaf/.local/bin/klitmus7 -o mymodules/ viro.litmus"
+echo "OPT=$OPT"
+echo "uname -r=$(uname -r)"
+echo
+
+zyva () {
+ name=$1
+ ko=$2
+ if test -f $ko
+ then
+ insmod $ko $OPT
+ cat /proc/litmus
+ rmmod $ko
+ fi
+}
+
+zyva "AL+test" litmus000.ko
+date
--
2.28.0.806.g8561365e88-goog