Re: Litmus test for question from Al Viro

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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




[Index of Archives]     [Linux Kernel]     [Kernel Newbies]     [x86 Platform Driver]     [Netdev]     [Linux Wireless]     [Netfilter]     [Bugtraq]     [Linux Filesystems]     [Yosemite Discussion]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Samba]     [Device Mapper]

  Powered by Linux