Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
openeuler
Kernel
提交
c7c6575f
K
Kernel
项目概览
openeuler
/
Kernel
1 年多 前同步成功
通知
8
Star
0
Fork
0
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
0
列表
看板
标记
里程碑
合并请求
0
DevOps
流水线
流水线任务
计划
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
K
Kernel
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
0
Issue
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
Pages
DevOps
DevOps
流水线
流水线任务
计划
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
流水线任务
提交
Issue看板
提交
c7c6575f
编写于
12月 12, 2011
作者:
D
David S. Miller
浏览文件
操作
浏览文件
下载
差异文件
Merge branch 'batman-adv/next' of
git://git.open-mesh.org/linux-merge
上级
0850f0f5
b5a1eeef
变更
8
隐藏空白更改
内联
并排
Showing
8 changed file
with
216 addition
and
168 deletion
+216
-168
Documentation/networking/batman-adv.txt
Documentation/networking/batman-adv.txt
+4
-3
net/batman-adv/gateway_client.c
net/batman-adv/gateway_client.c
+1
-1
net/batman-adv/icmp_socket.c
net/batman-adv/icmp_socket.c
+4
-10
net/batman-adv/routing.c
net/batman-adv/routing.c
+1
-2
net/batman-adv/soft-interface.c
net/batman-adv/soft-interface.c
+1
-1
net/batman-adv/translation-table.c
net/batman-adv/translation-table.c
+195
-141
net/batman-adv/types.h
net/batman-adv/types.h
+7
-7
net/batman-adv/vis.c
net/batman-adv/vis.c
+3
-3
未找到文件。
Documentation/networking/batman-adv.txt
浏览文件 @
c7c6575f
...
...
@@ -200,15 +200,16 @@ abled during run time. Following log_levels are defined:
0 - All debug output disabled
1 - Enable messages related to routing / flooding / broadcasting
2 - Enable route or tt entry added / changed / deleted
3 - Enable all messages
2 - Enable messages related to route added / changed / deleted
4 - Enable messages related to translation table operations
7 - Enable all messages
The debug output can be changed at runtime using the file
/sys/class/net/bat0/mesh/log_level. e.g.
# echo 2 > /sys/class/net/bat0/mesh/log_level
will enable debug messages for when routes
or TTs
change.
will enable debug messages for when routes change.
BATCTL
...
...
net/batman-adv/gateway_client.c
浏览文件 @
c7c6575f
...
...
@@ -695,7 +695,7 @@ bool gw_out_of_range(struct bat_priv *bat_priv,
}
neigh_old
=
find_router
(
bat_priv
,
orig_dst_node
,
NULL
);
if
(
!
!
neigh_old
)
if
(
!
neigh_old
)
goto
out
;
if
(
curr_tq_avg
-
neigh_old
->
tq_avg
>
GW_THRESHOLD
)
...
...
net/batman-adv/icmp_socket.c
浏览文件 @
c7c6575f
...
...
@@ -136,10 +136,9 @@ static ssize_t bat_socket_read(struct file *file, char __user *buf,
spin_unlock_bh
(
&
socket_client
->
lock
);
error
=
__copy_to_user
(
buf
,
&
socket_packet
->
icmp_packet
,
socket_packet
->
icmp
_len
);
packet_len
=
min
(
count
,
socket_packet
->
icmp_len
);
error
=
copy_to_user
(
buf
,
&
socket_packet
->
icmp_packet
,
packet
_len
);
packet_len
=
socket_packet
->
icmp_len
;
kfree
(
socket_packet
);
if
(
error
)
...
...
@@ -187,12 +186,7 @@ static ssize_t bat_socket_write(struct file *file, const char __user *buff,
skb_reserve
(
skb
,
sizeof
(
struct
ethhdr
));
icmp_packet
=
(
struct
icmp_packet_rr
*
)
skb_put
(
skb
,
packet_len
);
if
(
!
access_ok
(
VERIFY_READ
,
buff
,
packet_len
))
{
len
=
-
EFAULT
;
goto
free_skb
;
}
if
(
__copy_from_user
(
icmp_packet
,
buff
,
packet_len
))
{
if
(
copy_from_user
(
icmp_packet
,
buff
,
packet_len
))
{
len
=
-
EFAULT
;
goto
free_skb
;
}
...
...
@@ -217,7 +211,7 @@ static ssize_t bat_socket_write(struct file *file, const char __user *buff,
if
(
icmp_packet
->
version
!=
COMPAT_VERSION
)
{
icmp_packet
->
msg_type
=
PARAMETER_PROBLEM
;
icmp_packet
->
ttl
=
COMPAT_VERSION
;
icmp_packet
->
version
=
COMPAT_VERSION
;
bat_socket_add_packet
(
socket_client
,
icmp_packet
,
packet_len
);
goto
free_skb
;
}
...
...
net/batman-adv/routing.c
浏览文件 @
c7c6575f
...
...
@@ -627,8 +627,7 @@ int recv_tt_query(struct sk_buff *skb, struct hard_iface *recv_if)
/* Ensure we have all the claimed data */
if
(
unlikely
(
skb_headlen
(
skb
)
<
sizeof
(
struct
tt_query_packet
)
+
tt_len
))
sizeof
(
struct
tt_query_packet
)
+
tt_len
))
goto
out
;
handle_tt_response
(
bat_priv
,
tt_query
);
...
...
net/batman-adv/soft-interface.c
浏览文件 @
c7c6575f
...
...
@@ -874,7 +874,7 @@ struct net_device *softif_create(const char *name)
unreg_sysfs:
sysfs_del_meshif
(
soft_iface
);
unreg_soft_iface:
unregister_netdev
(
soft_iface
);
unregister_netdev
ice
(
soft_iface
);
return
NULL
;
free_soft_iface:
...
...
net/batman-adv/translation-table.c
浏览文件 @
c7c6575f
...
...
@@ -36,18 +36,9 @@ static void _tt_global_del(struct bat_priv *bat_priv,
static
void
tt_purge
(
struct
work_struct
*
work
);
/* returns 1 if they are the same mac addr */
static
int
compare_
l
tt
(
const
struct
hlist_node
*
node
,
const
void
*
data2
)
static
int
compare_tt
(
const
struct
hlist_node
*
node
,
const
void
*
data2
)
{
const
void
*
data1
=
container_of
(
node
,
struct
tt_local_entry
,
hash_entry
);
return
(
memcmp
(
data1
,
data2
,
ETH_ALEN
)
==
0
?
1
:
0
);
}
/* returns 1 if they are the same mac addr */
static
int
compare_gtt
(
const
struct
hlist_node
*
node
,
const
void
*
data2
)
{
const
void
*
data1
=
container_of
(
node
,
struct
tt_global_entry
,
const
void
*
data1
=
container_of
(
node
,
struct
tt_common_entry
,
hash_entry
);
return
(
memcmp
(
data1
,
data2
,
ETH_ALEN
)
==
0
?
1
:
0
);
...
...
@@ -60,13 +51,12 @@ static void tt_start_timer(struct bat_priv *bat_priv)
msecs_to_jiffies
(
5000
));
}
static
struct
tt_
local_entry
*
tt_local_hash_find
(
struct
bat_priv
*
bat_priv
,
const
void
*
data
)
static
struct
tt_
common_entry
*
tt_hash_find
(
struct
hashtable_t
*
hash
,
const
void
*
data
)
{
struct
hashtable_t
*
hash
=
bat_priv
->
tt_local_hash
;
struct
hlist_head
*
head
;
struct
hlist_node
*
node
;
struct
tt_
local_entry
*
tt_local_entry
,
*
tt_local
_entry_tmp
=
NULL
;
struct
tt_
common_entry
*
tt_common_entry
,
*
tt_common
_entry_tmp
=
NULL
;
uint32_t
index
;
if
(
!
hash
)
...
...
@@ -76,51 +66,46 @@ static struct tt_local_entry *tt_local_hash_find(struct bat_priv *bat_priv,
head
=
&
hash
->
table
[
index
];
rcu_read_lock
();
hlist_for_each_entry_rcu
(
tt_
local
_entry
,
node
,
head
,
hash_entry
)
{
if
(
!
compare_eth
(
tt_
local
_entry
,
data
))
hlist_for_each_entry_rcu
(
tt_
common
_entry
,
node
,
head
,
hash_entry
)
{
if
(
!
compare_eth
(
tt_
common
_entry
,
data
))
continue
;
if
(
!
atomic_inc_not_zero
(
&
tt_
local
_entry
->
refcount
))
if
(
!
atomic_inc_not_zero
(
&
tt_
common
_entry
->
refcount
))
continue
;
tt_
local_entry_tmp
=
tt_local
_entry
;
tt_
common_entry_tmp
=
tt_common
_entry
;
break
;
}
rcu_read_unlock
();
return
tt_
local
_entry_tmp
;
return
tt_
common
_entry_tmp
;
}
static
struct
tt_
global_entry
*
tt_glob
al_hash_find
(
struct
bat_priv
*
bat_priv
,
const
void
*
data
)
static
struct
tt_
local_entry
*
tt_loc
al_hash_find
(
struct
bat_priv
*
bat_priv
,
const
void
*
data
)
{
struct
hashtable_t
*
hash
=
bat_priv
->
tt_global_hash
;
struct
hlist_head
*
head
;
struct
hlist_node
*
node
;
struct
tt_global_entry
*
tt_global_entry
;
struct
tt_global_entry
*
tt_global_entry_tmp
=
NULL
;
uint32_t
index
;
if
(
!
hash
)
return
NULL
;
index
=
choose_orig
(
data
,
hash
->
size
);
head
=
&
hash
->
table
[
index
];
struct
tt_common_entry
*
tt_common_entry
;
struct
tt_local_entry
*
tt_local_entry
=
NULL
;
rcu_read_lock
();
hlist_for_each_entry_rcu
(
tt_global_entry
,
node
,
head
,
hash_entry
)
{
if
(
!
compare_eth
(
tt_global_entry
,
data
))
continue
;
tt_common_entry
=
tt_hash_find
(
bat_priv
->
tt_local_hash
,
data
);
if
(
tt_common_entry
)
tt_local_entry
=
container_of
(
tt_common_entry
,
struct
tt_local_entry
,
common
);
return
tt_local_entry
;
}
if
(
!
atomic_inc_not_zero
(
&
tt_global_entry
->
refcount
))
continue
;
static
struct
tt_global_entry
*
tt_global_hash_find
(
struct
bat_priv
*
bat_priv
,
const
void
*
data
)
{
struct
tt_common_entry
*
tt_common_entry
;
struct
tt_global_entry
*
tt_global_entry
=
NULL
;
tt_global_entry_tmp
=
tt_global_entry
;
break
;
}
rcu_read_unlock
();
tt_common_entry
=
tt_hash_find
(
bat_priv
->
tt_global_hash
,
data
);
if
(
tt_common_entry
)
tt_global_entry
=
container_of
(
tt_common_entry
,
struct
tt_global_entry
,
common
);
return
tt_global_entry
;
return
tt_global_entry_tmp
;
}
static
bool
is_out_of_time
(
unsigned
long
starting_time
,
unsigned
long
timeout
)
...
...
@@ -133,15 +118,18 @@ static bool is_out_of_time(unsigned long starting_time, unsigned long timeout)
static
void
tt_local_entry_free_ref
(
struct
tt_local_entry
*
tt_local_entry
)
{
if
(
atomic_dec_and_test
(
&
tt_local_entry
->
refcount
))
kfree_rcu
(
tt_local_entry
,
rcu
);
if
(
atomic_dec_and_test
(
&
tt_local_entry
->
common
.
refcount
))
kfree_rcu
(
tt_local_entry
,
common
.
rcu
);
}
static
void
tt_global_entry_free_rcu
(
struct
rcu_head
*
rcu
)
{
struct
tt_common_entry
*
tt_common_entry
;
struct
tt_global_entry
*
tt_global_entry
;
tt_global_entry
=
container_of
(
rcu
,
struct
tt_global_entry
,
rcu
);
tt_common_entry
=
container_of
(
rcu
,
struct
tt_common_entry
,
rcu
);
tt_global_entry
=
container_of
(
tt_common_entry
,
struct
tt_global_entry
,
common
);
if
(
tt_global_entry
->
orig_node
)
orig_node_free_ref
(
tt_global_entry
->
orig_node
);
...
...
@@ -151,8 +139,9 @@ static void tt_global_entry_free_rcu(struct rcu_head *rcu)
static
void
tt_global_entry_free_ref
(
struct
tt_global_entry
*
tt_global_entry
)
{
if
(
atomic_dec_and_test
(
&
tt_global_entry
->
refcount
))
call_rcu
(
&
tt_global_entry
->
rcu
,
tt_global_entry_free_rcu
);
if
(
atomic_dec_and_test
(
&
tt_global_entry
->
common
.
refcount
))
call_rcu
(
&
tt_global_entry
->
common
.
rcu
,
tt_global_entry_free_rcu
);
}
static
void
tt_local_event
(
struct
bat_priv
*
bat_priv
,
const
uint8_t
*
addr
,
...
...
@@ -201,6 +190,7 @@ void tt_local_add(struct net_device *soft_iface, const uint8_t *addr,
struct
bat_priv
*
bat_priv
=
netdev_priv
(
soft_iface
);
struct
tt_local_entry
*
tt_local_entry
=
NULL
;
struct
tt_global_entry
*
tt_global_entry
=
NULL
;
int
hash_added
;
tt_local_entry
=
tt_local_hash_find
(
bat_priv
,
addr
);
...
...
@@ -217,26 +207,33 @@ void tt_local_add(struct net_device *soft_iface, const uint8_t *addr,
"Creating new local tt entry: %pM (ttvn: %d)
\n
"
,
addr
,
(
uint8_t
)
atomic_read
(
&
bat_priv
->
ttvn
));
memcpy
(
tt_local_entry
->
addr
,
addr
,
ETH_ALEN
);
tt_local_entry
->
last_seen
=
jiffies
;
tt_local_entry
->
flags
=
NO_FLAGS
;
memcpy
(
tt_local_entry
->
common
.
addr
,
addr
,
ETH_ALEN
);
tt_local_entry
->
common
.
flags
=
NO_FLAGS
;
if
(
is_wifi_iface
(
ifindex
))
tt_local_entry
->
flags
|=
TT_CLIENT_WIFI
;
atomic_set
(
&
tt_local_entry
->
refcount
,
2
);
tt_local_entry
->
common
.
flags
|=
TT_CLIENT_WIFI
;
atomic_set
(
&
tt_local_entry
->
common
.
refcount
,
2
);
tt_local_entry
->
last_seen
=
jiffies
;
/* the batman interface mac address should never be purged */
if
(
compare_eth
(
addr
,
soft_iface
->
dev_addr
))
tt_local_entry
->
flags
|=
TT_CLIENT_NOPURGE
;
tt_local_entry
->
common
.
flags
|=
TT_CLIENT_NOPURGE
;
tt_local_event
(
bat_priv
,
addr
,
tt_local_entry
->
flags
);
hash_added
=
hash_add
(
bat_priv
->
tt_local_hash
,
compare_tt
,
choose_orig
,
&
tt_local_entry
->
common
,
&
tt_local_entry
->
common
.
hash_entry
);
if
(
unlikely
(
hash_added
!=
0
))
{
/* remove the reference for the hash */
tt_local_entry_free_ref
(
tt_local_entry
);
goto
out
;
}
tt_local_event
(
bat_priv
,
addr
,
tt_local_entry
->
common
.
flags
);
/* The local entry has to be marked as NEW to avoid to send it in
* a full table response going out before the next ttvn increment
* (consistency check) */
tt_local_entry
->
flags
|=
TT_CLIENT_NEW
;
hash_add
(
bat_priv
->
tt_local_hash
,
compare_ltt
,
choose_orig
,
tt_local_entry
,
&
tt_local_entry
->
hash_entry
);
tt_local_entry
->
common
.
flags
|=
TT_CLIENT_NEW
;
/* remove address from global hash if present */
tt_global_entry
=
tt_global_hash_find
(
bat_priv
,
addr
);
...
...
@@ -247,8 +244,8 @@ void tt_local_add(struct net_device *soft_iface, const uint8_t *addr,
tt_global_entry
->
orig_node
->
tt_poss_change
=
true
;
/* The global entry has to be marked as PENDING and has to be
* kept for consistency purpose */
tt_global_entry
->
flags
|=
TT_CLIENT_PENDING
;
send_roam_adv
(
bat_priv
,
tt_global_entry
->
addr
,
tt_global_entry
->
common
.
flags
|=
TT_CLIENT_PENDING
;
send_roam_adv
(
bat_priv
,
tt_global_entry
->
common
.
addr
,
tt_global_entry
->
orig_node
);
}
out:
...
...
@@ -310,7 +307,7 @@ int tt_local_seq_print_text(struct seq_file *seq, void *offset)
struct
net_device
*
net_dev
=
(
struct
net_device
*
)
seq
->
private
;
struct
bat_priv
*
bat_priv
=
netdev_priv
(
net_dev
);
struct
hashtable_t
*
hash
=
bat_priv
->
tt_local_hash
;
struct
tt_
local_entry
*
tt_local
_entry
;
struct
tt_
common_entry
*
tt_common
_entry
;
struct
hard_iface
*
primary_if
;
struct
hlist_node
*
node
;
struct
hlist_head
*
head
;
...
...
@@ -340,19 +337,19 @@ int tt_local_seq_print_text(struct seq_file *seq, void *offset)
head
=
&
hash
->
table
[
i
];
rcu_read_lock
();
hlist_for_each_entry_rcu
(
tt_
local
_entry
,
node
,
hlist_for_each_entry_rcu
(
tt_
common
_entry
,
node
,
head
,
hash_entry
)
{
seq_printf
(
seq
,
" * %pM [%c%c%c%c%c]
\n
"
,
tt_
local
_entry
->
addr
,
(
tt_
local
_entry
->
flags
&
tt_
common
_entry
->
addr
,
(
tt_
common
_entry
->
flags
&
TT_CLIENT_ROAM
?
'R'
:
'.'
),
(
tt_
local
_entry
->
flags
&
(
tt_
common
_entry
->
flags
&
TT_CLIENT_NOPURGE
?
'P'
:
'.'
),
(
tt_
local
_entry
->
flags
&
(
tt_
common
_entry
->
flags
&
TT_CLIENT_NEW
?
'N'
:
'.'
),
(
tt_
local
_entry
->
flags
&
(
tt_
common
_entry
->
flags
&
TT_CLIENT_PENDING
?
'X'
:
'.'
),
(
tt_
local
_entry
->
flags
&
(
tt_
common
_entry
->
flags
&
TT_CLIENT_WIFI
?
'W'
:
'.'
));
}
rcu_read_unlock
();
...
...
@@ -367,13 +364,13 @@ static void tt_local_set_pending(struct bat_priv *bat_priv,
struct
tt_local_entry
*
tt_local_entry
,
uint16_t
flags
)
{
tt_local_event
(
bat_priv
,
tt_local_entry
->
addr
,
tt_local_entry
->
flags
|
flags
);
tt_local_event
(
bat_priv
,
tt_local_entry
->
common
.
addr
,
tt_local_entry
->
common
.
flags
|
flags
);
/* The local client has to be marked as "pending to be removed" but has
* to be kept in the table in order to send it in a full table
* response issued before the net ttvn increment (consistency check) */
tt_local_entry
->
flags
|=
TT_CLIENT_PENDING
;
tt_local_entry
->
common
.
flags
|=
TT_CLIENT_PENDING
;
}
void
tt_local_remove
(
struct
bat_priv
*
bat_priv
,
const
uint8_t
*
addr
,
...
...
@@ -389,7 +386,7 @@ void tt_local_remove(struct bat_priv *bat_priv, const uint8_t *addr,
(
roaming
?
TT_CLIENT_ROAM
:
NO_FLAGS
));
bat_dbg
(
DBG_TT
,
bat_priv
,
"Local tt entry (%pM) pending to be removed: "
"%s
\n
"
,
tt_local_entry
->
addr
,
message
);
"%s
\n
"
,
tt_local_entry
->
common
.
addr
,
message
);
out:
if
(
tt_local_entry
)
tt_local_entry_free_ref
(
tt_local_entry
);
...
...
@@ -399,6 +396,7 @@ static void tt_local_purge(struct bat_priv *bat_priv)
{
struct
hashtable_t
*
hash
=
bat_priv
->
tt_local_hash
;
struct
tt_local_entry
*
tt_local_entry
;
struct
tt_common_entry
*
tt_common_entry
;
struct
hlist_node
*
node
,
*
node_tmp
;
struct
hlist_head
*
head
;
spinlock_t
*
list_lock
;
/* protects write access to the hash lists */
...
...
@@ -409,13 +407,16 @@ static void tt_local_purge(struct bat_priv *bat_priv)
list_lock
=
&
hash
->
list_locks
[
i
];
spin_lock_bh
(
list_lock
);
hlist_for_each_entry_safe
(
tt_
local
_entry
,
node
,
node_tmp
,
hlist_for_each_entry_safe
(
tt_
common
_entry
,
node
,
node_tmp
,
head
,
hash_entry
)
{
if
(
tt_local_entry
->
flags
&
TT_CLIENT_NOPURGE
)
tt_local_entry
=
container_of
(
tt_common_entry
,
struct
tt_local_entry
,
common
);
if
(
tt_local_entry
->
common
.
flags
&
TT_CLIENT_NOPURGE
)
continue
;
/* entry already marked for deletion */
if
(
tt_local_entry
->
flags
&
TT_CLIENT_PENDING
)
if
(
tt_local_entry
->
common
.
flags
&
TT_CLIENT_PENDING
)
continue
;
if
(
!
is_out_of_time
(
tt_local_entry
->
last_seen
,
...
...
@@ -426,7 +427,7 @@ static void tt_local_purge(struct bat_priv *bat_priv)
TT_CLIENT_DEL
);
bat_dbg
(
DBG_TT
,
bat_priv
,
"Local tt entry (%pM) "
"pending to be removed: timed out
\n
"
,
tt_local_entry
->
addr
);
tt_local_entry
->
common
.
addr
);
}
spin_unlock_bh
(
list_lock
);
}
...
...
@@ -437,6 +438,7 @@ static void tt_local_table_free(struct bat_priv *bat_priv)
{
struct
hashtable_t
*
hash
;
spinlock_t
*
list_lock
;
/* protects write access to the hash lists */
struct
tt_common_entry
*
tt_common_entry
;
struct
tt_local_entry
*
tt_local_entry
;
struct
hlist_node
*
node
,
*
node_tmp
;
struct
hlist_head
*
head
;
...
...
@@ -452,9 +454,12 @@ static void tt_local_table_free(struct bat_priv *bat_priv)
list_lock
=
&
hash
->
list_locks
[
i
];
spin_lock_bh
(
list_lock
);
hlist_for_each_entry_safe
(
tt_
local
_entry
,
node
,
node_tmp
,
hlist_for_each_entry_safe
(
tt_
common
_entry
,
node
,
node_tmp
,
head
,
hash_entry
)
{
hlist_del_rcu
(
node
);
tt_local_entry
=
container_of
(
tt_common_entry
,
struct
tt_local_entry
,
common
);
tt_local_entry_free_ref
(
tt_local_entry
);
}
spin_unlock_bh
(
list_lock
);
...
...
@@ -502,6 +507,7 @@ int tt_global_add(struct bat_priv *bat_priv, struct orig_node *orig_node,
struct
tt_global_entry
*
tt_global_entry
;
struct
orig_node
*
orig_node_tmp
;
int
ret
=
0
;
int
hash_added
;
tt_global_entry
=
tt_global_hash_find
(
bat_priv
,
tt_addr
);
...
...
@@ -512,18 +518,24 @@ int tt_global_add(struct bat_priv *bat_priv, struct orig_node *orig_node,
if
(
!
tt_global_entry
)
goto
out
;
memcpy
(
tt_global_entry
->
addr
,
tt_addr
,
ETH_ALEN
);
memcpy
(
tt_global_entry
->
common
.
addr
,
tt_addr
,
ETH_ALEN
);
tt_global_entry
->
common
.
flags
=
NO_FLAGS
;
atomic_set
(
&
tt_global_entry
->
common
.
refcount
,
2
);
/* Assign the new orig_node */
atomic_inc
(
&
orig_node
->
refcount
);
tt_global_entry
->
orig_node
=
orig_node
;
tt_global_entry
->
ttvn
=
ttvn
;
tt_global_entry
->
flags
=
NO_FLAGS
;
tt_global_entry
->
roam_at
=
0
;
atomic_set
(
&
tt_global_entry
->
refcount
,
2
);
hash_add
(
bat_priv
->
tt_global_hash
,
compare_gtt
,
choose_orig
,
tt_global_entry
,
&
tt_global_entry
->
hash_entry
);
hash_added
=
hash_add
(
bat_priv
->
tt_global_hash
,
compare_tt
,
choose_orig
,
&
tt_global_entry
->
common
,
&
tt_global_entry
->
common
.
hash_entry
);
if
(
unlikely
(
hash_added
!=
0
))
{
/* remove the reference for the hash */
tt_global_entry_free_ref
(
tt_global_entry
);
goto
out_remove
;
}
atomic_inc
(
&
orig_node
->
tt_size
);
}
else
{
if
(
tt_global_entry
->
orig_node
!=
orig_node
)
{
...
...
@@ -534,20 +546,21 @@ int tt_global_add(struct bat_priv *bat_priv, struct orig_node *orig_node,
orig_node_free_ref
(
orig_node_tmp
);
atomic_inc
(
&
orig_node
->
tt_size
);
}
tt_global_entry
->
common
.
flags
=
NO_FLAGS
;
tt_global_entry
->
ttvn
=
ttvn
;
tt_global_entry
->
flags
=
NO_FLAGS
;
tt_global_entry
->
roam_at
=
0
;
}
if
(
wifi
)
tt_global_entry
->
flags
|=
TT_CLIENT_WIFI
;
tt_global_entry
->
common
.
flags
|=
TT_CLIENT_WIFI
;
bat_dbg
(
DBG_TT
,
bat_priv
,
"Creating new global tt entry: %pM (via %pM)
\n
"
,
tt_global_entry
->
addr
,
orig_node
->
orig
);
tt_global_entry
->
common
.
addr
,
orig_node
->
orig
);
out_remove:
/* remove address from local hash if present */
tt_local_remove
(
bat_priv
,
tt_global_entry
->
addr
,
tt_local_remove
(
bat_priv
,
tt_global_entry
->
common
.
addr
,
"global tt received"
,
roaming
);
ret
=
1
;
out:
...
...
@@ -561,6 +574,7 @@ int tt_global_seq_print_text(struct seq_file *seq, void *offset)
struct
net_device
*
net_dev
=
(
struct
net_device
*
)
seq
->
private
;
struct
bat_priv
*
bat_priv
=
netdev_priv
(
net_dev
);
struct
hashtable_t
*
hash
=
bat_priv
->
tt_global_hash
;
struct
tt_common_entry
*
tt_common_entry
;
struct
tt_global_entry
*
tt_global_entry
;
struct
hard_iface
*
primary_if
;
struct
hlist_node
*
node
;
...
...
@@ -593,20 +607,24 @@ int tt_global_seq_print_text(struct seq_file *seq, void *offset)
head
=
&
hash
->
table
[
i
];
rcu_read_lock
();
hlist_for_each_entry_rcu
(
tt_
global
_entry
,
node
,
hlist_for_each_entry_rcu
(
tt_
common
_entry
,
node
,
head
,
hash_entry
)
{
tt_global_entry
=
container_of
(
tt_common_entry
,
struct
tt_global_entry
,
common
);
seq_printf
(
seq
,
" * %pM (%3u) via %pM (%3u) "
"[%c%c%c]
\n
"
,
tt_global_entry
->
addr
,
"[%c%c%c]
\n
"
,
tt_global_entry
->
common
.
addr
,
tt_global_entry
->
ttvn
,
tt_global_entry
->
orig_node
->
orig
,
(
uint8_t
)
atomic_read
(
&
tt_global_entry
->
orig_node
->
last_ttvn
),
(
tt_global_entry
->
flags
&
(
tt_global_entry
->
common
.
flags
&
TT_CLIENT_ROAM
?
'R'
:
'.'
),
(
tt_global_entry
->
flags
&
(
tt_global_entry
->
common
.
flags
&
TT_CLIENT_PENDING
?
'X'
:
'.'
),
(
tt_global_entry
->
flags
&
(
tt_global_entry
->
common
.
flags
&
TT_CLIENT_WIFI
?
'W'
:
'.'
));
}
rcu_read_unlock
();
...
...
@@ -626,13 +644,13 @@ static void _tt_global_del(struct bat_priv *bat_priv,
bat_dbg
(
DBG_TT
,
bat_priv
,
"Deleting global tt entry %pM (via %pM): %s
\n
"
,
tt_global_entry
->
addr
,
tt_global_entry
->
orig_node
->
orig
,
tt_global_entry
->
common
.
addr
,
tt_global_entry
->
orig_node
->
orig
,
message
);
atomic_dec
(
&
tt_global_entry
->
orig_node
->
tt_size
);
hash_remove
(
bat_priv
->
tt_global_hash
,
compare_
g
tt
,
choose_orig
,
tt_global_entry
->
addr
);
hash_remove
(
bat_priv
->
tt_global_hash
,
compare_tt
,
choose_orig
,
tt_global_entry
->
common
.
addr
);
out:
if
(
tt_global_entry
)
tt_global_entry_free_ref
(
tt_global_entry
);
...
...
@@ -650,7 +668,7 @@ void tt_global_del(struct bat_priv *bat_priv,
if
(
tt_global_entry
->
orig_node
==
orig_node
)
{
if
(
roaming
)
{
tt_global_entry
->
flags
|=
TT_CLIENT_ROAM
;
tt_global_entry
->
common
.
flags
|=
TT_CLIENT_ROAM
;
tt_global_entry
->
roam_at
=
jiffies
;
goto
out
;
}
...
...
@@ -665,6 +683,7 @@ void tt_global_del_orig(struct bat_priv *bat_priv,
struct
orig_node
*
orig_node
,
const
char
*
message
)
{
struct
tt_global_entry
*
tt_global_entry
;
struct
tt_common_entry
*
tt_common_entry
;
uint32_t
i
;
struct
hashtable_t
*
hash
=
bat_priv
->
tt_global_hash
;
struct
hlist_node
*
node
,
*
safe
;
...
...
@@ -679,13 +698,16 @@ void tt_global_del_orig(struct bat_priv *bat_priv,
list_lock
=
&
hash
->
list_locks
[
i
];
spin_lock_bh
(
list_lock
);
hlist_for_each_entry_safe
(
tt_
global
_entry
,
node
,
safe
,
hlist_for_each_entry_safe
(
tt_
common
_entry
,
node
,
safe
,
head
,
hash_entry
)
{
tt_global_entry
=
container_of
(
tt_common_entry
,
struct
tt_global_entry
,
common
);
if
(
tt_global_entry
->
orig_node
==
orig_node
)
{
bat_dbg
(
DBG_TT
,
bat_priv
,
"Deleting global tt entry %pM "
"(via %pM): %s
\n
"
,
tt_global_entry
->
addr
,
tt_global_entry
->
common
.
addr
,
tt_global_entry
->
orig_node
->
orig
,
message
);
hlist_del_rcu
(
node
);
...
...
@@ -700,6 +722,7 @@ void tt_global_del_orig(struct bat_priv *bat_priv,
static
void
tt_global_roam_purge
(
struct
bat_priv
*
bat_priv
)
{
struct
hashtable_t
*
hash
=
bat_priv
->
tt_global_hash
;
struct
tt_common_entry
*
tt_common_entry
;
struct
tt_global_entry
*
tt_global_entry
;
struct
hlist_node
*
node
,
*
node_tmp
;
struct
hlist_head
*
head
;
...
...
@@ -711,9 +734,12 @@ static void tt_global_roam_purge(struct bat_priv *bat_priv)
list_lock
=
&
hash
->
list_locks
[
i
];
spin_lock_bh
(
list_lock
);
hlist_for_each_entry_safe
(
tt_
global
_entry
,
node
,
node_tmp
,
hlist_for_each_entry_safe
(
tt_
common
_entry
,
node
,
node_tmp
,
head
,
hash_entry
)
{
if
(
!
(
tt_global_entry
->
flags
&
TT_CLIENT_ROAM
))
tt_global_entry
=
container_of
(
tt_common_entry
,
struct
tt_global_entry
,
common
);
if
(
!
(
tt_global_entry
->
common
.
flags
&
TT_CLIENT_ROAM
))
continue
;
if
(
!
is_out_of_time
(
tt_global_entry
->
roam_at
,
TT_CLIENT_ROAM_TIMEOUT
*
1000
))
...
...
@@ -721,7 +747,7 @@ static void tt_global_roam_purge(struct bat_priv *bat_priv)
bat_dbg
(
DBG_TT
,
bat_priv
,
"Deleting global "
"tt entry (%pM): Roaming timeout
\n
"
,
tt_global_entry
->
addr
);
tt_global_entry
->
common
.
addr
);
atomic_dec
(
&
tt_global_entry
->
orig_node
->
tt_size
);
hlist_del_rcu
(
node
);
tt_global_entry_free_ref
(
tt_global_entry
);
...
...
@@ -735,6 +761,7 @@ static void tt_global_table_free(struct bat_priv *bat_priv)
{
struct
hashtable_t
*
hash
;
spinlock_t
*
list_lock
;
/* protects write access to the hash lists */
struct
tt_common_entry
*
tt_common_entry
;
struct
tt_global_entry
*
tt_global_entry
;
struct
hlist_node
*
node
,
*
node_tmp
;
struct
hlist_head
*
head
;
...
...
@@ -750,9 +777,12 @@ static void tt_global_table_free(struct bat_priv *bat_priv)
list_lock
=
&
hash
->
list_locks
[
i
];
spin_lock_bh
(
list_lock
);
hlist_for_each_entry_safe
(
tt_
global
_entry
,
node
,
node_tmp
,
hlist_for_each_entry_safe
(
tt_
common
_entry
,
node
,
node_tmp
,
head
,
hash_entry
)
{
hlist_del_rcu
(
node
);
tt_global_entry
=
container_of
(
tt_common_entry
,
struct
tt_global_entry
,
common
);
tt_global_entry_free_ref
(
tt_global_entry
);
}
spin_unlock_bh
(
list_lock
);
...
...
@@ -768,8 +798,8 @@ static bool _is_ap_isolated(struct tt_local_entry *tt_local_entry,
{
bool
ret
=
false
;
if
(
tt_local_entry
->
flags
&
TT_CLIENT_WIFI
&&
tt_global_entry
->
flags
&
TT_CLIENT_WIFI
)
if
(
tt_local_entry
->
common
.
flags
&
TT_CLIENT_WIFI
&&
tt_global_entry
->
common
.
flags
&
TT_CLIENT_WIFI
)
ret
=
true
;
return
ret
;
...
...
@@ -802,7 +832,7 @@ struct orig_node *transtable_search(struct bat_priv *bat_priv,
/* A global client marked as PENDING has already moved from that
* originator */
if
(
tt_global_entry
->
flags
&
TT_CLIENT_PENDING
)
if
(
tt_global_entry
->
common
.
flags
&
TT_CLIENT_PENDING
)
goto
out
;
orig_node
=
tt_global_entry
->
orig_node
;
...
...
@@ -821,6 +851,7 @@ uint16_t tt_global_crc(struct bat_priv *bat_priv, struct orig_node *orig_node)
{
uint16_t
total
=
0
,
total_one
;
struct
hashtable_t
*
hash
=
bat_priv
->
tt_global_hash
;
struct
tt_common_entry
*
tt_common_entry
;
struct
tt_global_entry
*
tt_global_entry
;
struct
hlist_node
*
node
;
struct
hlist_head
*
head
;
...
...
@@ -831,20 +862,23 @@ uint16_t tt_global_crc(struct bat_priv *bat_priv, struct orig_node *orig_node)
head
=
&
hash
->
table
[
i
];
rcu_read_lock
();
hlist_for_each_entry_rcu
(
tt_
global
_entry
,
node
,
hlist_for_each_entry_rcu
(
tt_
common
_entry
,
node
,
head
,
hash_entry
)
{
tt_global_entry
=
container_of
(
tt_common_entry
,
struct
tt_global_entry
,
common
);
if
(
compare_eth
(
tt_global_entry
->
orig_node
,
orig_node
))
{
/* Roaming clients are in the global table for
* consistency only. They don't have to be
* taken into account while computing the
* global crc */
if
(
tt_
global
_entry
->
flags
&
TT_CLIENT_ROAM
)
if
(
tt_
common
_entry
->
flags
&
TT_CLIENT_ROAM
)
continue
;
total_one
=
0
;
for
(
j
=
0
;
j
<
ETH_ALEN
;
j
++
)
total_one
=
crc16_byte
(
total_one
,
tt_
global
_entry
->
addr
[
j
]);
tt_
common
_entry
->
addr
[
j
]);
total
^=
total_one
;
}
}
...
...
@@ -859,7 +893,7 @@ uint16_t tt_local_crc(struct bat_priv *bat_priv)
{
uint16_t
total
=
0
,
total_one
;
struct
hashtable_t
*
hash
=
bat_priv
->
tt_local_hash
;
struct
tt_
local_entry
*
tt_local
_entry
;
struct
tt_
common_entry
*
tt_common
_entry
;
struct
hlist_node
*
node
;
struct
hlist_head
*
head
;
uint32_t
i
;
...
...
@@ -869,16 +903,16 @@ uint16_t tt_local_crc(struct bat_priv *bat_priv)
head
=
&
hash
->
table
[
i
];
rcu_read_lock
();
hlist_for_each_entry_rcu
(
tt_
local
_entry
,
node
,
hlist_for_each_entry_rcu
(
tt_
common
_entry
,
node
,
head
,
hash_entry
)
{
/* not yet committed clients have not to be taken into
* account while computing the CRC */
if
(
tt_
local
_entry
->
flags
&
TT_CLIENT_NEW
)
if
(
tt_
common
_entry
->
flags
&
TT_CLIENT_NEW
)
continue
;
total_one
=
0
;
for
(
j
=
0
;
j
<
ETH_ALEN
;
j
++
)
total_one
=
crc16_byte
(
total_one
,
tt_
local
_entry
->
addr
[
j
]);
tt_
common
_entry
->
addr
[
j
]);
total
^=
total_one
;
}
rcu_read_unlock
();
...
...
@@ -967,21 +1001,25 @@ static struct tt_req_node *new_tt_req_node(struct bat_priv *bat_priv,
/* data_ptr is useless here, but has to be kept to respect the prototype */
static
int
tt_local_valid_entry
(
const
void
*
entry_ptr
,
const
void
*
data_ptr
)
{
const
struct
tt_
local_entry
*
tt_local
_entry
=
entry_ptr
;
const
struct
tt_
common_entry
*
tt_common
_entry
=
entry_ptr
;
if
(
tt_
local
_entry
->
flags
&
TT_CLIENT_NEW
)
if
(
tt_
common
_entry
->
flags
&
TT_CLIENT_NEW
)
return
0
;
return
1
;
}
static
int
tt_global_valid_entry
(
const
void
*
entry_ptr
,
const
void
*
data_ptr
)
{
const
struct
tt_global_entry
*
tt_global_entry
=
entry_ptr
;
const
struct
tt_common_entry
*
tt_common_entry
=
entry_ptr
;
const
struct
tt_global_entry
*
tt_global_entry
;
const
struct
orig_node
*
orig_node
=
data_ptr
;
if
(
tt_
global
_entry
->
flags
&
TT_CLIENT_ROAM
)
if
(
tt_
common
_entry
->
flags
&
TT_CLIENT_ROAM
)
return
0
;
tt_global_entry
=
container_of
(
tt_common_entry
,
struct
tt_global_entry
,
common
);
return
(
tt_global_entry
->
orig_node
==
orig_node
);
}
...
...
@@ -992,7 +1030,7 @@ static struct sk_buff *tt_response_fill_table(uint16_t tt_len, uint8_t ttvn,
const
void
*
),
void
*
cb_data
)
{
struct
tt_
local_entry
*
tt_local
_entry
;
struct
tt_
common_entry
*
tt_common
_entry
;
struct
tt_query_packet
*
tt_response
;
struct
tt_change
*
tt_change
;
struct
hlist_node
*
node
;
...
...
@@ -1024,15 +1062,16 @@ static struct sk_buff *tt_response_fill_table(uint16_t tt_len, uint8_t ttvn,
for
(
i
=
0
;
i
<
hash
->
size
;
i
++
)
{
head
=
&
hash
->
table
[
i
];
hlist_for_each_entry_rcu
(
tt_
local
_entry
,
node
,
hlist_for_each_entry_rcu
(
tt_
common
_entry
,
node
,
head
,
hash_entry
)
{
if
(
tt_count
==
tt_tot
)
break
;
if
((
valid_cb
)
&&
(
!
valid_cb
(
tt_
local
_entry
,
cb_data
)))
if
((
valid_cb
)
&&
(
!
valid_cb
(
tt_
common
_entry
,
cb_data
)))
continue
;
memcpy
(
tt_change
->
addr
,
tt_local_entry
->
addr
,
ETH_ALEN
);
memcpy
(
tt_change
->
addr
,
tt_common_entry
->
addr
,
ETH_ALEN
);
tt_change
->
flags
=
NO_FLAGS
;
tt_count
++
;
...
...
@@ -1449,7 +1488,7 @@ bool is_my_client(struct bat_priv *bat_priv, const uint8_t *addr)
goto
out
;
/* Check if the client has been logically deleted (but is kept for
* consistency purpose) */
if
(
tt_local_entry
->
flags
&
TT_CLIENT_PENDING
)
if
(
tt_local_entry
->
common
.
flags
&
TT_CLIENT_PENDING
)
goto
out
;
ret
=
true
;
out:
...
...
@@ -1672,40 +1711,48 @@ void tt_free(struct bat_priv *bat_priv)
kfree
(
bat_priv
->
tt_buff
);
}
/* This function will
reset the specified flags from all the entries in
*
the given hash table and will increment num_local_tt for each involved
* entry */
static
void
tt_local_reset_flags
(
struct
bat_priv
*
bat_priv
,
uint16_t
flags
)
/* This function will
enable or disable the specified flags for all the entries
*
in the given hash table and returns the number of modified entries */
static
uint16_t
tt_set_flags
(
struct
hashtable_t
*
hash
,
uint16_t
flags
,
bool
enable
)
{
uint32_t
i
;
struct
hashtable_t
*
hash
=
bat_priv
->
tt_local_hash
;
uint16_t
changed_num
=
0
;
struct
hlist_head
*
head
;
struct
hlist_node
*
node
;
struct
tt_
local_entry
*
tt_local
_entry
;
struct
tt_
common_entry
*
tt_common
_entry
;
if
(
!
hash
)
return
;
goto
out
;
for
(
i
=
0
;
i
<
hash
->
size
;
i
++
)
{
head
=
&
hash
->
table
[
i
];
rcu_read_lock
();
hlist_for_each_entry_rcu
(
tt_
local
_entry
,
node
,
hlist_for_each_entry_rcu
(
tt_
common
_entry
,
node
,
head
,
hash_entry
)
{
if
(
!
(
tt_local_entry
->
flags
&
flags
))
continue
;
tt_local_entry
->
flags
&=
~
flags
;
atomic_inc
(
&
bat_priv
->
num_local_tt
);
if
(
enable
)
{
if
((
tt_common_entry
->
flags
&
flags
)
==
flags
)
continue
;
tt_common_entry
->
flags
|=
flags
;
}
else
{
if
(
!
(
tt_common_entry
->
flags
&
flags
))
continue
;
tt_common_entry
->
flags
&=
~
flags
;
}
changed_num
++
;
}
rcu_read_unlock
();
}
out:
return
changed_num
;
}
/* Purge out all the tt local entries marked with TT_CLIENT_PENDING */
static
void
tt_local_purge_pending_clients
(
struct
bat_priv
*
bat_priv
)
{
struct
hashtable_t
*
hash
=
bat_priv
->
tt_local_hash
;
struct
tt_common_entry
*
tt_common_entry
;
struct
tt_local_entry
*
tt_local_entry
;
struct
hlist_node
*
node
,
*
node_tmp
;
struct
hlist_head
*
head
;
...
...
@@ -1720,16 +1767,19 @@ static void tt_local_purge_pending_clients(struct bat_priv *bat_priv)
list_lock
=
&
hash
->
list_locks
[
i
];
spin_lock_bh
(
list_lock
);
hlist_for_each_entry_safe
(
tt_
local
_entry
,
node
,
node_tmp
,
hlist_for_each_entry_safe
(
tt_
common
_entry
,
node
,
node_tmp
,
head
,
hash_entry
)
{
if
(
!
(
tt_
local
_entry
->
flags
&
TT_CLIENT_PENDING
))
if
(
!
(
tt_
common
_entry
->
flags
&
TT_CLIENT_PENDING
))
continue
;
bat_dbg
(
DBG_TT
,
bat_priv
,
"Deleting local tt entry "
"(%pM): pending
\n
"
,
tt_
local
_entry
->
addr
);
"(%pM): pending
\n
"
,
tt_
common
_entry
->
addr
);
atomic_dec
(
&
bat_priv
->
num_local_tt
);
hlist_del_rcu
(
node
);
tt_local_entry
=
container_of
(
tt_common_entry
,
struct
tt_local_entry
,
common
);
tt_local_entry_free_ref
(
tt_local_entry
);
}
spin_unlock_bh
(
list_lock
);
...
...
@@ -1739,7 +1789,11 @@ static void tt_local_purge_pending_clients(struct bat_priv *bat_priv)
void
tt_commit_changes
(
struct
bat_priv
*
bat_priv
)
{
tt_local_reset_flags
(
bat_priv
,
TT_CLIENT_NEW
);
uint16_t
changed_num
=
tt_set_flags
(
bat_priv
->
tt_local_hash
,
TT_CLIENT_NEW
,
false
);
/* all the reset entries have now to be effectively counted as local
* entries */
atomic_add
(
changed_num
,
&
bat_priv
->
num_local_tt
);
tt_local_purge_pending_clients
(
bat_priv
);
/* Increment the TTVN only once per OGM interval */
...
...
net/batman-adv/types.h
浏览文件 @
c7c6575f
...
...
@@ -222,24 +222,24 @@ struct socket_packet {
struct
icmp_packet_rr
icmp_packet
;
};
struct
tt_
local
_entry
{
struct
tt_
common
_entry
{
uint8_t
addr
[
ETH_ALEN
];
struct
hlist_node
hash_entry
;
unsigned
long
last_seen
;
uint16_t
flags
;
atomic_t
refcount
;
struct
rcu_head
rcu
;
};
struct
tt_local_entry
{
struct
tt_common_entry
common
;
unsigned
long
last_seen
;
};
struct
tt_global_entry
{
uint8_t
addr
[
ETH_ALEN
];
struct
hlist_node
hash_entry
;
/* entry in the global table */
struct
tt_common_entry
common
;
struct
orig_node
*
orig_node
;
uint8_t
ttvn
;
uint16_t
flags
;
/* only TT_GLOBAL_ROAM is used */
unsigned
long
roam_at
;
/* time at which TT_GLOBAL_ROAM was set */
atomic_t
refcount
;
struct
rcu_head
rcu
;
};
struct
tt_change_node
{
...
...
net/batman-adv/vis.c
浏览文件 @
c7c6575f
...
...
@@ -609,7 +609,7 @@ static int generate_vis_packet(struct bat_priv *bat_priv)
struct
vis_info
*
info
=
bat_priv
->
my_vis_info
;
struct
vis_packet
*
packet
=
(
struct
vis_packet
*
)
info
->
skb_packet
->
data
;
struct
vis_info_entry
*
entry
;
struct
tt_
local_entry
*
tt_local
_entry
;
struct
tt_
common_entry
*
tt_common
_entry
;
int
best_tq
=
-
1
;
uint32_t
i
;
...
...
@@ -672,13 +672,13 @@ static int generate_vis_packet(struct bat_priv *bat_priv)
head
=
&
hash
->
table
[
i
];
rcu_read_lock
();
hlist_for_each_entry_rcu
(
tt_
local
_entry
,
node
,
head
,
hlist_for_each_entry_rcu
(
tt_
common
_entry
,
node
,
head
,
hash_entry
)
{
entry
=
(
struct
vis_info_entry
*
)
skb_put
(
info
->
skb_packet
,
sizeof
(
*
entry
));
memset
(
entry
->
src
,
0
,
ETH_ALEN
);
memcpy
(
entry
->
dest
,
tt_
local
_entry
->
addr
,
ETH_ALEN
);
memcpy
(
entry
->
dest
,
tt_
common
_entry
->
addr
,
ETH_ALEN
);
entry
->
quality
=
0
;
/* 0 means TT */
packet
->
entries
++
;
...
...
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录