mirror of
https://github.com/versity/scoutfs.git
synced 2026-01-11 06:00:19 +00:00
scoutfs: add conn destroy workq
Lockdep gets angry when we try to destroy an accepted conn workqueue from within work in a listening conn's workqueue. It doesn't recognize that they have a hierarchical relationship that maintains a consistent order and we can't get at the workqueue lockdep_map to set subclasses. We add a destroy workqueue which will have its own class. Signed-off-by: Zach Brown <zab@versity.com>
This commit is contained in:
@@ -80,6 +80,7 @@
|
||||
*/
|
||||
struct net_info {
|
||||
struct workqueue_struct *shutdown_workq;
|
||||
struct workqueue_struct *destroy_workq;
|
||||
struct dentry *conn_tseq_dentry;
|
||||
struct scoutfs_tseq_tree conn_tseq_tree;
|
||||
struct dentry *msg_tseq_dentry;
|
||||
@@ -796,14 +797,25 @@ static void scoutfs_net_send_worker(struct work_struct *work)
|
||||
trace_scoutfs_net_send_work_exit(sb, 0, ret);
|
||||
}
|
||||
|
||||
static void destroy_conn(struct scoutfs_net_connection *conn)
|
||||
/*
|
||||
* Listening conns try to destroy accepted conns. Workqueues model
|
||||
* flushing work as acquiring a workqueue class lock so it thinks that
|
||||
* this is a deadlock because it doesn't know about our hierarchy of
|
||||
* workqueues. The workqueue lockdep_map is private so we can't set a
|
||||
* subclass to differentiate between listening and accepted conn
|
||||
* workqueues. Instead we queue final conn destruction off to a longer
|
||||
* lived specific workqueue that has a different class.
|
||||
*/
|
||||
static void scoutfs_net_destroy_worker(struct work_struct *work)
|
||||
{
|
||||
DEFINE_CONN_FROM_WORK(conn, work, destroy_work);
|
||||
struct super_block *sb = conn->sb;
|
||||
struct net_info *ninf = SCOUTFS_SB(sb)->net_info;
|
||||
struct scoutfs_net_connection *listener;
|
||||
struct message_send *msend;
|
||||
struct message_send *tmp;
|
||||
|
||||
trace_scoutfs_net_destroy_work_enter(sb, 0, 0);
|
||||
trace_scoutfs_conn_destroy_start(conn);
|
||||
|
||||
WARN_ON_ONCE(conn->sock != NULL);
|
||||
@@ -835,6 +847,15 @@ static void destroy_conn(struct scoutfs_net_connection *conn)
|
||||
kfree(conn->info);
|
||||
trace_scoutfs_conn_destroy_free(conn);
|
||||
kfree(conn);
|
||||
|
||||
trace_scoutfs_net_destroy_work_exit(sb, 0, 0);
|
||||
}
|
||||
|
||||
static void destroy_conn(struct scoutfs_net_connection *conn)
|
||||
{
|
||||
struct net_info *ninf = SCOUTFS_SB(conn->sb)->net_info;
|
||||
|
||||
queue_work(ninf->destroy_workq, &conn->destroy_work);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -1286,6 +1307,7 @@ scoutfs_net_alloc_conn(struct super_block *sb,
|
||||
INIT_WORK(&conn->send_work, scoutfs_net_send_worker);
|
||||
INIT_WORK(&conn->recv_work, scoutfs_net_recv_worker);
|
||||
INIT_WORK(&conn->shutdown_work, scoutfs_net_shutdown_worker);
|
||||
INIT_WORK(&conn->destroy_work, scoutfs_net_destroy_worker);
|
||||
INIT_DELAYED_WORK(&conn->reconn_free_dwork,
|
||||
scoutfs_net_reconn_free_worker);
|
||||
|
||||
@@ -1317,6 +1339,7 @@ void scoutfs_net_shutdown(struct super_block *sb,
|
||||
{
|
||||
shutdown_conn(conn);
|
||||
flush_work(&conn->shutdown_work);
|
||||
flush_work(&conn->destroy_work);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -1843,7 +1866,10 @@ int scoutfs_net_setup(struct super_block *sb)
|
||||
ninf->shutdown_workq = alloc_workqueue("scoutfs_net_shutdown",
|
||||
WQ_UNBOUND | WQ_NON_REENTRANT,
|
||||
0);
|
||||
if (!ninf->shutdown_workq) {
|
||||
ninf->destroy_workq = alloc_workqueue("scoutfs_net_destroy",
|
||||
WQ_UNBOUND | WQ_NON_REENTRANT,
|
||||
0);
|
||||
if (!ninf->shutdown_workq || !ninf->destroy_workq) {
|
||||
ret = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
@@ -1879,6 +1905,8 @@ void scoutfs_net_destroy(struct super_block *sb)
|
||||
if (ninf) {
|
||||
if (ninf->shutdown_workq)
|
||||
destroy_workqueue(ninf->shutdown_workq);
|
||||
if (ninf->destroy_workq)
|
||||
destroy_workqueue(ninf->destroy_workq);
|
||||
debugfs_remove(ninf->conn_tseq_dentry);
|
||||
debugfs_remove(ninf->msg_tseq_dentry);
|
||||
kfree(ninf);
|
||||
|
||||
@@ -67,6 +67,7 @@ struct scoutfs_net_connection {
|
||||
struct work_struct send_work;
|
||||
struct work_struct recv_work;
|
||||
struct work_struct shutdown_work;
|
||||
struct work_struct destroy_work;
|
||||
struct delayed_work reconn_free_dwork;
|
||||
/* message_recv proc_work also executes in the conn workq */
|
||||
|
||||
|
||||
@@ -2001,6 +2001,14 @@ DEFINE_EVENT(scoutfs_work_class, scoutfs_net_shutdown_work_exit,
|
||||
TP_PROTO(struct super_block *sb, u64 data, int ret),
|
||||
TP_ARGS(sb, data, ret)
|
||||
);
|
||||
DEFINE_EVENT(scoutfs_work_class, scoutfs_net_destroy_work_enter,
|
||||
TP_PROTO(struct super_block *sb, u64 data, int ret),
|
||||
TP_ARGS(sb, data, ret)
|
||||
);
|
||||
DEFINE_EVENT(scoutfs_work_class, scoutfs_net_destroy_work_exit,
|
||||
TP_PROTO(struct super_block *sb, u64 data, int ret),
|
||||
TP_ARGS(sb, data, ret)
|
||||
);
|
||||
DEFINE_EVENT(scoutfs_work_class, scoutfs_net_reconn_free_work_enter,
|
||||
TP_PROTO(struct super_block *sb, u64 data, int ret),
|
||||
TP_ARGS(sb, data, ret)
|
||||
|
||||
Reference in New Issue
Block a user