aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorIlya Dryomov <ilya.dryomov@inktank.com>2014-05-22 11:28:52 -0400
committerYan, Zheng <zheng.z.yan@intel.com>2014-06-05 21:29:59 -0400
commitb30a01f2a307f55a505762ba09c0440d906c6711 (patch)
tree7f9ff0633c8dfef285ef546117b40867df9be710
parent30ba1f020221991cf239d905c82984958f29bdfe (diff)
rbd: fix osd_request memory leak in __rbd_dev_header_watch_sync()
osd_request, along with r_request and r_reply messages attached to it are leaked in __rbd_dev_header_watch_sync() if the requested image doesn't exist. This is because lingering requests are special and get an extra ref in the reply path. Fix it by unregistering linger request on the error path and split __rbd_dev_header_watch_sync() into two functions to make it maintainable. Signed-off-by: Ilya Dryomov <ilya.dryomov@inktank.com>
-rw-r--r--drivers/block/rbd.c123
1 files changed, 85 insertions, 38 deletions
diff --git a/drivers/block/rbd.c b/drivers/block/rbd.c
index daf7b4659b4a..94747afc2e78 100644
--- a/drivers/block/rbd.c
+++ b/drivers/block/rbd.c
@@ -2872,56 +2872,55 @@ static void rbd_watch_cb(u64 ver, u64 notify_id, u8 opcode, void *data)
2872} 2872}
2873 2873
2874/* 2874/*
2875 * Request sync osd watch/unwatch. The value of "start" determines 2875 * Initiate a watch request, synchronously.
2876 * whether a watch request is being initiated or torn down.
2877 */ 2876 */
2878static int __rbd_dev_header_watch_sync(struct rbd_device *rbd_dev, bool start) 2877static int rbd_dev_header_watch_sync(struct rbd_device *rbd_dev)
2879{ 2878{
2880 struct ceph_osd_client *osdc = &rbd_dev->rbd_client->client->osdc; 2879 struct ceph_osd_client *osdc = &rbd_dev->rbd_client->client->osdc;
2881 struct rbd_obj_request *obj_request; 2880 struct rbd_obj_request *obj_request;
2882 int ret; 2881 int ret;
2883 2882
2884 rbd_assert(start ^ !!rbd_dev->watch_event); 2883 rbd_assert(!rbd_dev->watch_event);
2885 rbd_assert(start ^ !!rbd_dev->watch_request); 2884 rbd_assert(!rbd_dev->watch_request);
2886 2885
2887 if (start) { 2886 ret = ceph_osdc_create_event(osdc, rbd_watch_cb, rbd_dev,
2888 ret = ceph_osdc_create_event(osdc, rbd_watch_cb, rbd_dev, 2887 &rbd_dev->watch_event);
2889 &rbd_dev->watch_event); 2888 if (ret < 0)
2890 if (ret < 0) 2889 return ret;
2891 return ret; 2890
2892 rbd_assert(rbd_dev->watch_event != NULL); 2891 rbd_assert(rbd_dev->watch_event);
2893 }
2894 2892
2895 ret = -ENOMEM;
2896 obj_request = rbd_obj_request_create(rbd_dev->header_name, 0, 0, 2893 obj_request = rbd_obj_request_create(rbd_dev->header_name, 0, 0,
2897 OBJ_REQUEST_NODATA); 2894 OBJ_REQUEST_NODATA);
2898 if (!obj_request) 2895 if (!obj_request) {
2896 ret = -ENOMEM;
2899 goto out_cancel; 2897 goto out_cancel;
2898 }
2900 2899
2901 obj_request->osd_req = rbd_osd_req_create(rbd_dev, true, 1, 2900 obj_request->osd_req = rbd_osd_req_create(rbd_dev, true, 1,
2902 obj_request); 2901 obj_request);
2903 if (!obj_request->osd_req) 2902 if (!obj_request->osd_req) {
2904 goto out_cancel; 2903 ret = -ENOMEM;
2904 goto out_put;
2905 }
2905 2906
2906 if (start) 2907 ceph_osdc_set_request_linger(osdc, obj_request->osd_req);
2907 ceph_osdc_set_request_linger(osdc, obj_request->osd_req);
2908 else
2909 ceph_osdc_unregister_linger_request(osdc,
2910 rbd_dev->watch_request->osd_req);
2911 2908
2912 osd_req_op_watch_init(obj_request->osd_req, 0, CEPH_OSD_OP_WATCH, 2909 osd_req_op_watch_init(obj_request->osd_req, 0, CEPH_OSD_OP_WATCH,
2913 rbd_dev->watch_event->cookie, 0, start ? 1 : 0); 2910 rbd_dev->watch_event->cookie, 0, 1);
2914 rbd_osd_req_format_write(obj_request); 2911 rbd_osd_req_format_write(obj_request);
2915 2912
2916 ret = rbd_obj_request_submit(osdc, obj_request); 2913 ret = rbd_obj_request_submit(osdc, obj_request);
2917 if (ret) 2914 if (ret)
2918 goto out_cancel; 2915 goto out_linger;
2916
2919 ret = rbd_obj_request_wait(obj_request); 2917 ret = rbd_obj_request_wait(obj_request);
2920 if (ret) 2918 if (ret)
2921 goto out_cancel; 2919 goto out_linger;
2920
2922 ret = obj_request->result; 2921 ret = obj_request->result;
2923 if (ret) 2922 if (ret)
2924 goto out_cancel; 2923 goto out_linger;
2925 2924
2926 /* 2925 /*
2927 * A watch request is set to linger, so the underlying osd 2926 * A watch request is set to linger, so the underlying osd
@@ -2931,36 +2930,84 @@ static int __rbd_dev_header_watch_sync(struct rbd_device *rbd_dev, bool start)
2931 * it. We'll drop that reference (below) after we've 2930 * it. We'll drop that reference (below) after we've
2932 * unregistered it. 2931 * unregistered it.
2933 */ 2932 */
2934 if (start) { 2933 rbd_dev->watch_request = obj_request;
2935 rbd_dev->watch_request = obj_request;
2936 2934
2937 return 0; 2935 return 0;
2936
2937out_linger:
2938 ceph_osdc_unregister_linger_request(osdc, obj_request->osd_req);
2939out_put:
2940 rbd_obj_request_put(obj_request);
2941out_cancel:
2942 ceph_osdc_cancel_event(rbd_dev->watch_event);
2943 rbd_dev->watch_event = NULL;
2944
2945 return ret;
2946}
2947
2948/*
2949 * Tear down a watch request, synchronously.
2950 */
2951static int __rbd_dev_header_unwatch_sync(struct rbd_device *rbd_dev)
2952{
2953 struct ceph_osd_client *osdc = &rbd_dev->rbd_client->client->osdc;
2954 struct rbd_obj_request *obj_request;
2955 int ret;
2956
2957 rbd_assert(rbd_dev->watch_event);
2958 rbd_assert(rbd_dev->watch_request);
2959
2960 obj_request = rbd_obj_request_create(rbd_dev->header_name, 0, 0,
2961 OBJ_REQUEST_NODATA);
2962 if (!obj_request) {
2963 ret = -ENOMEM;
2964 goto out_cancel;
2938 } 2965 }
2939 2966
2967 obj_request->osd_req = rbd_osd_req_create(rbd_dev, true, 1,
2968 obj_request);
2969 if (!obj_request->osd_req) {
2970 ret = -ENOMEM;
2971 goto out_put;
2972 }
2973
2974 osd_req_op_watch_init(obj_request->osd_req, 0, CEPH_OSD_OP_WATCH,
2975 rbd_dev->watch_event->cookie, 0, 0);
2976 rbd_osd_req_format_write(obj_request);
2977
2978 ret = rbd_obj_request_submit(osdc, obj_request);
2979 if (ret)
2980 goto out_put;
2981
2982 ret = rbd_obj_request_wait(obj_request);
2983 if (ret)
2984 goto out_put;
2985
2986 ret = obj_request->result;
2987 if (ret)
2988 goto out_put;
2989
2940 /* We have successfully torn down the watch request */ 2990 /* We have successfully torn down the watch request */
2941 2991
2992 ceph_osdc_unregister_linger_request(osdc,
2993 rbd_dev->watch_request->osd_req);
2942 rbd_obj_request_put(rbd_dev->watch_request); 2994 rbd_obj_request_put(rbd_dev->watch_request);
2943 rbd_dev->watch_request = NULL; 2995 rbd_dev->watch_request = NULL;
2996
2997out_put:
2998 rbd_obj_request_put(obj_request);
2944out_cancel: 2999out_cancel:
2945 /* Cancel the event if we're tearing down, or on error */
2946 ceph_osdc_cancel_event(rbd_dev->watch_event); 3000 ceph_osdc_cancel_event(rbd_dev->watch_event);
2947 rbd_dev->watch_event = NULL; 3001 rbd_dev->watch_event = NULL;
2948 if (obj_request)
2949 rbd_obj_request_put(obj_request);
2950 3002
2951 return ret; 3003 return ret;
2952} 3004}
2953 3005
2954static int rbd_dev_header_watch_sync(struct rbd_device *rbd_dev)
2955{
2956 return __rbd_dev_header_watch_sync(rbd_dev, true);
2957}
2958
2959static void rbd_dev_header_unwatch_sync(struct rbd_device *rbd_dev) 3006static void rbd_dev_header_unwatch_sync(struct rbd_device *rbd_dev)
2960{ 3007{
2961 int ret; 3008 int ret;
2962 3009
2963 ret = __rbd_dev_header_watch_sync(rbd_dev, false); 3010 ret = __rbd_dev_header_unwatch_sync(rbd_dev);
2964 if (ret) { 3011 if (ret) {
2965 rbd_warn(rbd_dev, "unable to tear down watch request: %d\n", 3012 rbd_warn(rbd_dev, "unable to tear down watch request: %d\n",
2966 ret); 3013 ret);