Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
V
varnish-cache
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Commits
Open sidebar
varnishcache
varnish-cache
Commits
6ed66f50
Commit
6ed66f50
authored
Feb 05, 2016
by
Poul-Henning Kamp
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Make it possible for persistent to avoid LRU.
parent
658932ab
Changes
10
Show whitespace changes
Inline
Side-by-side
Showing
10 changed files
with
25 additions
and
47 deletions
+25
-47
cache_obj.h
bin/varnishd/cache/cache_obj.h
+0
-2
stevedore.c
bin/varnishd/storage/stevedore.c
+0
-2
storage.h
bin/varnishd/storage/storage.h
+1
-1
storage_file.c
bin/varnishd/storage/storage_file.c
+3
-2
storage_lru.c
bin/varnishd/storage/storage_lru.c
+1
-5
storage_malloc.c
bin/varnishd/storage/storage_malloc.c
+3
-2
storage_persistent.c
bin/varnishd/storage/storage_persistent.c
+4
-5
storage_persistent.h
bin/varnishd/storage/storage_persistent.h
+1
-1
storage_persistent_silo.c
bin/varnishd/storage/storage_persistent_silo.c
+5
-23
storage_simple.c
bin/varnishd/storage/storage_simple.c
+7
-4
No files found.
bin/varnishd/cache/cache_obj.h
View file @
6ed66f50
...
@@ -32,7 +32,6 @@
...
@@ -32,7 +32,6 @@
typedef
void
objupdatemeta_f
(
struct
worker
*
,
struct
objcore
*
);
typedef
void
objupdatemeta_f
(
struct
worker
*
,
struct
objcore
*
);
typedef
void
objfree_f
(
struct
worker
*
,
struct
objcore
*
);
typedef
void
objfree_f
(
struct
worker
*
,
struct
objcore
*
);
typedef
struct
lru
*
objgetlru_f
(
const
struct
objcore
*
);
/* This method is only used by SML (...to get to persistent) */
/* This method is only used by SML (...to get to persistent) */
typedef
struct
object
*
sml_getobj_f
(
struct
worker
*
,
struct
objcore
*
);
typedef
struct
object
*
sml_getobj_f
(
struct
worker
*
,
struct
objcore
*
);
...
@@ -53,7 +52,6 @@ typedef void objtouch_f(struct worker *, struct objcore *, double now);
...
@@ -53,7 +52,6 @@ typedef void objtouch_f(struct worker *, struct objcore *, double now);
struct
obj_methods
{
struct
obj_methods
{
objfree_f
*
objfree
;
objfree_f
*
objfree
;
objgetlru_f
*
objgetlru
;
objupdatemeta_f
*
objupdatemeta
;
objupdatemeta_f
*
objupdatemeta
;
sml_getobj_f
*
sml_getobj
;
sml_getobj_f
*
sml_getobj
;
...
...
bin/varnishd/storage/stevedore.c
View file @
6ed66f50
...
@@ -128,12 +128,10 @@ STV_open(void)
...
@@ -128,12 +128,10 @@ STV_open(void)
struct
stevedore
*
stv
;
struct
stevedore
*
stv
;
VTAILQ_FOREACH
(
stv
,
&
stv_stevedores
,
list
)
{
VTAILQ_FOREACH
(
stv
,
&
stv_stevedores
,
list
)
{
stv
->
lru
=
LRU_Alloc
();
if
(
stv
->
open
!=
NULL
)
if
(
stv
->
open
!=
NULL
)
stv
->
open
(
stv
);
stv
->
open
(
stv
);
}
}
stv
=
stv_transient
;
stv
=
stv_transient
;
stv
->
lru
=
LRU_Alloc
();
if
(
stv
->
open
!=
NULL
)
if
(
stv
->
open
!=
NULL
)
stv
->
open
(
stv
);
stv
->
open
(
stv
);
stv_next
=
VTAILQ_FIRST
(
&
stv_stevedores
);
stv_next
=
VTAILQ_FIRST
(
&
stv_stevedores
);
...
...
bin/varnishd/storage/storage.h
View file @
6ed66f50
...
@@ -58,7 +58,7 @@ struct storage {
...
@@ -58,7 +58,7 @@ struct storage {
/* Prototypes --------------------------------------------------------*/
/* Prototypes --------------------------------------------------------*/
typedef
void
storage_init_f
(
struct
stevedore
*
,
int
ac
,
char
*
const
*
av
);
typedef
void
storage_init_f
(
struct
stevedore
*
,
int
ac
,
char
*
const
*
av
);
typedef
void
storage_open_f
(
const
struct
stevedore
*
);
typedef
void
storage_open_f
(
struct
stevedore
*
);
typedef
struct
storage
*
storage_alloc_f
(
const
struct
stevedore
*
,
size_t
size
);
typedef
struct
storage
*
storage_alloc_f
(
const
struct
stevedore
*
,
size_t
size
);
typedef
void
storage_free_f
(
struct
storage
*
);
typedef
void
storage_free_f
(
struct
storage
*
);
typedef
int
storage_allocobj_f
(
struct
worker
*
,
const
struct
stevedore
*
,
typedef
int
storage_allocobj_f
(
struct
worker
*
,
const
struct
stevedore
*
,
...
...
bin/varnishd/storage/storage_file.c
View file @
6ed66f50
...
@@ -385,14 +385,15 @@ smf_open_chunk(struct smf_sc *sc, off_t sz, off_t off, off_t *fail, off_t *sum)
...
@@ -385,14 +385,15 @@ smf_open_chunk(struct smf_sc *sc, off_t sz, off_t off, off_t *fail, off_t *sum)
smf_open_chunk
(
sc
,
sz
-
h
,
off
+
h
,
fail
,
sum
);
smf_open_chunk
(
sc
,
sz
-
h
,
off
+
h
,
fail
,
sum
);
}
}
static
void
static
void
__match_proto__
(
storage_open_f
)
smf_open
(
const
struct
stevedore
*
st
)
smf_open
(
struct
stevedore
*
st
)
{
{
struct
smf_sc
*
sc
;
struct
smf_sc
*
sc
;
off_t
fail
=
1
<<
30
;
/* XXX: where is OFF_T_MAX ? */
off_t
fail
=
1
<<
30
;
/* XXX: where is OFF_T_MAX ? */
off_t
sum
=
0
;
off_t
sum
=
0
;
ASSERT_CLI
();
ASSERT_CLI
();
st
->
lru
=
LRU_Alloc
();
if
(
lck_smf
==
NULL
)
if
(
lck_smf
==
NULL
)
lck_smf
=
Lck_CreateClass
(
"smf"
);
lck_smf
=
Lck_CreateClass
(
"smf"
);
CAST_OBJ_NOTNULL
(
sc
,
st
->
priv
,
SMF_SC_MAGIC
);
CAST_OBJ_NOTNULL
(
sc
,
st
->
priv
,
SMF_SC_MAGIC
);
...
...
bin/varnishd/storage/storage_lru.c
View file @
6ed66f50
...
@@ -50,11 +50,7 @@ lru_get(const struct objcore *oc)
...
@@ -50,11 +50,7 @@ lru_get(const struct objcore *oc)
{
{
CHECK_OBJ_NOTNULL
(
oc
,
OBJCORE_MAGIC
);
CHECK_OBJ_NOTNULL
(
oc
,
OBJCORE_MAGIC
);
CHECK_OBJ_NOTNULL
(
oc
->
stobj
->
stevedore
,
STEVEDORE_MAGIC
);
CHECK_OBJ_NOTNULL
(
oc
->
stobj
->
stevedore
,
STEVEDORE_MAGIC
);
AN
(
oc
->
stobj
->
stevedore
->
methods
);
CHECK_OBJ_NOTNULL
(
oc
->
stobj
->
stevedore
->
lru
,
LRU_MAGIC
);
const
struct
obj_methods
*
m
=
oc
->
stobj
->
stevedore
->
methods
;
if
(
m
->
objgetlru
!=
NULL
)
return
(
m
->
objgetlru
(
oc
));
return
(
oc
->
stobj
->
stevedore
->
lru
);
return
(
oc
->
stobj
->
stevedore
->
lru
);
}
}
...
...
bin/varnishd/storage/storage_malloc.c
View file @
6ed66f50
...
@@ -198,12 +198,13 @@ sma_init(struct stevedore *parent, int ac, char * const *av)
...
@@ -198,12 +198,13 @@ sma_init(struct stevedore *parent, int ac, char * const *av)
sc
->
sma_max
=
u
;
sc
->
sma_max
=
u
;
}
}
static
void
static
void
__match_proto__
(
storage_open_f
)
sma_open
(
const
struct
stevedore
*
st
)
sma_open
(
struct
stevedore
*
st
)
{
{
struct
sma_sc
*
sma_sc
;
struct
sma_sc
*
sma_sc
;
ASSERT_CLI
();
ASSERT_CLI
();
st
->
lru
=
LRU_Alloc
();
if
(
lck_sma
==
NULL
)
if
(
lck_sma
==
NULL
)
lck_sma
=
Lck_CreateClass
(
"sma"
);
lck_sma
=
Lck_CreateClass
(
"sma"
);
CAST_OBJ_NOTNULL
(
sma_sc
,
st
->
priv
,
SMA_SC_MAGIC
);
CAST_OBJ_NOTNULL
(
sma_sc
,
st
->
priv
,
SMA_SC_MAGIC
);
...
...
bin/varnishd/storage/storage_persistent.c
View file @
6ed66f50
...
@@ -234,8 +234,7 @@ smp_open_segs(struct smp_sc *sc, struct smp_signspace *spc)
...
@@ -234,8 +234,7 @@ smp_open_segs(struct smp_sc *sc, struct smp_signspace *spc)
for
(;
ss
<=
se
;
ss
++
)
{
for
(;
ss
<=
se
;
ss
++
)
{
ALLOC_OBJ
(
sg
,
SMP_SEG_MAGIC
);
ALLOC_OBJ
(
sg
,
SMP_SEG_MAGIC
);
AN
(
sg
);
AN
(
sg
);
sg
->
lru
=
LRU_Alloc
();
VTAILQ_INIT
(
&
sg
->
objcores
);
AN
(
sg
->
lru
);
sg
->
p
=
*
ss
;
sg
->
p
=
*
ss
;
sg
->
flags
|=
SMP_SEG_MUSTLOAD
;
sg
->
flags
|=
SMP_SEG_MUSTLOAD
;
...
@@ -320,8 +319,8 @@ smp_thread(struct worker *wrk, void *priv)
...
@@ -320,8 +319,8 @@ smp_thread(struct worker *wrk, void *priv)
* Open a silo in the worker process
* Open a silo in the worker process
*/
*/
static
void
static
void
__match_proto__
(
storage_open_f
)
smp_open
(
const
struct
stevedore
*
st
)
smp_open
(
struct
stevedore
*
st
)
{
{
struct
smp_sc
*
sc
;
struct
smp_sc
*
sc
;
...
@@ -569,6 +568,7 @@ smp_allocobj(struct worker *wrk, const struct stevedore *stv,
...
@@ -569,6 +568,7 @@ smp_allocobj(struct worker *wrk, const struct stevedore *stv,
smp_init_oc
(
oc
,
sg
,
objidx
);
smp_init_oc
(
oc
,
sg
,
objidx
);
VTAILQ_INSERT_TAIL
(
&
sg
->
objcores
,
oc
,
lru_list
);
Lck_Unlock
(
&
sc
->
mtx
);
Lck_Unlock
(
&
sc
->
mtx
);
return
(
1
);
return
(
1
);
}
}
...
@@ -689,7 +689,6 @@ SMP_Init(void)
...
@@ -689,7 +689,6 @@ SMP_Init(void)
smp_oc_realmethods
.
sml_getobj
=
smp_oc_methods
.
sml_getobj
;
smp_oc_realmethods
.
sml_getobj
=
smp_oc_methods
.
sml_getobj
;
smp_oc_realmethods
.
objupdatemeta
=
smp_oc_methods
.
objupdatemeta
;
smp_oc_realmethods
.
objupdatemeta
=
smp_oc_methods
.
objupdatemeta
;
smp_oc_realmethods
.
objfree
=
smp_oc_methods
.
objfree
;
smp_oc_realmethods
.
objfree
=
smp_oc_methods
.
objfree
;
smp_oc_realmethods
.
objgetlru
=
smp_oc_methods
.
objgetlru
;
smp_oc_realmethods
.
objtouch
=
NULL
;
smp_oc_realmethods
.
objtouch
=
NULL
;
}
}
...
...
bin/varnishd/storage/storage_persistent.h
View file @
6ed66f50
...
@@ -193,7 +193,7 @@ struct smp_seg {
...
@@ -193,7 +193,7 @@ struct smp_seg {
#define SMP_SEG_MAGIC 0x45c61895
#define SMP_SEG_MAGIC 0x45c61895
struct
smp_sc
*
sc
;
struct
smp_sc
*
sc
;
struct
lru
*
lru
;
VTAILQ_HEAD
(,
objcore
)
objcores
;
VTAILQ_ENTRY
(
smp_seg
)
list
;
/* on smp_sc.smp_segments */
VTAILQ_ENTRY
(
smp_seg
)
list
;
/* on smp_sc.smp_segments */
...
...
bin/varnishd/storage/storage_persistent_silo.c
View file @
6ed66f50
...
@@ -106,7 +106,7 @@ smp_save_segs(struct smp_sc *sc)
...
@@ -106,7 +106,7 @@ smp_save_segs(struct smp_sc *sc)
if
(
sg
==
sc
->
cur_seg
)
if
(
sg
==
sc
->
cur_seg
)
continue
;
continue
;
VTAILQ_REMOVE
(
&
sc
->
segments
,
sg
,
list
);
VTAILQ_REMOVE
(
&
sc
->
segments
,
sg
,
list
);
LRU_Free
(
&
sg
->
lru
);
AN
(
VTAILQ_EMPTY
(
&
sg
->
objcores
)
);
FREE_OBJ
(
sg
);
FREE_OBJ
(
sg
);
}
}
smp_save_seg
(
sc
,
&
sc
->
seg1
);
smp_save_seg
(
sc
,
&
sc
->
seg1
);
...
@@ -163,6 +163,7 @@ smp_load_seg(struct worker *wrk, const struct smp_sc *sc,
...
@@ -163,6 +163,7 @@ smp_load_seg(struct worker *wrk, const struct smp_sc *sc,
oc
->
flags
&=
~
OC_F_BUSY
;
oc
->
flags
&=
~
OC_F_BUSY
;
oc
->
stobj
->
stevedore
=
sc
->
parent
;
oc
->
stobj
->
stevedore
=
sc
->
parent
;
smp_init_oc
(
oc
,
sg
,
no
);
smp_init_oc
(
oc
,
sg
,
no
);
VTAILQ_INSERT_TAIL
(
&
sg
->
objcores
,
oc
,
lru_list
);
oc
->
stobj
->
priv2
|=
NEED_FIXUP
;
oc
->
stobj
->
priv2
|=
NEED_FIXUP
;
oc
->
ban
=
BAN_RefBan
(
oc
,
so
->
ban
);
oc
->
ban
=
BAN_RefBan
(
oc
,
so
->
ban
);
oc
->
exp
=
so
->
exp
;
oc
->
exp
=
so
->
exp
;
...
@@ -170,9 +171,7 @@ smp_load_seg(struct worker *wrk, const struct smp_sc *sc,
...
@@ -170,9 +171,7 @@ smp_load_seg(struct worker *wrk, const struct smp_sc *sc,
oc
->
refcnt
++
;
oc
->
refcnt
++
;
HSH_Insert
(
wrk
,
so
->
hash
,
oc
);
HSH_Insert
(
wrk
,
so
->
hash
,
oc
);
EXP_Insert
(
wrk
,
oc
);
EXP_Insert
(
wrk
,
oc
);
AN
(
isnan
(
oc
->
last_lru
));
HSH_DerefBoc
(
wrk
,
oc
);
// XXX Keep it an stream resurrection?
HSH_DerefBoc
(
wrk
,
oc
);
// XXX Keep it an stream resurrection?
AZ
(
isnan
(
oc
->
last_lru
));
(
void
)
HSH_DerefObjCore
(
wrk
,
&
oc
);
(
void
)
HSH_DerefObjCore
(
wrk
,
&
oc
);
}
}
Pool_Sumstat
(
wrk
);
Pool_Sumstat
(
wrk
);
...
@@ -224,11 +223,9 @@ smp_new_seg(struct smp_sc *sc)
...
@@ -224,11 +223,9 @@ smp_new_seg(struct smp_sc *sc)
ALLOC_OBJ
(
sg
,
SMP_SEG_MAGIC
);
ALLOC_OBJ
(
sg
,
SMP_SEG_MAGIC
);
if
(
sg
==
NULL
)
if
(
sg
==
NULL
)
/* Failed allocation */
return
;
return
;
*
sg
=
tmpsg
;
*
sg
=
tmpsg
;
sg
->
lru
=
LRU_Alloc
();
VTAILQ_INIT
(
&
sg
->
objcores
);
AN
(
sg
->
lru
);
sg
->
p
.
offset
=
IRNUP
(
sc
,
sg
->
p
.
offset
);
sg
->
p
.
offset
=
IRNUP
(
sc
,
sg
->
p
.
offset
);
sg
->
p
.
length
-=
sg
->
p
.
offset
-
tmpsg
.
p
.
offset
;
sg
->
p
.
length
-=
sg
->
p
.
offset
-
tmpsg
.
p
.
offset
;
...
@@ -277,7 +274,7 @@ smp_close_seg(struct smp_sc *sc, struct smp_seg *sg)
...
@@ -277,7 +274,7 @@ smp_close_seg(struct smp_sc *sc, struct smp_seg *sg)
assert
(
sg
->
p
.
offset
>=
sc
->
ident
->
stuff
[
SMP_SPC_STUFF
]);
assert
(
sg
->
p
.
offset
>=
sc
->
ident
->
stuff
[
SMP_SPC_STUFF
]);
assert
(
sg
->
p
.
offset
<
sc
->
mediasize
);
assert
(
sg
->
p
.
offset
<
sc
->
mediasize
);
sc
->
free_offset
=
sg
->
p
.
offset
;
sc
->
free_offset
=
sg
->
p
.
offset
;
LRU_Free
(
&
sg
->
lru
);
AN
(
VTAILQ_EMPTY
(
&
sg
->
objcores
)
);
FREE_OBJ
(
sg
);
FREE_OBJ
(
sg
);
return
;
return
;
}
}
...
@@ -509,31 +506,16 @@ smp_oc_objfree(struct worker *wrk, struct objcore *oc)
...
@@ -509,31 +506,16 @@ smp_oc_objfree(struct worker *wrk, struct objcore *oc)
sg
->
nfixed
--
;
sg
->
nfixed
--
;
wrk
->
stats
->
n_object
--
;
wrk
->
stats
->
n_object
--
;
}
}
AZ
(
isnan
(
oc
->
last_lru
));
VTAILQ_REMOVE
(
&
sg
->
objcores
,
oc
,
lru_list
);
LRU_Remove
(
oc
);
Lck_Unlock
(
&
sg
->
sc
->
mtx
);
Lck_Unlock
(
&
sg
->
sc
->
mtx
);
memset
(
oc
->
stobj
,
0
,
sizeof
oc
->
stobj
);
memset
(
oc
->
stobj
,
0
,
sizeof
oc
->
stobj
);
}
}
/*--------------------------------------------------------------------
* Find the per-segment lru list for this object
*/
static
struct
lru
*
__match_proto__
(
objgetlru_f
)
smp_oc_objgetlru
(
const
struct
objcore
*
oc
)
{
struct
smp_seg
*
sg
;
CAST_OBJ_NOTNULL
(
sg
,
oc
->
stobj
->
priv
,
SMP_SEG_MAGIC
);
return
(
sg
->
lru
);
}
const
struct
obj_methods
smp_oc_methods
=
{
const
struct
obj_methods
smp_oc_methods
=
{
.
sml_getobj
=
smp_oc_sml_getobj
,
.
sml_getobj
=
smp_oc_sml_getobj
,
.
objupdatemeta
=
smp_oc_objupdatemeta
,
.
objupdatemeta
=
smp_oc_objupdatemeta
,
.
objfree
=
smp_oc_objfree
,
.
objfree
=
smp_oc_objfree
,
.
objgetlru
=
smp_oc_objgetlru
,
};
};
/*--------------------------------------------------------------------*/
/*--------------------------------------------------------------------*/
...
...
bin/varnishd/storage/storage_simple.c
View file @
6ed66f50
...
@@ -206,7 +206,7 @@ sml_objfree(struct worker *wrk, struct objcore *oc)
...
@@ -206,7 +206,7 @@ sml_objfree(struct worker *wrk, struct objcore *oc)
CAST_OBJ_NOTNULL
(
o
,
oc
->
stobj
->
priv
,
OBJECT_MAGIC
);
CAST_OBJ_NOTNULL
(
o
,
oc
->
stobj
->
priv
,
OBJECT_MAGIC
);
o
->
magic
=
0
;
o
->
magic
=
0
;
if
(
oc
->
boc
==
NULL
)
if
(
oc
->
boc
==
NULL
&&
oc
->
stobj
->
stevedore
->
lru
!=
NULL
)
LRU_Remove
(
oc
);
LRU_Remove
(
oc
);
sml_stv_free
(
oc
->
stobj
->
stevedore
,
o
->
objstore
);
sml_stv_free
(
oc
->
stobj
->
stevedore
,
o
->
objstore
);
...
@@ -329,6 +329,8 @@ objallocwithnuke(struct worker *wrk, const struct stevedore *stv, size_t size)
...
@@ -329,6 +329,8 @@ objallocwithnuke(struct worker *wrk, const struct stevedore *stv, size_t size)
break
;
break
;
/* no luck; try to free some space and keep trying */
/* no luck; try to free some space and keep trying */
if
(
stv
->
lru
==
NULL
)
break
;
if
(
fail
<
cache_param
->
nuke_limit
&&
if
(
fail
<
cache_param
->
nuke_limit
&&
!
LRU_NukeOne
(
wrk
,
stv
->
lru
))
!
LRU_NukeOne
(
wrk
,
stv
->
lru
))
break
;
break
;
...
@@ -457,17 +459,18 @@ sml_stable(struct worker *wrk, struct objcore *oc, struct boc *boc)
...
@@ -457,17 +459,18 @@ sml_stable(struct worker *wrk, struct objcore *oc, struct boc *boc)
CHECK_OBJ_NOTNULL
(
wrk
,
WORKER_MAGIC
);
CHECK_OBJ_NOTNULL
(
wrk
,
WORKER_MAGIC
);
CHECK_OBJ_NOTNULL
(
oc
,
OBJCORE_MAGIC
);
CHECK_OBJ_NOTNULL
(
oc
,
OBJCORE_MAGIC
);
CHECK_OBJ_NOTNULL
(
boc
,
BOC_MAGIC
);
CHECK_OBJ_NOTNULL
(
boc
,
BOC_MAGIC
);
stv
=
oc
->
stobj
->
stevedore
;
CHECK_OBJ_NOTNULL
(
stv
,
STEVEDORE_MAGIC
);
if
(
boc
->
stevedore_priv
!=
NULL
)
{
if
(
boc
->
stevedore_priv
!=
NULL
)
{
/* Free any leftovers from Trim */
/* Free any leftovers from Trim */
CAST_OBJ_NOTNULL
(
st
,
boc
->
stevedore_priv
,
STORAGE_MAGIC
);
CAST_OBJ_NOTNULL
(
st
,
boc
->
stevedore_priv
,
STORAGE_MAGIC
);
boc
->
stevedore_priv
=
0
;
boc
->
stevedore_priv
=
0
;
CHECK_OBJ_NOTNULL
(
st
,
STORAGE_MAGIC
);
CHECK_OBJ_NOTNULL
(
st
,
STORAGE_MAGIC
);
stv
=
oc
->
stobj
->
stevedore
;
CHECK_OBJ_NOTNULL
(
stv
,
STEVEDORE_MAGIC
);
sml_stv_free
(
stv
,
st
);
sml_stv_free
(
stv
,
st
);
}
}
if
(
stv
->
lru
!=
NULL
)
LRU_Add
(
oc
,
wrk
->
lastused
);
// approx timestamp is OK
LRU_Add
(
oc
,
wrk
->
lastused
);
// approx timestamp is OK
}
}
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment