2019-05-28 20:10:09 +03:00
// SPDX-License-Identifier: GPL-2.0-only
2016-04-07 04:43:31 +03:00
/* Copyright (c) 2016 Facebook
*/
# define _GNU_SOURCE
# include <sched.h>
2018-07-04 16:36:38 +03:00
# include <errno.h>
2016-04-07 04:43:31 +03:00
# include <stdio.h>
# include <sys/types.h>
# include <asm/unistd.h>
# include <fcntl.h>
# include <unistd.h>
# include <assert.h>
# include <sys/wait.h>
samples/bpf: replace broken overhead microbenchmark with fib_table_lookup
The test_overhead bpf program is designed to compare performance
between tracepoint and kprobe. Initially it used task_rename and
urandom_read tracepoint.
However, commit 14c174633f34 ("random: remove unused tracepoints")
removed urandom_read tracepoint, and for this reason the test_overhead
got broken.
This commit introduces new microbenchmark using fib_table_lookup.
This microbenchmark sends UDP packets to localhost in order to invoke
fib_table_lookup.
In a nutshell:
fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
addr.sin_addr.s_addr = inet_addr(DUMMY_IP);
addr.sin_port = htons(DUMMY_PORT);
for() {
sendto(fd, buf, strlen(buf), 0,
(struct sockaddr *)&addr, sizeof(addr));
}
on 4 cpus in parallel:
lookup per sec
base (no tracepoints, no kprobes) 381k
with kprobe at fib_table_lookup() 325k
with tracepoint at fib:fib_table_lookup 330k
with raw_tracepoint at fib:fib_table_lookup 365k
Fixes: 14c174633f34 ("random: remove unused tracepoints")
Signed-off-by: Daniel T. Lee <danieltimlee@gmail.com>
Link: https://lore.kernel.org/r/20230115071613.125791-6-danieltimlee@gmail.com
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
2023-01-15 10:16:08 +03:00
# include <sys/socket.h>
# include <arpa/inet.h>
2016-04-07 04:43:31 +03:00
# include <stdlib.h>
# include <signal.h>
# include <linux/bpf.h>
# include <string.h>
# include <time.h>
2018-05-15 08:35:02 +03:00
# include <bpf/bpf.h>
2020-11-24 12:03:08 +03:00
# include <bpf/libbpf.h>
2016-04-07 04:43:31 +03:00
# define MAX_CNT 1000000
samples/bpf: replace broken overhead microbenchmark with fib_table_lookup
The test_overhead bpf program is designed to compare performance
between tracepoint and kprobe. Initially it used task_rename and
urandom_read tracepoint.
However, commit 14c174633f34 ("random: remove unused tracepoints")
removed urandom_read tracepoint, and for this reason the test_overhead
got broken.
This commit introduces new microbenchmark using fib_table_lookup.
This microbenchmark sends UDP packets to localhost in order to invoke
fib_table_lookup.
In a nutshell:
fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
addr.sin_addr.s_addr = inet_addr(DUMMY_IP);
addr.sin_port = htons(DUMMY_PORT);
for() {
sendto(fd, buf, strlen(buf), 0,
(struct sockaddr *)&addr, sizeof(addr));
}
on 4 cpus in parallel:
lookup per sec
base (no tracepoints, no kprobes) 381k
with kprobe at fib_table_lookup() 325k
with tracepoint at fib:fib_table_lookup 330k
with raw_tracepoint at fib:fib_table_lookup 365k
Fixes: 14c174633f34 ("random: remove unused tracepoints")
Signed-off-by: Daniel T. Lee <danieltimlee@gmail.com>
Link: https://lore.kernel.org/r/20230115071613.125791-6-danieltimlee@gmail.com
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
2023-01-15 10:16:08 +03:00
# define DUMMY_IP "127.0.0.1"
# define DUMMY_PORT 80
2016-04-07 04:43:31 +03:00
2020-11-24 12:03:08 +03:00
static struct bpf_link * links [ 2 ] ;
static struct bpf_object * obj ;
static int cnt ;
2016-04-07 04:43:31 +03:00
static __u64 time_get_ns ( void )
{
struct timespec ts ;
clock_gettime ( CLOCK_MONOTONIC , & ts ) ;
return ts . tv_sec * 1000000000ull + ts . tv_nsec ;
}
static void test_task_rename ( int cpu )
{
char buf [ ] = " test \n " ;
samples/bpf: replace broken overhead microbenchmark with fib_table_lookup
The test_overhead bpf program is designed to compare performance
between tracepoint and kprobe. Initially it used task_rename and
urandom_read tracepoint.
However, commit 14c174633f34 ("random: remove unused tracepoints")
removed urandom_read tracepoint, and for this reason the test_overhead
got broken.
This commit introduces new microbenchmark using fib_table_lookup.
This microbenchmark sends UDP packets to localhost in order to invoke
fib_table_lookup.
In a nutshell:
fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
addr.sin_addr.s_addr = inet_addr(DUMMY_IP);
addr.sin_port = htons(DUMMY_PORT);
for() {
sendto(fd, buf, strlen(buf), 0,
(struct sockaddr *)&addr, sizeof(addr));
}
on 4 cpus in parallel:
lookup per sec
base (no tracepoints, no kprobes) 381k
with kprobe at fib_table_lookup() 325k
with tracepoint at fib:fib_table_lookup 330k
with raw_tracepoint at fib:fib_table_lookup 365k
Fixes: 14c174633f34 ("random: remove unused tracepoints")
Signed-off-by: Daniel T. Lee <danieltimlee@gmail.com>
Link: https://lore.kernel.org/r/20230115071613.125791-6-danieltimlee@gmail.com
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
2023-01-15 10:16:08 +03:00
__u64 start_time ;
2016-04-07 04:43:31 +03:00
int i , fd ;
fd = open ( " /proc/self/comm " , O_WRONLY | O_TRUNC ) ;
if ( fd < 0 ) {
printf ( " couldn't open /proc \n " ) ;
exit ( 1 ) ;
}
start_time = time_get_ns ( ) ;
2018-07-04 16:36:38 +03:00
for ( i = 0 ; i < MAX_CNT ; i + + ) {
if ( write ( fd , buf , sizeof ( buf ) ) < 0 ) {
printf ( " task rename failed: %s \n " , strerror ( errno ) ) ;
close ( fd ) ;
return ;
}
}
2016-04-07 04:43:31 +03:00
printf ( " task_rename:%d: %lld events per sec \n " ,
cpu , MAX_CNT * 1000000000ll / ( time_get_ns ( ) - start_time ) ) ;
close ( fd ) ;
}
samples/bpf: replace broken overhead microbenchmark with fib_table_lookup
The test_overhead bpf program is designed to compare performance
between tracepoint and kprobe. Initially it used task_rename and
urandom_read tracepoint.
However, commit 14c174633f34 ("random: remove unused tracepoints")
removed urandom_read tracepoint, and for this reason the test_overhead
got broken.
This commit introduces new microbenchmark using fib_table_lookup.
This microbenchmark sends UDP packets to localhost in order to invoke
fib_table_lookup.
In a nutshell:
fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
addr.sin_addr.s_addr = inet_addr(DUMMY_IP);
addr.sin_port = htons(DUMMY_PORT);
for() {
sendto(fd, buf, strlen(buf), 0,
(struct sockaddr *)&addr, sizeof(addr));
}
on 4 cpus in parallel:
lookup per sec
base (no tracepoints, no kprobes) 381k
with kprobe at fib_table_lookup() 325k
with tracepoint at fib:fib_table_lookup 330k
with raw_tracepoint at fib:fib_table_lookup 365k
Fixes: 14c174633f34 ("random: remove unused tracepoints")
Signed-off-by: Daniel T. Lee <danieltimlee@gmail.com>
Link: https://lore.kernel.org/r/20230115071613.125791-6-danieltimlee@gmail.com
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
2023-01-15 10:16:08 +03:00
static void test_fib_table_lookup ( int cpu )
2016-04-07 04:43:31 +03:00
{
samples/bpf: replace broken overhead microbenchmark with fib_table_lookup
The test_overhead bpf program is designed to compare performance
between tracepoint and kprobe. Initially it used task_rename and
urandom_read tracepoint.
However, commit 14c174633f34 ("random: remove unused tracepoints")
removed urandom_read tracepoint, and for this reason the test_overhead
got broken.
This commit introduces new microbenchmark using fib_table_lookup.
This microbenchmark sends UDP packets to localhost in order to invoke
fib_table_lookup.
In a nutshell:
fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
addr.sin_addr.s_addr = inet_addr(DUMMY_IP);
addr.sin_port = htons(DUMMY_PORT);
for() {
sendto(fd, buf, strlen(buf), 0,
(struct sockaddr *)&addr, sizeof(addr));
}
on 4 cpus in parallel:
lookup per sec
base (no tracepoints, no kprobes) 381k
with kprobe at fib_table_lookup() 325k
with tracepoint at fib:fib_table_lookup 330k
with raw_tracepoint at fib:fib_table_lookup 365k
Fixes: 14c174633f34 ("random: remove unused tracepoints")
Signed-off-by: Daniel T. Lee <danieltimlee@gmail.com>
Link: https://lore.kernel.org/r/20230115071613.125791-6-danieltimlee@gmail.com
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
2023-01-15 10:16:08 +03:00
struct sockaddr_in addr ;
char buf [ ] = " test \n " ;
2016-04-07 04:43:31 +03:00
__u64 start_time ;
int i , fd ;
samples/bpf: replace broken overhead microbenchmark with fib_table_lookup
The test_overhead bpf program is designed to compare performance
between tracepoint and kprobe. Initially it used task_rename and
urandom_read tracepoint.
However, commit 14c174633f34 ("random: remove unused tracepoints")
removed urandom_read tracepoint, and for this reason the test_overhead
got broken.
This commit introduces new microbenchmark using fib_table_lookup.
This microbenchmark sends UDP packets to localhost in order to invoke
fib_table_lookup.
In a nutshell:
fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
addr.sin_addr.s_addr = inet_addr(DUMMY_IP);
addr.sin_port = htons(DUMMY_PORT);
for() {
sendto(fd, buf, strlen(buf), 0,
(struct sockaddr *)&addr, sizeof(addr));
}
on 4 cpus in parallel:
lookup per sec
base (no tracepoints, no kprobes) 381k
with kprobe at fib_table_lookup() 325k
with tracepoint at fib:fib_table_lookup 330k
with raw_tracepoint at fib:fib_table_lookup 365k
Fixes: 14c174633f34 ("random: remove unused tracepoints")
Signed-off-by: Daniel T. Lee <danieltimlee@gmail.com>
Link: https://lore.kernel.org/r/20230115071613.125791-6-danieltimlee@gmail.com
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
2023-01-15 10:16:08 +03:00
fd = socket ( AF_INET , SOCK_DGRAM , IPPROTO_UDP ) ;
2016-04-07 04:43:31 +03:00
if ( fd < 0 ) {
samples/bpf: replace broken overhead microbenchmark with fib_table_lookup
The test_overhead bpf program is designed to compare performance
between tracepoint and kprobe. Initially it used task_rename and
urandom_read tracepoint.
However, commit 14c174633f34 ("random: remove unused tracepoints")
removed urandom_read tracepoint, and for this reason the test_overhead
got broken.
This commit introduces new microbenchmark using fib_table_lookup.
This microbenchmark sends UDP packets to localhost in order to invoke
fib_table_lookup.
In a nutshell:
fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
addr.sin_addr.s_addr = inet_addr(DUMMY_IP);
addr.sin_port = htons(DUMMY_PORT);
for() {
sendto(fd, buf, strlen(buf), 0,
(struct sockaddr *)&addr, sizeof(addr));
}
on 4 cpus in parallel:
lookup per sec
base (no tracepoints, no kprobes) 381k
with kprobe at fib_table_lookup() 325k
with tracepoint at fib:fib_table_lookup 330k
with raw_tracepoint at fib:fib_table_lookup 365k
Fixes: 14c174633f34 ("random: remove unused tracepoints")
Signed-off-by: Daniel T. Lee <danieltimlee@gmail.com>
Link: https://lore.kernel.org/r/20230115071613.125791-6-danieltimlee@gmail.com
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
2023-01-15 10:16:08 +03:00
printf ( " couldn't open socket \n " ) ;
2016-04-07 04:43:31 +03:00
exit ( 1 ) ;
}
samples/bpf: replace broken overhead microbenchmark with fib_table_lookup
The test_overhead bpf program is designed to compare performance
between tracepoint and kprobe. Initially it used task_rename and
urandom_read tracepoint.
However, commit 14c174633f34 ("random: remove unused tracepoints")
removed urandom_read tracepoint, and for this reason the test_overhead
got broken.
This commit introduces new microbenchmark using fib_table_lookup.
This microbenchmark sends UDP packets to localhost in order to invoke
fib_table_lookup.
In a nutshell:
fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
addr.sin_addr.s_addr = inet_addr(DUMMY_IP);
addr.sin_port = htons(DUMMY_PORT);
for() {
sendto(fd, buf, strlen(buf), 0,
(struct sockaddr *)&addr, sizeof(addr));
}
on 4 cpus in parallel:
lookup per sec
base (no tracepoints, no kprobes) 381k
with kprobe at fib_table_lookup() 325k
with tracepoint at fib:fib_table_lookup 330k
with raw_tracepoint at fib:fib_table_lookup 365k
Fixes: 14c174633f34 ("random: remove unused tracepoints")
Signed-off-by: Daniel T. Lee <danieltimlee@gmail.com>
Link: https://lore.kernel.org/r/20230115071613.125791-6-danieltimlee@gmail.com
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
2023-01-15 10:16:08 +03:00
memset ( ( char * ) & addr , 0 , sizeof ( addr ) ) ;
addr . sin_addr . s_addr = inet_addr ( DUMMY_IP ) ;
addr . sin_port = htons ( DUMMY_PORT ) ;
addr . sin_family = AF_INET ;
2016-04-07 04:43:31 +03:00
start_time = time_get_ns ( ) ;
2018-07-04 16:36:38 +03:00
for ( i = 0 ; i < MAX_CNT ; i + + ) {
samples/bpf: replace broken overhead microbenchmark with fib_table_lookup
The test_overhead bpf program is designed to compare performance
between tracepoint and kprobe. Initially it used task_rename and
urandom_read tracepoint.
However, commit 14c174633f34 ("random: remove unused tracepoints")
removed urandom_read tracepoint, and for this reason the test_overhead
got broken.
This commit introduces new microbenchmark using fib_table_lookup.
This microbenchmark sends UDP packets to localhost in order to invoke
fib_table_lookup.
In a nutshell:
fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
addr.sin_addr.s_addr = inet_addr(DUMMY_IP);
addr.sin_port = htons(DUMMY_PORT);
for() {
sendto(fd, buf, strlen(buf), 0,
(struct sockaddr *)&addr, sizeof(addr));
}
on 4 cpus in parallel:
lookup per sec
base (no tracepoints, no kprobes) 381k
with kprobe at fib_table_lookup() 325k
with tracepoint at fib:fib_table_lookup 330k
with raw_tracepoint at fib:fib_table_lookup 365k
Fixes: 14c174633f34 ("random: remove unused tracepoints")
Signed-off-by: Daniel T. Lee <danieltimlee@gmail.com>
Link: https://lore.kernel.org/r/20230115071613.125791-6-danieltimlee@gmail.com
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
2023-01-15 10:16:08 +03:00
if ( sendto ( fd , buf , strlen ( buf ) , 0 ,
( struct sockaddr * ) & addr , sizeof ( addr ) ) < 0 ) {
printf ( " failed to start ping: %s \n " , strerror ( errno ) ) ;
2018-07-04 16:36:38 +03:00
close ( fd ) ;
return ;
}
}
samples/bpf: replace broken overhead microbenchmark with fib_table_lookup
The test_overhead bpf program is designed to compare performance
between tracepoint and kprobe. Initially it used task_rename and
urandom_read tracepoint.
However, commit 14c174633f34 ("random: remove unused tracepoints")
removed urandom_read tracepoint, and for this reason the test_overhead
got broken.
This commit introduces new microbenchmark using fib_table_lookup.
This microbenchmark sends UDP packets to localhost in order to invoke
fib_table_lookup.
In a nutshell:
fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
addr.sin_addr.s_addr = inet_addr(DUMMY_IP);
addr.sin_port = htons(DUMMY_PORT);
for() {
sendto(fd, buf, strlen(buf), 0,
(struct sockaddr *)&addr, sizeof(addr));
}
on 4 cpus in parallel:
lookup per sec
base (no tracepoints, no kprobes) 381k
with kprobe at fib_table_lookup() 325k
with tracepoint at fib:fib_table_lookup 330k
with raw_tracepoint at fib:fib_table_lookup 365k
Fixes: 14c174633f34 ("random: remove unused tracepoints")
Signed-off-by: Daniel T. Lee <danieltimlee@gmail.com>
Link: https://lore.kernel.org/r/20230115071613.125791-6-danieltimlee@gmail.com
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
2023-01-15 10:16:08 +03:00
printf ( " fib_table_lookup:%d: %lld events per sec \n " ,
2016-04-07 04:43:31 +03:00
cpu , MAX_CNT * 1000000000ll / ( time_get_ns ( ) - start_time ) ) ;
close ( fd ) ;
}
static void loop ( int cpu , int flags )
{
cpu_set_t cpuset ;
CPU_ZERO ( & cpuset ) ;
CPU_SET ( cpu , & cpuset ) ;
sched_setaffinity ( 0 , sizeof ( cpuset ) , & cpuset ) ;
if ( flags & 1 )
test_task_rename ( cpu ) ;
if ( flags & 2 )
samples/bpf: replace broken overhead microbenchmark with fib_table_lookup
The test_overhead bpf program is designed to compare performance
between tracepoint and kprobe. Initially it used task_rename and
urandom_read tracepoint.
However, commit 14c174633f34 ("random: remove unused tracepoints")
removed urandom_read tracepoint, and for this reason the test_overhead
got broken.
This commit introduces new microbenchmark using fib_table_lookup.
This microbenchmark sends UDP packets to localhost in order to invoke
fib_table_lookup.
In a nutshell:
fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
addr.sin_addr.s_addr = inet_addr(DUMMY_IP);
addr.sin_port = htons(DUMMY_PORT);
for() {
sendto(fd, buf, strlen(buf), 0,
(struct sockaddr *)&addr, sizeof(addr));
}
on 4 cpus in parallel:
lookup per sec
base (no tracepoints, no kprobes) 381k
with kprobe at fib_table_lookup() 325k
with tracepoint at fib:fib_table_lookup 330k
with raw_tracepoint at fib:fib_table_lookup 365k
Fixes: 14c174633f34 ("random: remove unused tracepoints")
Signed-off-by: Daniel T. Lee <danieltimlee@gmail.com>
Link: https://lore.kernel.org/r/20230115071613.125791-6-danieltimlee@gmail.com
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
2023-01-15 10:16:08 +03:00
test_fib_table_lookup ( cpu ) ;
2016-04-07 04:43:31 +03:00
}
static void run_perf_test ( int tasks , int flags )
{
pid_t pid [ tasks ] ;
int i ;
for ( i = 0 ; i < tasks ; i + + ) {
pid [ i ] = fork ( ) ;
if ( pid [ i ] = = 0 ) {
loop ( i , flags ) ;
exit ( 0 ) ;
} else if ( pid [ i ] = = - 1 ) {
printf ( " couldn't spawn #%d process \n " , i ) ;
exit ( 1 ) ;
}
}
for ( i = 0 ; i < tasks ; i + + ) {
int status ;
assert ( waitpid ( pid [ i ] , & status , 0 ) = = pid [ i ] ) ;
assert ( status = = 0 ) ;
}
}
2020-11-24 12:03:08 +03:00
static int load_progs ( char * filename )
{
struct bpf_program * prog ;
int err = 0 ;
obj = bpf_object__open_file ( filename , NULL ) ;
err = libbpf_get_error ( obj ) ;
if ( err < 0 ) {
fprintf ( stderr , " ERROR: opening BPF object file failed \n " ) ;
return err ;
}
/* load BPF program */
err = bpf_object__load ( obj ) ;
if ( err < 0 ) {
fprintf ( stderr , " ERROR: loading BPF object file failed \n " ) ;
return err ;
}
bpf_object__for_each_program ( prog , obj ) {
links [ cnt ] = bpf_program__attach ( prog ) ;
err = libbpf_get_error ( links [ cnt ] ) ;
if ( err < 0 ) {
fprintf ( stderr , " ERROR: bpf_program__attach failed \n " ) ;
links [ cnt ] = NULL ;
return err ;
}
cnt + + ;
}
return err ;
}
2016-04-07 04:43:31 +03:00
static void unload_progs ( void )
{
2020-11-24 12:03:08 +03:00
while ( cnt )
bpf_link__destroy ( links [ - - cnt ] ) ;
bpf_object__close ( obj ) ;
2016-04-07 04:43:31 +03:00
}
int main ( int argc , char * * argv )
{
2020-11-24 12:03:08 +03:00
int num_cpu = sysconf ( _SC_NPROCESSORS_ONLN ) ;
2016-04-07 04:43:31 +03:00
int test_flags = ~ 0 ;
2020-11-24 12:03:08 +03:00
char filename [ 256 ] ;
int err = 0 ;
2016-04-07 04:43:31 +03:00
if ( argc > 1 )
test_flags = atoi ( argv [ 1 ] ) ? : test_flags ;
if ( argc > 2 )
num_cpu = atoi ( argv [ 2 ] ) ? : num_cpu ;
if ( test_flags & 0x3 ) {
printf ( " BASE \n " ) ;
run_perf_test ( num_cpu , test_flags ) ;
}
if ( test_flags & 0xC ) {
snprintf ( filename , sizeof ( filename ) ,
2023-01-15 10:16:13 +03:00
" %s_kprobe.bpf.o " , argv [ 0 ] ) ;
2020-11-24 12:03:08 +03:00
2016-04-07 04:43:31 +03:00
printf ( " w/KPROBE \n " ) ;
2020-11-24 12:03:08 +03:00
err = load_progs ( filename ) ;
if ( ! err )
run_perf_test ( num_cpu , test_flags > > 2 ) ;
2016-04-07 04:43:31 +03:00
unload_progs ( ) ;
}
if ( test_flags & 0x30 ) {
snprintf ( filename , sizeof ( filename ) ,
2023-01-15 10:16:13 +03:00
" %s_tp.bpf.o " , argv [ 0 ] ) ;
2016-04-07 04:43:31 +03:00
printf ( " w/TRACEPOINT \n " ) ;
2020-11-24 12:03:08 +03:00
err = load_progs ( filename ) ;
if ( ! err )
run_perf_test ( num_cpu , test_flags > > 4 ) ;
2016-04-07 04:43:31 +03:00
unload_progs ( ) ;
}
2018-03-28 22:05:39 +03:00
if ( test_flags & 0xC0 ) {
snprintf ( filename , sizeof ( filename ) ,
2023-01-15 10:16:13 +03:00
" %s_raw_tp.bpf.o " , argv [ 0 ] ) ;
2018-03-28 22:05:39 +03:00
printf ( " w/RAW_TRACEPOINT \n " ) ;
2020-11-24 12:03:08 +03:00
err = load_progs ( filename ) ;
if ( ! err )
run_perf_test ( num_cpu , test_flags > > 6 ) ;
2018-03-28 22:05:39 +03:00
unload_progs ( ) ;
}
2020-11-24 12:03:08 +03:00
return err ;
2016-04-07 04:43:31 +03:00
}