From 30787a417086df301c7eb2f4ae14f2acab70e4b2 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Fri, 2 Sep 2016 22:39:44 +0100 Subject: rxrpc: fix undefined behavior in rxrpc_mark_call_released gcc -Wmaybe-initialized correctly points out a newly introduced bug through which we can end up calling rxrpc_queue_call() for a dead connection: net/rxrpc/call_object.c: In function 'rxrpc_mark_call_released': net/rxrpc/call_object.c:600:5: error: 'sched' may be used uninitialized in this function [-Werror=maybe-uninitialized] This sets the 'sched' variable to zero to restore the previous behavior. Signed-off-by: Arnd Bergmann Fixes: f5c17aaeb2ae ("rxrpc: Calls should only have one terminal state") Signed-off-by: David Howells diff --git a/net/rxrpc/call_object.c b/net/rxrpc/call_object.c index 516d8ea..57e00fc 100644 --- a/net/rxrpc/call_object.c +++ b/net/rxrpc/call_object.c @@ -586,7 +586,7 @@ static void rxrpc_dead_call_expired(unsigned long _call) */ static void rxrpc_mark_call_released(struct rxrpc_call *call) { - bool sched; + bool sched = false; rxrpc_see_call(call); write_lock(&call->state_lock); -- cgit v0.10.2 From 00b5407e427ac2588a2496b92035a94602b3cd1b Mon Sep 17 00:00:00 2001 From: David Howells Date: Fri, 2 Sep 2016 22:39:44 +0100 Subject: rxrpc: Fix uninitialised variable warning Fix the following uninitialised variable warning: ../net/rxrpc/call_event.c: In function 'rxrpc_process_call': ../net/rxrpc/call_event.c:879:58: warning: 'error' may be used uninitialized in this function [-Wmaybe-uninitialized] _debug("post net error %d", error); ^ Signed-off-by: David Howells diff --git a/net/rxrpc/call_event.c b/net/rxrpc/call_event.c index de72de6..4754c7f 100644 --- a/net/rxrpc/call_event.c +++ b/net/rxrpc/call_event.c @@ -868,7 +868,6 @@ skip_msg_init: /* deal with events of a final nature */ if (test_bit(RXRPC_CALL_EV_RCVD_ERROR, &call->events)) { enum rxrpc_skb_mark mark; - int error; clear_bit(RXRPC_CALL_EV_CONN_ABORT, &call->events); clear_bit(RXRPC_CALL_EV_REJECT_BUSY, &call->events); @@ -876,10 +875,10 @@ skip_msg_init: if (call->completion == RXRPC_CALL_NETWORK_ERROR) { mark = RXRPC_SKB_MARK_NET_ERROR; - _debug("post net error %d", error); + _debug("post net error %d", call->error); } else { mark = RXRPC_SKB_MARK_LOCAL_ERROR; - _debug("post net local error %d", error); + _debug("post net local error %d", call->error); } if (rxrpc_post_message(call, mark, call->error, true) < 0) -- cgit v0.10.2 From af338a9ea60acc6337fe9fcdcf664aec2520e541 Mon Sep 17 00:00:00 2001 From: David Howells Date: Sun, 4 Sep 2016 13:10:10 +0100 Subject: rxrpc: The client call state must be changed before attachment to conn We must set the client call state to RXRPC_CALL_CLIENT_SEND_REQUEST before attaching the call to the connection struct, not after, as it's liable to receive errors and conn aborts as soon as the assignment is made - and these will cause its state to be changed outside of the initiating thread's control. Signed-off-by: David Howells diff --git a/net/rxrpc/call_object.c b/net/rxrpc/call_object.c index 57e00fc..6569174 100644 --- a/net/rxrpc/call_object.c +++ b/net/rxrpc/call_object.c @@ -197,8 +197,6 @@ static int rxrpc_begin_client_call(struct rxrpc_call *call, if (ret < 0) return ret; - call->state = RXRPC_CALL_CLIENT_SEND_REQUEST; - spin_lock(&call->conn->params.peer->lock); hlist_add_head(&call->error_link, &call->conn->params.peer->error_targets); spin_unlock(&call->conn->params.peer->lock); diff --git a/net/rxrpc/conn_client.c b/net/rxrpc/conn_client.c index 4b213bc..e19804d 100644 --- a/net/rxrpc/conn_client.c +++ b/net/rxrpc/conn_client.c @@ -537,6 +537,10 @@ static void rxrpc_activate_one_channel(struct rxrpc_connection *conn, struct rxrpc_call, chan_wait_link); u32 call_id = chan->call_counter + 1; + write_lock_bh(&call->state_lock); + call->state = RXRPC_CALL_CLIENT_SEND_REQUEST; + write_unlock_bh(&call->state_lock); + rxrpc_see_call(call); list_del_init(&call->chan_wait_link); conn->active_chans |= 1 << channel; -- cgit v0.10.2 From 5f2d9c44389e7cd9fe192570f6f20199bc861eb8 Mon Sep 17 00:00:00 2001 From: David Howells Date: Fri, 2 Sep 2016 22:39:45 +0100 Subject: rxrpc: Randomise epoch and starting client conn ID values Create a random epoch value rather than a time-based one on startup and set the top bit to indicate that this is the case. Also create a random starting client connection ID value. This will be incremented from here as new client connections are created. Signed-off-by: David Howells diff --git a/include/rxrpc/packet.h b/include/rxrpc/packet.h index b201744..3c6128e 100644 --- a/include/rxrpc/packet.h +++ b/include/rxrpc/packet.h @@ -24,6 +24,7 @@ typedef __be32 rxrpc_serial_net_t; /* on-the-wire Rx message serial number */ */ struct rxrpc_wire_header { __be32 epoch; /* client boot timestamp */ +#define RXRPC_RANDOM_EPOCH 0x80000000 /* Random if set, date-based if not */ __be32 cid; /* connection and channel ID */ #define RXRPC_MAXCALLS 4 /* max active calls per conn */ diff --git a/net/rxrpc/af_rxrpc.c b/net/rxrpc/af_rxrpc.c index 32d5449..b66a9e6 100644 --- a/net/rxrpc/af_rxrpc.c +++ b/net/rxrpc/af_rxrpc.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include @@ -700,7 +701,13 @@ static int __init af_rxrpc_init(void) BUILD_BUG_ON(sizeof(struct rxrpc_skb_priv) > FIELD_SIZEOF(struct sk_buff, cb)); - rxrpc_epoch = get_seconds(); + get_random_bytes(&rxrpc_epoch, sizeof(rxrpc_epoch)); + rxrpc_epoch |= RXRPC_RANDOM_EPOCH; + get_random_bytes(&rxrpc_client_conn_ids.cur, + sizeof(rxrpc_client_conn_ids.cur)); + rxrpc_client_conn_ids.cur &= 0x3fffffff; + if (rxrpc_client_conn_ids.cur == 0) + rxrpc_client_conn_ids.cur = 1; ret = -ENOMEM; rxrpc_call_jar = kmem_cache_create( -- cgit v0.10.2 From 090f85deb6e88f0edff1a18d610abd857e30c753 Mon Sep 17 00:00:00 2001 From: David Howells Date: Sun, 4 Sep 2016 13:14:46 +0100 Subject: rxrpc: Don't change the epoch It seems the local epoch should only be changed on boot, so remove the code that changes it for client connections. Signed-off-by: David Howells diff --git a/net/rxrpc/conn_client.c b/net/rxrpc/conn_client.c index e19804d..82de1ae 100644 --- a/net/rxrpc/conn_client.c +++ b/net/rxrpc/conn_client.c @@ -108,12 +108,12 @@ static DECLARE_DELAYED_WORK(rxrpc_client_conn_reap, /* * Get a connection ID and epoch for a client connection from the global pool. * The connection struct pointer is then recorded in the idr radix tree. The - * epoch is changed if this wraps. + * epoch doesn't change until the client is rebooted (or, at least, unless the + * module is unloaded). */ static int rxrpc_get_client_connection_id(struct rxrpc_connection *conn, gfp_t gfp) { - u32 epoch; int id; _enter(""); @@ -121,34 +121,18 @@ static int rxrpc_get_client_connection_id(struct rxrpc_connection *conn, idr_preload(gfp); spin_lock(&rxrpc_conn_id_lock); - epoch = rxrpc_epoch; - - /* We could use idr_alloc_cyclic() here, but we really need to know - * when the thing wraps so that we can advance the epoch. - */ - if (rxrpc_client_conn_ids.cur == 0) - rxrpc_client_conn_ids.cur = 1; - id = idr_alloc(&rxrpc_client_conn_ids, conn, - rxrpc_client_conn_ids.cur, 0x40000000, GFP_NOWAIT); - if (id < 0) { - if (id != -ENOSPC) - goto error; - id = idr_alloc(&rxrpc_client_conn_ids, conn, - 1, 0x40000000, GFP_NOWAIT); - if (id < 0) - goto error; - epoch++; - rxrpc_epoch = epoch; - } - rxrpc_client_conn_ids.cur = id + 1; + id = idr_alloc_cyclic(&rxrpc_client_conn_ids, conn, + 1, 0x40000000, GFP_NOWAIT); + if (id < 0) + goto error; spin_unlock(&rxrpc_conn_id_lock); idr_preload_end(); - conn->proto.epoch = epoch; + conn->proto.epoch = rxrpc_epoch; conn->proto.cid = id << RXRPC_CIDSHIFT; set_bit(RXRPC_CONN_HAS_IDR, &conn->flags); - _leave(" [CID %x:%x]", epoch, conn->proto.cid); + _leave(" [CID %x]", conn->proto.cid); return 0; error: -- cgit v0.10.2 From 9ce4d7d3850d1af0f3732c3da8e324cb83a45ca0 Mon Sep 17 00:00:00 2001 From: Bhaktipriya Shridhar Date: Sun, 4 Sep 2016 20:52:39 +0530 Subject: fs/afs/vlocation: Remove deprecated create_singlethread_workqueue The workqueue "afs_vlocation_update_worker" queues a single work item &afs_vlocation_update and hence it doesn't require execution ordering. Hence, alloc_workqueue has been used to replace the deprecated create_singlethread_workqueue instance. Since the workqueue is being used on a memory reclaim path, WQ_MEM_RECLAIM flag has been set to ensure forward progress under memory pressure. Since there are fixed number of work items, explicit concurrency limit is unnecessary here. Signed-off-by: Bhaktipriya Shridhar Signed-off-by: David Howells diff --git a/fs/afs/vlocation.c b/fs/afs/vlocation.c index 5297678..45a8639 100644 --- a/fs/afs/vlocation.c +++ b/fs/afs/vlocation.c @@ -594,8 +594,8 @@ static void afs_vlocation_reaper(struct work_struct *work) */ int __init afs_vlocation_update_init(void) { - afs_vlocation_update_worker = - create_singlethread_workqueue("kafs_vlupdated"); + afs_vlocation_update_worker = alloc_workqueue("kafs_vlupdated", + WQ_MEM_RECLAIM, 0); return afs_vlocation_update_worker ? 0 : -ENOMEM; } -- cgit v0.10.2 From 69ad052aec6bb9a3f376b6ae117dfde28ed337c8 Mon Sep 17 00:00:00 2001 From: Bhaktipriya Shridhar Date: Sun, 4 Sep 2016 20:53:42 +0530 Subject: fs/afs/rxrpc: Remove deprecated create_singlethread_workqueue The workqueue "afs_async_calls" queues work item &call->async_work per afs_call. Since there could be multiple calls and since these calls can be run concurrently, alloc_workqueue has been used to replace the deprecated create_singlethread_workqueue instance. The WQ_MEM_RECLAIM flag has been set to ensure forward progress under memory pressure because the workqueue is being used on a memory reclaim path. Since there are fixed number of work items, explicit concurrency limit is unnecessary here. Signed-off-by: Bhaktipriya Shridhar Signed-off-by: David Howells diff --git a/fs/afs/rxrpc.c b/fs/afs/rxrpc.c index 244896b..37608be 100644 --- a/fs/afs/rxrpc.c +++ b/fs/afs/rxrpc.c @@ -76,7 +76,7 @@ int afs_open_socket(void) _enter(""); ret = -ENOMEM; - afs_async_calls = create_singlethread_workqueue("kafsd"); + afs_async_calls = alloc_workqueue("kafsd", WQ_MEM_RECLAIM, 0); if (!afs_async_calls) goto error_0; -- cgit v0.10.2 From 4c136dae62f7a62383a9d6563c8613e732bfefaf Mon Sep 17 00:00:00 2001 From: Bhaktipriya Shridhar Date: Sun, 4 Sep 2016 20:54:11 +0530 Subject: fs/afs/callback: Remove deprecated create_singlethread_workqueue The workqueue "afs_callback_update_worker" queues multiple work items viz &vnode->cb_broken_work, &server->cb_break_work which require strict execution ordering. Hence, an ordered dedicated workqueue has been used. Since the workqueue is being used on a memory reclaim path, WQ_MEM_RECLAIM has been set to ensure forward progress under memory pressure. Signed-off-by: Bhaktipriya Shridhar Signed-off-by: David Howells diff --git a/fs/afs/callback.c b/fs/afs/callback.c index 7ef637d..1e9d2f8 100644 --- a/fs/afs/callback.c +++ b/fs/afs/callback.c @@ -461,8 +461,8 @@ static void afs_callback_updater(struct work_struct *work) */ int __init afs_callback_update_init(void) { - afs_callback_update_worker = - create_singlethread_workqueue("kafs_callbackd"); + afs_callback_update_worker = alloc_ordered_workqueue("kafs_callbackd", + WQ_MEM_RECLAIM); return afs_callback_update_worker ? 0 : -ENOMEM; } -- cgit v0.10.2 From 434e6120036d1dd1004cadf5a99941cdd9c1a59f Mon Sep 17 00:00:00 2001 From: Bhaktipriya Shridhar Date: Sun, 4 Sep 2016 20:54:58 +0530 Subject: fs/afs/flock: Remove deprecated create_singlethread_workqueue The workqueue "afs_lock_manager" queues work item &vnode->lock_work, per vnode. Since there can be multiple vnodes and since their work items can be executed concurrently, alloc_workqueue has been used to replace the deprecated create_singlethread_workqueue instance. The WQ_MEM_RECLAIM flag has been set to ensure forward progress under memory pressure because the workqueue is being used on a memory reclaim path. Since there are fixed number of work items, explicit concurrency limit is unnecessary here. Signed-off-by: Bhaktipriya Shridhar Signed-off-by: David Howells diff --git a/fs/afs/flock.c b/fs/afs/flock.c index d91a9c9..3191dff 100644 --- a/fs/afs/flock.c +++ b/fs/afs/flock.c @@ -36,8 +36,8 @@ static int afs_init_lock_manager(void) if (!afs_lock_manager) { mutex_lock(&afs_lock_manager_mutex); if (!afs_lock_manager) { - afs_lock_manager = - create_singlethread_workqueue("kafs_lockd"); + afs_lock_manager = alloc_workqueue("kafs_lockd", + WQ_MEM_RECLAIM, 0); if (!afs_lock_manager) ret = -ENOMEM; } -- cgit v0.10.2