summaryrefslogtreecommitdiff
path: root/net
diff options
context:
space:
mode:
authorAlex Elder <elder@inktank.com>2013-05-22 20:54:25 -0500
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2014-01-09 12:24:24 -0800
commit1d02ec7ffd7577d07c91d2cbb391386d9a129fda (patch)
tree40b1ac9b21a13cf29c5abb5baf6dda23e5cc0816 /net
parentf1b6559cfa8c280249b52112223459b00dcdb6e9 (diff)
downloadlinux-3.10-1d02ec7ffd7577d07c91d2cbb391386d9a129fda.tar.gz
linux-3.10-1d02ec7ffd7577d07c91d2cbb391386d9a129fda.tar.bz2
linux-3.10-1d02ec7ffd7577d07c91d2cbb391386d9a129fda.zip
libceph: add lingering request reference when registered
commit 96e4dac66f69d28af2b736e723364efbbdf9fdee upstream. When an osd request is set to linger, the osd client holds onto the request so it can be re-submitted following certain osd map changes. The osd client holds a reference to the request until it is unregistered. This is used by rbd for watch requests. Currently, the reference is taken when the request is marked with the linger flag. This means that if an error occurs after that time but before the the request completes successfully, that reference is leaked. There's really no reason to take the reference until the request is registered in the the osd client's list of lingering requests, and that only happens when the lingering (watch) request completes successfully. So take that reference only when it gets registered following succesful completion, and drop it (as before) when the request gets unregistered. This avoids the reference problem on error in rbd. Rearrange ceph_osdc_unregister_linger_request() to avoid using the request pointer after it may have been freed. And hold an extra reference in kick_requests() while handling a linger request that has not yet been registered, to ensure it doesn't go away. This resolves: http://tracker.ceph.com/issues/3859 Signed-off-by: Alex Elder <elder@inktank.com> Reviewed-by: Josh Durgin <josh.durgin@inktank.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'net')
-rw-r--r--net/ceph/osd_client.c12
1 files changed, 5 insertions, 7 deletions
diff --git a/net/ceph/osd_client.c b/net/ceph/osd_client.c
index 46ec7672ccc..d127e36b496 100644
--- a/net/ceph/osd_client.c
+++ b/net/ceph/osd_client.c
@@ -1174,6 +1174,7 @@ static void __register_linger_request(struct ceph_osd_client *osdc,
struct ceph_osd_request *req)
{
dout("__register_linger_request %p\n", req);
+ ceph_osdc_get_request(req);
list_add_tail(&req->r_linger_item, &osdc->req_linger);
if (req->r_osd)
list_add_tail(&req->r_linger_osd,
@@ -1196,6 +1197,7 @@ static void __unregister_linger_request(struct ceph_osd_client *osdc,
if (list_empty(&req->r_osd_item))
req->r_osd = NULL;
}
+ ceph_osdc_put_request(req);
}
void ceph_osdc_unregister_linger_request(struct ceph_osd_client *osdc,
@@ -1203,9 +1205,8 @@ void ceph_osdc_unregister_linger_request(struct ceph_osd_client *osdc,
{
mutex_lock(&osdc->request_mutex);
if (req->r_linger) {
- __unregister_linger_request(osdc, req);
req->r_linger = 0;
- ceph_osdc_put_request(req);
+ __unregister_linger_request(osdc, req);
}
mutex_unlock(&osdc->request_mutex);
}
@@ -1217,11 +1218,6 @@ void ceph_osdc_set_request_linger(struct ceph_osd_client *osdc,
if (!req->r_linger) {
dout("set_request_linger %p\n", req);
req->r_linger = 1;
- /*
- * caller is now responsible for calling
- * unregister_linger_request
- */
- ceph_osdc_get_request(req);
}
}
EXPORT_SYMBOL(ceph_osdc_set_request_linger);
@@ -1633,8 +1629,10 @@ static void kick_requests(struct ceph_osd_client *osdc, int force_resend)
dout("%p tid %llu restart on osd%d\n",
req, req->r_tid,
req->r_osd ? req->r_osd->o_osd : -1);
+ ceph_osdc_get_request(req);
__unregister_request(osdc, req);
__register_linger_request(osdc, req);
+ ceph_osdc_put_request(req);
continue;
}