lwref: insane idea on reference counting

36
An insane idea on reference counting Gleb Smirnoff [email protected] BSDCan 2014 Ottawa May 16, 2014 Gleb Smirnoff [email protected] An insane idea on reference counting May 16, 2014 1 / 28

Upload: gleb-smirnoff

Post on 15-Jul-2015

213 views

Category:

Science


1 download

TRANSCRIPT

Page 1: lwref: insane idea on reference counting

An insane idea on reference counting

Gleb [email protected]

BSDCan 2014Ottawa

May 16, 2014

Gleb Smirnoff [email protected] An insane idea on reference counting May 16, 2014 1 / 28

Page 2: lwref: insane idea on reference counting

Introduction

The configuration problem

Problem:A structure in memory describing a “configuration”Multiple readers at high rateSporadic writers

Examples:local IP addresses hashinterfaces list, particular ifnetfirewall rules

Gleb Smirnoff [email protected] An insane idea on reference counting May 16, 2014 2 / 28

Page 3: lwref: insane idea on reference counting

Introduction

The configuration problem

Problem:A structure in memory describing a “configuration”Multiple readers at high rateSporadic writers

Examples:local IP addresses hashinterfaces list, particular ifnetfirewall rules

Gleb Smirnoff [email protected] An insane idea on reference counting May 16, 2014 2 / 28

Page 4: lwref: insane idea on reference counting

Introduction

Ready to use solutions

What we use in FreeBSD:

rwlock(9)Acquiring thread == Releasing threadVery expensive: all readers do atomic(9) on the sameword

rmlock(9)Acquiring thread == Releasing threadDoes sched_pin(9) for the entire operation

refcount(9)Acquiring thread != Releasing threadExpensive: is atomic(9)

Gleb Smirnoff [email protected] An insane idea on reference counting May 16, 2014 3 / 28

Page 5: lwref: insane idea on reference counting

Introduction

Ready to use solutions

What we use in FreeBSD:rwlock(9)

Acquiring thread == Releasing threadVery expensive: all readers do atomic(9) on the sameword

rmlock(9)Acquiring thread == Releasing threadDoes sched_pin(9) for the entire operation

refcount(9)Acquiring thread != Releasing threadExpensive: is atomic(9)

Gleb Smirnoff [email protected] An insane idea on reference counting May 16, 2014 3 / 28

Page 6: lwref: insane idea on reference counting

Introduction

Ready to use solutions

What we use in FreeBSD:rwlock(9)

Acquiring thread == Releasing threadVery expensive: all readers do atomic(9) on the sameword

rmlock(9)Acquiring thread == Releasing threadDoes sched_pin(9) for the entire operation

refcount(9)Acquiring thread != Releasing threadExpensive: is atomic(9)

Gleb Smirnoff [email protected] An insane idea on reference counting May 16, 2014 3 / 28

Page 7: lwref: insane idea on reference counting

Introduction

Ready to use solutions

What we use in FreeBSD:rwlock(9)

Acquiring thread == Releasing threadVery expensive: all readers do atomic(9) on the sameword

rmlock(9)Acquiring thread == Releasing threadDoes sched_pin(9) for the entire operation

refcount(9)Acquiring thread != Releasing threadExpensive: is atomic(9)

Gleb Smirnoff [email protected] An insane idea on reference counting May 16, 2014 3 / 28

Page 8: lwref: insane idea on reference counting

Introduction

Patented solution

RCUAcquiring thread == Releasing threadPatented :(

Gleb Smirnoff [email protected] An insane idea on reference counting May 16, 2014 4 / 28

Page 9: lwref: insane idea on reference counting

Introduction

Goals

Ultra lightweight for a readerAcquiring thread != Releasing thread

Sounds like refcount(9) w/o atomics.

Gleb Smirnoff [email protected] An insane idea on reference counting May 16, 2014 5 / 28

Page 10: lwref: insane idea on reference counting

Introduction

Goals

Ultra lightweight for a readerAcquiring thread != Releasing thread

Sounds like refcount(9) w/o atomics.

Gleb Smirnoff [email protected] An insane idea on reference counting May 16, 2014 5 / 28

Page 11: lwref: insane idea on reference counting

counter(9) based refcounter API

counter(9) as refcount?

counter(9) - new facility in FreeBSD 10counter_u64_t cnt;

cnt = counter_u64_alloc(M_WAITOK);counter_u64_add(cnt , 1);

lightweight, due to per-CPU memorycounter_u64_add is single instruction on amd64

Gleb Smirnoff [email protected] An insane idea on reference counting May 16, 2014 6 / 28

Page 12: lwref: insane idea on reference counting

counter(9) based refcounter API

Suggested API

struct lwref {void *ptr;counter_u64_t cnt;

};

typedef struct lwref * lwref_t;

Gleb Smirnoff [email protected] An insane idea on reference counting May 16, 2014 7 / 28

Page 13: lwref: insane idea on reference counting

counter(9) based refcounter API

Suggested API

void *lwref_acquire(lwref_t lwr , counter_u64_t *c);

Returns the “configuration” pointer from lwrIncrements counter(9) in lwrReturns the counter(9)

Gleb Smirnoff [email protected] An insane idea on reference counting May 16, 2014 8 / 28

Page 14: lwref: insane idea on reference counting

counter(9) based refcounter API

Suggested API

void lwref_change(lwref_t lwr , void *newptr ,void (* freefn)(void *, void *), void *freearg);

Changes the “configuration” pointer in lwr to newptrAllocates new counter(9) for the lwrAsynchronously frees old pointer and old counter(9)when it is safe

Gleb Smirnoff [email protected] An insane idea on reference counting May 16, 2014 9 / 28

Page 15: lwref: insane idea on reference counting

counter(9) based refcounter API

Suggested API

lwref_acquire must be safe against lwref_changelwref_acquire must not be expensivelwref_change is allowed to be expensive

Gleb Smirnoff [email protected] An insane idea on reference counting May 16, 2014 10 / 28

Page 16: lwref: insane idea on reference counting

implementation restartable function

naive racy lwref_acquire

void *lwref_acquire(lwref_t lwr , counter_u64_t *cp){

void *ptr;

ptr = lwr ->ptr;cp = &lwr ->cnt;

counter_u64_add (*cp, 1);

return (ptr);}

Gleb Smirnoff [email protected] An insane idea on reference counting May 16, 2014 11 / 28

Page 17: lwref: insane idea on reference counting

implementation restartable function

Hypothetical lwref_change operation

Update contents of lwref_t on all CPUsCheck if lwref_acquire is running on any CPU

How check that?And what if it is running?

Gleb Smirnoff [email protected] An insane idea on reference counting May 16, 2014 12 / 28

Page 18: lwref: insane idea on reference counting

implementation restartable function

Hypothetical lwref_change operation

Update contents of lwref_t on all CPUsCheck if lwref_acquire is running on any CPU

How check that?And what if it is running?

Gleb Smirnoff [email protected] An insane idea on reference counting May 16, 2014 12 / 28

Page 19: lwref: insane idea on reference counting

implementation restartable function

lwref_change is SMP rendezvous

voidlwref_change(lwref_t lwr , void *newptr ,

void (* freefn)(void *, void *), void *freearg){

struct lwref_change_ctx ctx;

ctx ->lwr = lwr;ctx ->newptr = newptr;ctx ->newcnt = counter_u64_alloc ();

smp_rendezvous(lwref_change_action , &ctx);}

Gleb Smirnoff [email protected] An insane idea on reference counting May 16, 2014 13 / 28

Page 20: lwref: insane idea on reference counting

implementation restartable function

lwref_change_action code

voidlwref_change_action(void *v){

struct lwref_change_ctx *ctx = v;lwref_t lwr = ctx ->lwr;

lwr ->ptr = ctx ->newptr;lwr ->refcnt = ctx ->newcnt;

/** Check if we interrupted lwref_acquire ().*/

...}

Gleb Smirnoff [email protected] An insane idea on reference counting May 16, 2014 14 / 28

Page 21: lwref: insane idea on reference counting

implementation restartable function

interruption possibilities

The rendezvous IPI interrupted lwref_acquire

Any other interrupt (usually timer) interruptedlwref_acquire and the thread went on scheduler’s runqueue, prior to lwref_change execution

Gleb Smirnoff [email protected] An insane idea on reference counting May 16, 2014 15 / 28

Page 22: lwref: insane idea on reference counting

implementation restartable function

interruption possibilities

The rendezvous IPI interrupted lwref_acquireAny other interrupt (usually timer) interruptedlwref_acquire and the thread went on scheduler’s runqueue, prior to lwref_change execution

Gleb Smirnoff [email protected] An insane idea on reference counting May 16, 2014 15 / 28

Page 23: lwref: insane idea on reference counting

implementation restartable function

restartable lwref_acquire

ENTRY(lwref_acquire)mov (%rdi), %raxmov 0x8(%rdi), %rcxmov %rcx , (%rsi)mov $__pcpu , %rdxsub %rdx , %rcxaddq $1 , %gs:(%rcx)ret

END(lwref_acquire)

Gleb Smirnoff [email protected] An insane idea on reference counting May 16, 2014 16 / 28

Page 24: lwref: insane idea on reference counting

implementation restartable function

restartable lwref_acquire

ENTRY(lwref_acquire)mov (%rdi), %raxmov 0x8(%rdi), %rcxmov %rcx , (%rsi)mov $__pcpu , %rdxsub %rdx , %rcxaddq $1 , %gs:(%rcx)

.globl lwref_acquire_ponrlwref_acquire_ponr:

retEND(lwref_acquire)

Gleb Smirnoff [email protected] An insane idea on reference counting May 16, 2014 17 / 28

Page 25: lwref: insane idea on reference counting

implementation restartable function

When restart?

Option 1: whenever any interrupt interruptslwref_acquireOption 2: whenever lwref_change interruptslwref_acquire

Gleb Smirnoff [email protected] An insane idea on reference counting May 16, 2014 18 / 28

Page 26: lwref: insane idea on reference counting

implementation any interrupt rolls back

Option 1: Any interrupt rolls back

The PUSH_FRAME() macro inamd64/include/asmacros.h should check and fix up %ripin pushed frame.

Pros: very simpleCons: extra instructions on every interrupt

Gleb Smirnoff [email protected] An insane idea on reference counting May 16, 2014 19 / 28

Page 27: lwref: insane idea on reference counting

implementation any interrupt rolls back

Option 1: Any interrupt rolls back

The PUSH_FRAME() macro inamd64/include/asmacros.h should check and fix up %ripin pushed frame.

Pros: very simpleCons: extra instructions on every interrupt

Gleb Smirnoff [email protected] An insane idea on reference counting May 16, 2014 19 / 28

Page 28: lwref: insane idea on reference counting

implementation any interrupt rolls back

change to PUSH_FRAME() macro

@@ -167,7 +167 ,14 @@movw %es ,TF_ES (%rsp) ;movw %ds ,TF_DS (%rsp) ;movl $TF_HASSEGS ,TF_FLAGS (%rsp) ;

- cld+ movq TF_RIP (%rsp), %rax ;+ cmpq %rax , lwref_acquire ;+ jb 2f ;+ cmpq %rax , lwref_acquire_ponr ;+ jae 2f ;+ movq lwref_acquire , %rax ;+ movq %rax , TF_RIP (%rsp) ;+2: cld

Gleb Smirnoff [email protected] An insane idea on reference counting May 16, 2014 20 / 28

Page 29: lwref: insane idea on reference counting

implementation lwref_change_action rolls back

Option 2: lwref_change rolls back

voidlwref_change_action(void *v){

struct trapframe *tf;...

/** Check if we interrupted lwref_acquire ().*/

tf = (struct trapframe *)(( register_t *) __builtin_frame_address (1) + 2);

lwref_fixup_rip (&tf->tf_rip);}

Gleb Smirnoff [email protected] An insane idea on reference counting May 16, 2014 21 / 28

Page 30: lwref: insane idea on reference counting

implementation lwref_change_action rolls back

lwref_fixup_rip

static voidlwref_fixup_rip(register_t *rip){

if (*rip >= (register_t )lwref_acquire &&*rip < (register_t )lwref_acquire_ponr)

*rip = (register_t )lwref_acquire;}

Gleb Smirnoff [email protected] An insane idea on reference counting May 16, 2014 22 / 28

Page 31: lwref: insane idea on reference counting

implementation lwref_change_action rolls back

What about scheduler run queues?

New function:void sched_foreach_on_runq(void (*)(void *));

Gleb Smirnoff [email protected] An insane idea on reference counting May 16, 2014 23 / 28

Page 32: lwref: insane idea on reference counting

implementation lwref_change_action rolls back

lwref_change rolls back (continued)

voidlwref_change_action(void *v){

...sched_foreach_on_runq(lwref_fixup_td);

}

Gleb Smirnoff [email protected] An insane idea on reference counting May 16, 2014 24 / 28

Page 33: lwref: insane idea on reference counting

implementation lwref_change_action rolls back

naive lwref_fixup_td

static voidlwref_fixup_td(void *arg){

struct thread *td = arg;

tf = (struct trapframe *)(( register_t *) (***( void ****)(td ->td_pcb ->

pcb_rbp)) + 2);

lwref_fixup_rip (&tf->tf_rip);}

Gleb Smirnoff [email protected] An insane idea on reference counting May 16, 2014 25 / 28

Page 34: lwref: insane idea on reference counting

implementation lwref_change_action rolls back

static voidlwref_fixup_td(void *arg){

struct thread *td = arg;struct trapframe *tf;register_t *rbp , rip;

for (rbp = (register_t *)td->td_pcb ->pcb_rbp;rbp && rbp < (register_t *)*rbp;rbp = (register_t *)*rbp) {

rip = (register_t )*(rbp + 1);

if (rip == (register_t )timerint_ret ||...rip == (register_t )

ipi_intr_bitmap_handler_ret) {tf = (struct trapframe *)(rbp + 2);lwref_fixup_rip (&tf->tf_rip);

}}

}Gleb Smirnoff [email protected] An insane idea on reference counting May 16, 2014 26 / 28

Page 35: lwref: insane idea on reference counting

implementation lwref_change_action rolls back

hint from jhb@

Use td->td_frame to get access to frame :)

Gleb Smirnoff [email protected] An insane idea on reference counting May 16, 2014 27 / 28

Page 36: lwref: insane idea on reference counting

implementation lwref_change_action rolls back

Questions?

Gleb Smirnoff [email protected] An insane idea on reference counting May 16, 2014 28 / 28