Commit f5daba1d authored by Heiko Carstens's avatar Heiko Carstens Committed by Martin Schwidefsky

[S390] split/move machine check handler code

Split machine check handler code and move it to cio and kernel code
where it belongs to. No functional change.
Signed-off-by: default avatarHeiko Carstens <heiko.carstens@de.ibm.com>
Signed-off-by: default avatarMartin Schwidefsky <schwidefsky@de.ibm.com>
parent 70193af9
/*
* Data definitions for channel report processing
* Copyright IBM Corp. 2000,2009
* Author(s): Ingo Adlung <adlung@de.ibm.com>,
* Martin Schwidefsky <schwidefsky@de.ibm.com>,
* Cornelia Huck <cornelia.huck@de.ibm.com>,
* Heiko Carstens <heiko.carstens@de.ibm.com>,
*/
#ifndef _ASM_S390_CRW_H
#define _ASM_S390_CRW_H
#include <linux/types.h>
/*
* Channel Report Word
*/
struct crw {
__u32 res1 : 1; /* reserved zero */
__u32 slct : 1; /* solicited */
__u32 oflw : 1; /* overflow */
__u32 chn : 1; /* chained */
__u32 rsc : 4; /* reporting source code */
__u32 anc : 1; /* ancillary report */
__u32 res2 : 1; /* reserved zero */
__u32 erc : 6; /* error-recovery code */
__u32 rsid : 16; /* reporting-source ID */
} __attribute__ ((packed));
typedef void (*crw_handler_t)(struct crw *, struct crw *, int);
extern int crw_register_handler(int rsc, crw_handler_t handler);
extern void crw_unregister_handler(int rsc);
extern void crw_handle_channel_report(void);
#define NR_RSCS 16
#define CRW_RSC_MONITOR 0x2 /* monitoring facility */
#define CRW_RSC_SCH 0x3 /* subchannel */
#define CRW_RSC_CPATH 0x4 /* channel path */
#define CRW_RSC_CONFIG 0x9 /* configuration-alert facility */
#define CRW_RSC_CSS 0xB /* channel subsystem */
#define CRW_ERC_EVENT 0x00 /* event information pending */
#define CRW_ERC_AVAIL 0x01 /* available */
#define CRW_ERC_INIT 0x02 /* initialized */
#define CRW_ERC_TERROR 0x03 /* temporary error */
#define CRW_ERC_IPARM 0x04 /* installed parm initialized */
#define CRW_ERC_TERM 0x05 /* terminal */
#define CRW_ERC_PERRN 0x06 /* perm. error, fac. not init */
#define CRW_ERC_PERRI 0x07 /* perm. error, facility init */
#define CRW_ERC_PMOD 0x08 /* installed parameters modified */
static inline int stcrw(struct crw *pcrw)
{
int ccode;
asm volatile(
" stcrw 0(%2)\n"
" ipm %0\n"
" srl %0,28\n"
: "=d" (ccode), "=m" (*pcrw)
: "a" (pcrw)
: "cc" );
return ccode;
}
#endif /* _ASM_S390_CRW_H */
/*
* Machine check handler definitions
*
* Copyright IBM Corp. 2000,2009
* Author(s): Ingo Adlung <adlung@de.ibm.com>,
* Martin Schwidefsky <schwidefsky@de.ibm.com>,
* Cornelia Huck <cornelia.huck@de.ibm.com>,
* Heiko Carstens <heiko.carstens@de.ibm.com>,
*/
#ifndef _ASM_S390_NMI_H
#define _ASM_S390_NMI_H
#include <linux/types.h>
struct mci {
__u32 sd : 1; /* 00 system damage */
__u32 pd : 1; /* 01 instruction-processing damage */
__u32 sr : 1; /* 02 system recovery */
__u32 : 1; /* 03 */
__u32 cd : 1; /* 04 timing-facility damage */
__u32 ed : 1; /* 05 external damage */
__u32 : 1; /* 06 */
__u32 dg : 1; /* 07 degradation */
__u32 w : 1; /* 08 warning pending */
__u32 cp : 1; /* 09 channel-report pending */
__u32 sp : 1; /* 10 service-processor damage */
__u32 ck : 1; /* 11 channel-subsystem damage */
__u32 : 2; /* 12-13 */
__u32 b : 1; /* 14 backed up */
__u32 : 1; /* 15 */
__u32 se : 1; /* 16 storage error uncorrected */
__u32 sc : 1; /* 17 storage error corrected */
__u32 ke : 1; /* 18 storage-key error uncorrected */
__u32 ds : 1; /* 19 storage degradation */
__u32 wp : 1; /* 20 psw mwp validity */
__u32 ms : 1; /* 21 psw mask and key validity */
__u32 pm : 1; /* 22 psw program mask and cc validity */
__u32 ia : 1; /* 23 psw instruction address validity */
__u32 fa : 1; /* 24 failing storage address validity */
__u32 : 1; /* 25 */
__u32 ec : 1; /* 26 external damage code validity */
__u32 fp : 1; /* 27 floating point register validity */
__u32 gr : 1; /* 28 general register validity */
__u32 cr : 1; /* 29 control register validity */
__u32 : 1; /* 30 */
__u32 st : 1; /* 31 storage logical validity */
__u32 ie : 1; /* 32 indirect storage error */
__u32 ar : 1; /* 33 access register validity */
__u32 da : 1; /* 34 delayed access exception */
__u32 : 7; /* 35-41 */
__u32 pr : 1; /* 42 tod programmable register validity */
__u32 fc : 1; /* 43 fp control register validity */
__u32 ap : 1; /* 44 ancillary report */
__u32 : 1; /* 45 */
__u32 ct : 1; /* 46 cpu timer validity */
__u32 cc : 1; /* 47 clock comparator validity */
__u32 : 16; /* 47-63 */
};
struct pt_regs;
extern void s390_handle_mcck(void);
extern void s390_do_machine_check(struct pt_regs *regs);
#endif /* _ASM_S390_NMI_H */
...@@ -22,7 +22,7 @@ CFLAGS_sysinfo.o += -Iinclude/math-emu -Iarch/s390/math-emu -w ...@@ -22,7 +22,7 @@ CFLAGS_sysinfo.o += -Iinclude/math-emu -Iarch/s390/math-emu -w
obj-y := bitmap.o traps.o time.o process.o base.o early.o setup.o \ obj-y := bitmap.o traps.o time.o process.o base.o early.o setup.o \
processor.o sys_s390.o ptrace.o signal.o cpcmd.o ebcdic.o \ processor.o sys_s390.o ptrace.o signal.o cpcmd.o ebcdic.o \
s390_ext.o debug.o irq.o ipl.o dis.o diag.o mem_detect.o \ s390_ext.o debug.o irq.o ipl.o dis.o diag.o mem_detect.o \
vdso.o vtime.o sysinfo.o vdso.o vtime.o sysinfo.o nmi.o
obj-y += $(if $(CONFIG_64BIT),entry64.o,entry.o) obj-y += $(if $(CONFIG_64BIT),entry64.o,entry.o)
obj-y += $(if $(CONFIG_64BIT),reipl64.o,reipl.o) obj-y += $(if $(CONFIG_64BIT),reipl64.o,reipl.o)
......
...@@ -39,6 +39,7 @@ ...@@ -39,6 +39,7 @@
#include <asm/processor.h> #include <asm/processor.h>
#include <asm/irq.h> #include <asm/irq.h>
#include <asm/timer.h> #include <asm/timer.h>
#include <asm/nmi.h>
#include "entry.h" #include "entry.h"
asmlinkage void ret_from_fork(void) asm ("ret_from_fork"); asmlinkage void ret_from_fork(void) asm ("ret_from_fork");
...@@ -68,7 +69,6 @@ unsigned long thread_saved_pc(struct task_struct *tsk) ...@@ -68,7 +69,6 @@ unsigned long thread_saved_pc(struct task_struct *tsk)
return sf->gprs[8]; return sf->gprs[8];
} }
extern void s390_handle_mcck(void);
/* /*
* The idle loop on a S390... * The idle loop on a S390...
*/ */
......
...@@ -23,7 +23,7 @@ ...@@ -23,7 +23,7 @@
#include <linux/timer.h> #include <linux/timer.h>
#include <asm/lowcore.h> #include <asm/lowcore.h>
#include <asm/pgtable.h> #include <asm/pgtable.h>
#include <asm/nmi.h>
#include "kvm-s390.h" #include "kvm-s390.h"
#include "gaccess.h" #include "gaccess.h"
...@@ -440,8 +440,6 @@ int kvm_arch_vcpu_ioctl_set_mpstate(struct kvm_vcpu *vcpu, ...@@ -440,8 +440,6 @@ int kvm_arch_vcpu_ioctl_set_mpstate(struct kvm_vcpu *vcpu,
return -EINVAL; /* not implemented yet */ return -EINVAL; /* not implemented yet */
} }
extern void s390_handle_mcck(void);
static void __vcpu_run(struct kvm_vcpu *vcpu) static void __vcpu_run(struct kvm_vcpu *vcpu)
{ {
memcpy(&vcpu->arch.sie_block->gg14, &vcpu->arch.guest_gprs[14], 16); memcpy(&vcpu->arch.sie_block->gg14, &vcpu->arch.guest_gprs[14], 16);
......
...@@ -2,7 +2,6 @@ ...@@ -2,7 +2,6 @@
# Makefile for the S/390 specific device drivers # Makefile for the S/390 specific device drivers
# #
obj-y += s390mach.o
obj-y += cio/ block/ char/ crypto/ net/ scsi/ kvm/ obj-y += cio/ block/ char/ crypto/ net/ scsi/ kvm/
drivers-y += drivers/s390/built-in.o drivers-y += drivers/s390/built-in.o
......
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
# #
obj-y += airq.o blacklist.o chsc.o cio.o css.o chp.o idset.o isc.o scsw.o \ obj-y += airq.o blacklist.o chsc.o cio.o css.o chp.o idset.o isc.o scsw.o \
fcx.o itcw.o fcx.o itcw.o crw.o
ccw_device-objs += device.o device_fsm.o device_ops.o ccw_device-objs += device.o device_fsm.o device_ops.o
ccw_device-objs += device_id.o device_pgid.o device_status.o ccw_device-objs += device_id.o device_pgid.o device_status.o
obj-y += ccw_device.o cmf.o obj-y += ccw_device.o cmf.o
......
...@@ -17,8 +17,8 @@ ...@@ -17,8 +17,8 @@
#include <linux/errno.h> #include <linux/errno.h>
#include <asm/chpid.h> #include <asm/chpid.h>
#include <asm/sclp.h> #include <asm/sclp.h>
#include <asm/crw.h>
#include "../s390mach.h"
#include "cio.h" #include "cio.h"
#include "css.h" #include "css.h"
#include "ioasm.h" #include "ioasm.h"
...@@ -706,12 +706,12 @@ static int __init chp_init(void) ...@@ -706,12 +706,12 @@ static int __init chp_init(void)
struct chp_id chpid; struct chp_id chpid;
int ret; int ret;
ret = s390_register_crw_handler(CRW_RSC_CPATH, chp_process_crw); ret = crw_register_handler(CRW_RSC_CPATH, chp_process_crw);
if (ret) if (ret)
return ret; return ret;
chp_wq = create_singlethread_workqueue("cio_chp"); chp_wq = create_singlethread_workqueue("cio_chp");
if (!chp_wq) { if (!chp_wq) {
s390_unregister_crw_handler(CRW_RSC_CPATH); crw_unregister_handler(CRW_RSC_CPATH);
return -ENOMEM; return -ENOMEM;
} }
INIT_WORK(&cfg_work, cfg_func); INIT_WORK(&cfg_work, cfg_func);
......
...@@ -19,8 +19,8 @@ ...@@ -19,8 +19,8 @@
#include <asm/cio.h> #include <asm/cio.h>
#include <asm/chpid.h> #include <asm/chpid.h>
#include <asm/chsc.h> #include <asm/chsc.h>
#include <asm/crw.h>
#include "../s390mach.h"
#include "css.h" #include "css.h"
#include "cio.h" #include "cio.h"
#include "cio_debug.h" #include "cio_debug.h"
...@@ -820,7 +820,7 @@ int __init chsc_alloc_sei_area(void) ...@@ -820,7 +820,7 @@ int __init chsc_alloc_sei_area(void)
"chsc machine checks!\n"); "chsc machine checks!\n");
return -ENOMEM; return -ENOMEM;
} }
ret = s390_register_crw_handler(CRW_RSC_CSS, chsc_process_crw); ret = crw_register_handler(CRW_RSC_CSS, chsc_process_crw);
if (ret) if (ret)
kfree(sei_page); kfree(sei_page);
return ret; return ret;
...@@ -828,7 +828,7 @@ int __init chsc_alloc_sei_area(void) ...@@ -828,7 +828,7 @@ int __init chsc_alloc_sei_area(void)
void __init chsc_free_sei_area(void) void __init chsc_free_sei_area(void)
{ {
s390_unregister_crw_handler(CRW_RSC_CSS); crw_unregister_handler(CRW_RSC_CSS);
kfree(sei_page); kfree(sei_page);
} }
......
...@@ -30,6 +30,8 @@ ...@@ -30,6 +30,8 @@
#include <asm/isc.h> #include <asm/isc.h>
#include <asm/cpu.h> #include <asm/cpu.h>
#include <asm/fcx.h> #include <asm/fcx.h>
#include <asm/nmi.h>
#include <asm/crw.h>
#include "cio.h" #include "cio.h"
#include "css.h" #include "css.h"
#include "chsc.h" #include "chsc.h"
...@@ -38,7 +40,6 @@ ...@@ -38,7 +40,6 @@
#include "blacklist.h" #include "blacklist.h"
#include "cio_debug.h" #include "cio_debug.h"
#include "chp.h" #include "chp.h"
#include "../s390mach.h"
debug_info_t *cio_debug_msg_id; debug_info_t *cio_debug_msg_id;
debug_info_t *cio_debug_trace_id; debug_info_t *cio_debug_trace_id;
......
/*
* Channel report handling code
*
* Copyright IBM Corp. 2000,2009
* Author(s): Ingo Adlung <adlung@de.ibm.com>,
* Martin Schwidefsky <schwidefsky@de.ibm.com>,
* Cornelia Huck <cornelia.huck@de.ibm.com>,
* Heiko Carstens <heiko.carstens@de.ibm.com>,
*/
#include <linux/semaphore.h>
#include <linux/kthread.h>
#include <linux/init.h>
#include <asm/crw.h>
static struct semaphore crw_semaphore;
static crw_handler_t crw_handlers[NR_RSCS];
/**
* crw_register_handler() - register a channel report word handler
* @rsc: reporting source code to handle
* @handler: handler to be registered
*
* Returns %0 on success and a negative error value otherwise.
*/
int crw_register_handler(int rsc, crw_handler_t handler)
{
if ((rsc < 0) || (rsc >= NR_RSCS))
return -EINVAL;
if (!cmpxchg(&crw_handlers[rsc], NULL, handler))
return 0;
return -EBUSY;
}
/**
* crw_unregister_handler() - unregister a channel report word handler
* @rsc: reporting source code to handle
*/
void crw_unregister_handler(int rsc)
{
if ((rsc < 0) || (rsc >= NR_RSCS))
return;
xchg(&crw_handlers[rsc], NULL);
synchronize_sched();
}
/*
* Retrieve CRWs and call function to handle event.
*/
static int crw_collect_info(void *unused)
{
struct crw crw[2];
int ccode;
unsigned int chain;
int ignore;
repeat:
ignore = down_interruptible(&crw_semaphore);
chain = 0;
while (1) {
if (unlikely(chain > 1)) {
struct crw tmp_crw;
printk(KERN_WARNING"%s: Code does not support more "
"than two chained crws; please report to "
"linux390@de.ibm.com!\n", __func__);
ccode = stcrw(&tmp_crw);
printk(KERN_WARNING"%s: crw reports slct=%d, oflw=%d, "
"chn=%d, rsc=%X, anc=%d, erc=%X, rsid=%X\n",
__func__, tmp_crw.slct, tmp_crw.oflw,
tmp_crw.chn, tmp_crw.rsc, tmp_crw.anc,
tmp_crw.erc, tmp_crw.rsid);
printk(KERN_WARNING"%s: This was crw number %x in the "
"chain\n", __func__, chain);
if (ccode != 0)
break;
chain = tmp_crw.chn ? chain + 1 : 0;
continue;
}
ccode = stcrw(&crw[chain]);
if (ccode != 0)
break;
printk(KERN_DEBUG "crw_info : CRW reports slct=%d, oflw=%d, "
"chn=%d, rsc=%X, anc=%d, erc=%X, rsid=%X\n",
crw[chain].slct, crw[chain].oflw, crw[chain].chn,
crw[chain].rsc, crw[chain].anc, crw[chain].erc,
crw[chain].rsid);
/* Check for overflows. */
if (crw[chain].oflw) {
int i;
pr_debug("%s: crw overflow detected!\n", __func__);
for (i = 0; i < NR_RSCS; i++) {
if (crw_handlers[i])
crw_handlers[i](NULL, NULL, 1);
}
chain = 0;
continue;
}
if (crw[0].chn && !chain) {
chain++;
continue;
}
if (crw_handlers[crw[chain].rsc])
crw_handlers[crw[chain].rsc](&crw[0],
chain ? &crw[1] : NULL,
0);
/* chain is always 0 or 1 here. */
chain = crw[chain].chn ? chain + 1 : 0;
}
goto repeat;
return 0;
}
void crw_handle_channel_report(void)
{
up(&crw_semaphore);
}
/*
* Separate initcall needed for semaphore initialization since
* crw_handle_channel_report might be called before crw_machine_check_init.
*/
static int __init crw_init_semaphore(void)
{
init_MUTEX_LOCKED(&crw_semaphore);
return 0;
}
pure_initcall(crw_init_semaphore);
/*
* Machine checks for the channel subsystem must be enabled
* after the channel subsystem is initialized
*/
static int __init crw_machine_check_init(void)
{
struct task_struct *task;
task = kthread_run(crw_collect_info, NULL, "kmcheck");
if (IS_ERR(task))
return PTR_ERR(task);
ctl_set_bit(14, 28); /* enable channel report MCH */
return 0;
}
device_initcall(crw_machine_check_init);
...@@ -18,8 +18,8 @@ ...@@ -18,8 +18,8 @@
#include <linux/list.h> #include <linux/list.h>
#include <linux/reboot.h> #include <linux/reboot.h>
#include <asm/isc.h> #include <asm/isc.h>
#include <asm/crw.h>
#include "../s390mach.h"
#include "css.h" #include "css.h"
#include "cio.h" #include "cio.h"
#include "cio_debug.h" #include "cio_debug.h"
...@@ -765,7 +765,7 @@ init_channel_subsystem (void) ...@@ -765,7 +765,7 @@ init_channel_subsystem (void)
if (ret) if (ret)
goto out; goto out;
ret = s390_register_crw_handler(CRW_RSC_SCH, css_process_crw); ret = crw_register_handler(CRW_RSC_SCH, css_process_crw);
if (ret) if (ret)
goto out; goto out;
...@@ -845,7 +845,7 @@ init_channel_subsystem (void) ...@@ -845,7 +845,7 @@ init_channel_subsystem (void)
out_bus: out_bus:
bus_unregister(&css_bus_type); bus_unregister(&css_bus_type);
out: out:
s390_unregister_crw_handler(CRW_RSC_CSS); crw_unregister_handler(CRW_RSC_CSS);
chsc_free_sei_area(); chsc_free_sei_area();
kfree(slow_subchannel_set); kfree(slow_subchannel_set);
pr_alert("The CSS device driver initialization failed with " pr_alert("The CSS device driver initialization failed with "
......
/*
* drivers/s390/s390mach.h
* S/390 data definitions for machine check processing
*
* S390 version
* Copyright (C) 2000 IBM Deutschland Entwicklung GmbH, IBM Corporation
* Author(s): Ingo Adlung (adlung@de.ibm.com)
*/
#ifndef __s390mach_h
#define __s390mach_h
#include <asm/types.h>
struct mci {
__u32 sd : 1; /* 00 system damage */
__u32 pd : 1; /* 01 instruction-processing damage */
__u32 sr : 1; /* 02 system recovery */
__u32 to_be_defined_1 : 1; /* 03 */
__u32 cd : 1; /* 04 timing-facility damage */
__u32 ed : 1; /* 05 external damage */
__u32 to_be_defined_2 : 1; /* 06 */
__u32 dg : 1; /* 07 degradation */
__u32 w : 1; /* 08 warning pending */
__u32 cp : 1; /* 09 channel-report pending */
__u32 sp : 1; /* 10 service-processor damage */
__u32 ck : 1; /* 11 channel-subsystem damage */
__u32 to_be_defined_3 : 2; /* 12-13 */
__u32 b : 1; /* 14 backed up */
__u32 to_be_defined_4 : 1; /* 15 */
__u32 se : 1; /* 16 storage error uncorrected */
__u32 sc : 1; /* 17 storage error corrected */
__u32 ke : 1; /* 18 storage-key error uncorrected */
__u32 ds : 1; /* 19 storage degradation */
__u32 wp : 1; /* 20 psw mwp validity */
__u32 ms : 1; /* 21 psw mask and key validity */
__u32 pm : 1; /* 22 psw program mask and cc validity */
__u32 ia : 1; /* 23 psw instruction address validity */
__u32 fa : 1; /* 24 failing storage address validity */
__u32 to_be_defined_5 : 1; /* 25 */
__u32 ec : 1; /* 26 external damage code validity */
__u32 fp : 1; /* 27 floating point register validity */
__u32 gr : 1; /* 28 general register validity */
__u32 cr : 1; /* 29 control register validity */
__u32 to_be_defined_6 : 1; /* 30 */
__u32 st : 1; /* 31 storage logical validity */
__u32 ie : 1; /* 32 indirect storage error */
__u32 ar : 1; /* 33 access register validity */
__u32 da : 1; /* 34 delayed access exception */
__u32 to_be_defined_7 : 7; /* 35-41 */
__u32 pr : 1; /* 42 tod programmable register validity */
__u32 fc : 1; /* 43 fp control register validity */
__u32 ap : 1; /* 44 ancillary report */
__u32 to_be_defined_8 : 1; /* 45 */
__u32 ct : 1; /* 46 cpu timer validity */
__u32 cc : 1; /* 47 clock comparator validity */
__u32 to_be_defined_9 : 16; /* 47-63 */
};
/*
* Channel Report Word
*/
struct crw {
__u32 res1 : 1; /* reserved zero */
__u32 slct : 1; /* solicited */
__u32 oflw : 1; /* overflow */
__u32 chn : 1; /* chained */
__u32 rsc : 4; /* reporting source code */
__u32 anc : 1; /* ancillary report */
__u32 res2 : 1; /* reserved zero */
__u32 erc : 6; /* error-recovery code */
__u32 rsid : 16; /* reporting-source ID */
} __attribute__ ((packed));
typedef void (*crw_handler_t)(struct crw *, struct crw *, int);
extern int s390_register_crw_handler(int rsc, crw_handler_t handler);
extern void s390_unregister_crw_handler(int rsc);
#define NR_RSCS 16
#define CRW_RSC_MONITOR 0x2 /* monitoring facility */
#define CRW_RSC_SCH 0x3 /* subchannel */
#define CRW_RSC_CPATH 0x4 /* channel path */
#define CRW_RSC_CONFIG 0x9 /* configuration-alert facility */
#define CRW_RSC_CSS 0xB /* channel subsystem */
#define CRW_ERC_EVENT 0x00 /* event information pending */
#define CRW_ERC_AVAIL 0x01 /* available */
#define CRW_ERC_INIT 0x02 /* initialized */
#define CRW_ERC_TERROR 0x03 /* temporary error */
#define CRW_ERC_IPARM 0x04 /* installed parm initialized */
#define CRW_ERC_TERM 0x05 /* terminal */
#define CRW_ERC_PERRN 0x06 /* perm. error, fac. not init */
#define CRW_ERC_PERRI 0x07 /* perm. error, facility init */
#define CRW_ERC_PMOD 0x08 /* installed parameters modified */
static inline int stcrw(struct crw *pcrw )
{
int ccode;
__asm__ __volatile__(
"stcrw 0(%2)\n\t"
"ipm %0\n\t"
"srl %0,28\n\t"
: "=d" (ccode), "=m" (*pcrw)
: "a" (pcrw)
: "cc" );
return ccode;
}
#define ED_ETR_SYNC 12 /* External damage ETR sync check */
#define ED_ETR_SWITCH 13 /* External damage ETR switch to local */
#define ED_STP_SYNC 7 /* External damage STP sync check */
#define ED_STP_ISLAND 6 /* External damage STP island check */
struct pt_regs;
void s390_handle_mcck(void);
void s390_do_machine_check(struct pt_regs *regs);
#endif /* __s390mach */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment