|
@@ -68,8 +68,6 @@
|
|
|
static void xprt_init(struct rpc_xprt *xprt, struct net *net);
|
|
|
static __be32 xprt_alloc_xid(struct rpc_xprt *xprt);
|
|
|
static void xprt_connect_status(struct rpc_task *task);
|
|
|
-static int __xprt_get_cong(struct rpc_xprt *, struct rpc_task *);
|
|
|
-static void __xprt_put_cong(struct rpc_xprt *, struct rpc_rqst *);
|
|
|
static void xprt_destroy(struct rpc_xprt *xprt);
|
|
|
|
|
|
static DEFINE_SPINLOCK(xprt_list_lock);
|
|
@@ -221,6 +219,31 @@ static void xprt_clear_locked(struct rpc_xprt *xprt)
|
|
|
queue_work(xprtiod_workqueue, &xprt->task_cleanup);
|
|
|
}
|
|
|
|
|
|
+static bool
|
|
|
+xprt_need_congestion_window_wait(struct rpc_xprt *xprt)
|
|
|
+{
|
|
|
+ return test_bit(XPRT_CWND_WAIT, &xprt->state);
|
|
|
+}
|
|
|
+
|
|
|
+static void
|
|
|
+xprt_set_congestion_window_wait(struct rpc_xprt *xprt)
|
|
|
+{
|
|
|
+ if (!list_empty(&xprt->xmit_queue)) {
|
|
|
+ /* Peek at head of queue to see if it can make progress */
|
|
|
+ if (list_first_entry(&xprt->xmit_queue, struct rpc_rqst,
|
|
|
+ rq_xmit)->rq_cong)
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ set_bit(XPRT_CWND_WAIT, &xprt->state);
|
|
|
+}
|
|
|
+
|
|
|
+static void
|
|
|
+xprt_test_and_clear_congestion_window_wait(struct rpc_xprt *xprt)
|
|
|
+{
|
|
|
+ if (!RPCXPRT_CONGESTED(xprt))
|
|
|
+ clear_bit(XPRT_CWND_WAIT, &xprt->state);
|
|
|
+}
|
|
|
+
|
|
|
/*
|
|
|
* xprt_reserve_xprt_cong - serialize write access to transports
|
|
|
* @task: task that is requesting access to the transport
|
|
@@ -228,6 +251,7 @@ static void xprt_clear_locked(struct rpc_xprt *xprt)
|
|
|
* Same as xprt_reserve_xprt, but Van Jacobson congestion control is
|
|
|
* integrated into the decision of whether a request is allowed to be
|
|
|
* woken up and given access to the transport.
|
|
|
+ * Note that the lock is only granted if we know there are free slots.
|
|
|
*/
|
|
|
int xprt_reserve_xprt_cong(struct rpc_xprt *xprt, struct rpc_task *task)
|
|
|
{
|
|
@@ -243,14 +267,12 @@ int xprt_reserve_xprt_cong(struct rpc_xprt *xprt, struct rpc_task *task)
|
|
|
xprt->snd_task = task;
|
|
|
return 1;
|
|
|
}
|
|
|
- if (__xprt_get_cong(xprt, task)) {
|
|
|
+ if (!xprt_need_congestion_window_wait(xprt)) {
|
|
|
xprt->snd_task = task;
|
|
|
return 1;
|
|
|
}
|
|
|
xprt_clear_locked(xprt);
|
|
|
out_sleep:
|
|
|
- if (req)
|
|
|
- __xprt_put_cong(xprt, req);
|
|
|
dprintk("RPC: %5u failed to lock transport %p\n", task->tk_pid, xprt);
|
|
|
task->tk_timeout = 0;
|
|
|
task->tk_status = -EAGAIN;
|
|
@@ -294,32 +316,14 @@ static void __xprt_lock_write_next(struct rpc_xprt *xprt)
|
|
|
xprt_clear_locked(xprt);
|
|
|
}
|
|
|
|
|
|
-static bool __xprt_lock_write_cong_func(struct rpc_task *task, void *data)
|
|
|
-{
|
|
|
- struct rpc_xprt *xprt = data;
|
|
|
- struct rpc_rqst *req;
|
|
|
-
|
|
|
- req = task->tk_rqstp;
|
|
|
- if (req == NULL) {
|
|
|
- xprt->snd_task = task;
|
|
|
- return true;
|
|
|
- }
|
|
|
- if (__xprt_get_cong(xprt, task)) {
|
|
|
- xprt->snd_task = task;
|
|
|
- req->rq_ntrans++;
|
|
|
- return true;
|
|
|
- }
|
|
|
- return false;
|
|
|
-}
|
|
|
-
|
|
|
static void __xprt_lock_write_next_cong(struct rpc_xprt *xprt)
|
|
|
{
|
|
|
if (test_and_set_bit(XPRT_LOCKED, &xprt->state))
|
|
|
return;
|
|
|
- if (RPCXPRT_CONGESTED(xprt))
|
|
|
+ if (xprt_need_congestion_window_wait(xprt))
|
|
|
goto out_unlock;
|
|
|
if (rpc_wake_up_first_on_wq(xprtiod_workqueue, &xprt->sending,
|
|
|
- __xprt_lock_write_cong_func, xprt))
|
|
|
+ __xprt_lock_write_func, xprt))
|
|
|
return;
|
|
|
out_unlock:
|
|
|
xprt_clear_locked(xprt);
|
|
@@ -370,16 +374,16 @@ static inline void xprt_release_write(struct rpc_xprt *xprt, struct rpc_task *ta
|
|
|
* overflowed. Put the task to sleep if this is the case.
|
|
|
*/
|
|
|
static int
|
|
|
-__xprt_get_cong(struct rpc_xprt *xprt, struct rpc_task *task)
|
|
|
+__xprt_get_cong(struct rpc_xprt *xprt, struct rpc_rqst *req)
|
|
|
{
|
|
|
- struct rpc_rqst *req = task->tk_rqstp;
|
|
|
-
|
|
|
if (req->rq_cong)
|
|
|
return 1;
|
|
|
dprintk("RPC: %5u xprt_cwnd_limited cong = %lu cwnd = %lu\n",
|
|
|
- task->tk_pid, xprt->cong, xprt->cwnd);
|
|
|
- if (RPCXPRT_CONGESTED(xprt))
|
|
|
+ req->rq_task->tk_pid, xprt->cong, xprt->cwnd);
|
|
|
+ if (RPCXPRT_CONGESTED(xprt)) {
|
|
|
+ xprt_set_congestion_window_wait(xprt);
|
|
|
return 0;
|
|
|
+ }
|
|
|
req->rq_cong = 1;
|
|
|
xprt->cong += RPC_CWNDSCALE;
|
|
|
return 1;
|
|
@@ -396,9 +400,31 @@ __xprt_put_cong(struct rpc_xprt *xprt, struct rpc_rqst *req)
|
|
|
return;
|
|
|
req->rq_cong = 0;
|
|
|
xprt->cong -= RPC_CWNDSCALE;
|
|
|
+ xprt_test_and_clear_congestion_window_wait(xprt);
|
|
|
__xprt_lock_write_next_cong(xprt);
|
|
|
}
|
|
|
|
|
|
+/**
|
|
|
+ * xprt_request_get_cong - Request congestion control credits
|
|
|
+ * @xprt: pointer to transport
|
|
|
+ * @req: pointer to RPC request
|
|
|
+ *
|
|
|
+ * Useful for transports that require congestion control.
|
|
|
+ */
|
|
|
+bool
|
|
|
+xprt_request_get_cong(struct rpc_xprt *xprt, struct rpc_rqst *req)
|
|
|
+{
|
|
|
+ bool ret = false;
|
|
|
+
|
|
|
+ if (req->rq_cong)
|
|
|
+ return true;
|
|
|
+ spin_lock_bh(&xprt->transport_lock);
|
|
|
+ ret = __xprt_get_cong(xprt, req) != 0;
|
|
|
+ spin_unlock_bh(&xprt->transport_lock);
|
|
|
+ return ret;
|
|
|
+}
|
|
|
+EXPORT_SYMBOL_GPL(xprt_request_get_cong);
|
|
|
+
|
|
|
/**
|
|
|
* xprt_release_rqst_cong - housekeeping when request is complete
|
|
|
* @task: RPC request that recently completed
|
|
@@ -413,6 +439,20 @@ void xprt_release_rqst_cong(struct rpc_task *task)
|
|
|
}
|
|
|
EXPORT_SYMBOL_GPL(xprt_release_rqst_cong);
|
|
|
|
|
|
+/*
|
|
|
+ * Clear the congestion window wait flag and wake up the next
|
|
|
+ * entry on xprt->sending
|
|
|
+ */
|
|
|
+static void
|
|
|
+xprt_clear_congestion_window_wait(struct rpc_xprt *xprt)
|
|
|
+{
|
|
|
+ if (test_and_clear_bit(XPRT_CWND_WAIT, &xprt->state)) {
|
|
|
+ spin_lock_bh(&xprt->transport_lock);
|
|
|
+ __xprt_lock_write_next_cong(xprt);
|
|
|
+ spin_unlock_bh(&xprt->transport_lock);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
/**
|
|
|
* xprt_adjust_cwnd - adjust transport congestion window
|
|
|
* @xprt: pointer to xprt
|
|
@@ -1058,12 +1098,28 @@ xprt_request_enqueue_transmit(struct rpc_task *task)
|
|
|
|
|
|
if (xprt_request_need_enqueue_transmit(task, req)) {
|
|
|
spin_lock(&xprt->queue_lock);
|
|
|
- list_for_each_entry(pos, &xprt->xmit_queue, rq_xmit) {
|
|
|
- if (pos->rq_task->tk_owner != task->tk_owner)
|
|
|
- continue;
|
|
|
- list_add_tail(&req->rq_xmit2, &pos->rq_xmit2);
|
|
|
- INIT_LIST_HEAD(&req->rq_xmit);
|
|
|
- goto out;
|
|
|
+ /*
|
|
|
+ * Requests that carry congestion control credits are added
|
|
|
+ * to the head of the list to avoid starvation issues.
|
|
|
+ */
|
|
|
+ if (req->rq_cong) {
|
|
|
+ xprt_clear_congestion_window_wait(xprt);
|
|
|
+ list_for_each_entry(pos, &xprt->xmit_queue, rq_xmit) {
|
|
|
+ if (pos->rq_cong)
|
|
|
+ continue;
|
|
|
+ /* Note: req is added _before_ pos */
|
|
|
+ list_add_tail(&req->rq_xmit, &pos->rq_xmit);
|
|
|
+ INIT_LIST_HEAD(&req->rq_xmit2);
|
|
|
+ goto out;
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ list_for_each_entry(pos, &xprt->xmit_queue, rq_xmit) {
|
|
|
+ if (pos->rq_task->tk_owner != task->tk_owner)
|
|
|
+ continue;
|
|
|
+ list_add_tail(&req->rq_xmit2, &pos->rq_xmit2);
|
|
|
+ INIT_LIST_HEAD(&req->rq_xmit);
|
|
|
+ goto out;
|
|
|
+ }
|
|
|
}
|
|
|
list_add_tail(&req->rq_xmit, &xprt->xmit_queue);
|
|
|
INIT_LIST_HEAD(&req->rq_xmit2);
|