History

libceph: fix potential use-after-free on linger ping and resends

request_reinit() is not only ugly as the comment rightfully suggests, but also unsafe. Even though it is called with osdc->lock held for write in all cases, resetting the OSD request refcount can still race with handle_reply() and result in use-after-free. Taking linger ping as an example: handle_timeout thread handle_reply thread down_read(&osdc->lock) req = lookup_request(...) ... finish_request(req) # unregisters up_read(&osdc->lock) __complete_request(req) linger_ping_cb(req) # req->r_kref == 2 because handle_reply still holds its ref down_write(&osdc->lock) send_linger_ping(lreq) req = lreq->ping_req # same req # cancel_linger_request is NOT # called - handle_reply already # unregistered request_reinit(req) WARN_ON(req->r_kref != 1) # fires request_init(req) kref_init(req->r_kref) # req->r_kref == 1 after kref_init ceph_osdc_put_request(req) kref_put(req->r_kref) # req->r_kref == 0 after kref_put, req is freed <further req initialization/use> !!! This happens because send_linger_ping() always (re)uses the same OSD request for watch ping requests, relying on cancel_linger_request() to unregister it from the OSD client and rip its messages out from the messenger. send_linger() does the same for watch/notify registration and watch reconnect requests. Unfortunately cancel_request() doesn't guarantee that after it returns the OSD client would be completely done with the OSD request -- a ref could still be held and the callback (if specified) could still be invoked too. The original motivation for request_reinit() was inability to deal with allocation failures in send_linger() and send_linger_ping(). Switching to using osdc->req_mempool (currently only used by CephFS) respects that and allows us to get rid of request_reinit().

Ilya Dryomov committed 4 years ago

Commit 75dbb681 parent 42226c92 files · +122 −183Browse files

Unified · Split

2 files
  1. include/linux/ceph/osd_client.h +3 −0
  2. net/ceph/osd_client.c +119 −183
include/linux/ceph/osd_client.h+3−0
@@ -287,6 +287,9 @@ struct ceph_osd_linger_request { · 286 unmodified lines
287 rados_watcherrcb_t errcb;287 rados_watcherrcb_t errcb;
288 void *data;288 void *data;
289 289
290 struct ceph_pagelist *request_pl;
291 struct page **notify_id_pages;
292
290 struct page ***preply_pages;293 struct page ***preply_pages;
291 size_t *preply_len;294 size_t *preply_len;
292};295};
net/ceph/osd_client.c+119−183
@@ -537,43 +537,6 @@ static void request_init(struct ceph_osd_request *req) · 536 unmodified lines
537 target_init(&req->r_t);537 target_init(&req->r_t);
538}538}
539 539
540/*
541 * This is ugly, but it allows us to reuse linger registration and ping
542 * requests, keeping the structure of the code around send_linger{_ping}()
543 * reasonable. Setting up a min_nr=2 mempool for each linger request
544 * and dealing with copying ops (this blasts req only, watch op remains
545 * intact) isn't any better.
546 */
547static void request_reinit(struct ceph_osd_request *req)
548{
549 struct ceph_osd_client *osdc = req->r_osdc;
550 bool mempool = req->r_mempool;
551 unsigned int num_ops = req->r_num_ops;
552 u64 snapid = req->r_snapid;
553 struct ceph_snap_context *snapc = req->r_snapc;
554 bool linger = req->r_linger;
555 struct ceph_msg *request_msg = req->r_request;
556 struct ceph_msg *reply_msg = req->r_reply;
557
558 dout("%s req %p\n", __func__, req);
559 WARN_ON(kref_read(&req->r_kref) != 1);
560 request_release_checks(req);
561
562 WARN_ON(kref_read(&request_msg->kref) != 1);
563 WARN_ON(kref_read(&reply_msg->kref) != 1);
564 target_destroy(&req->r_t);
565
566 request_init(req);
567 req->r_osdc = osdc;
568 req->r_mempool = mempool;
569 req->r_num_ops = num_ops;
570 req->r_snapid = snapid;
571 req->r_snapc = snapc;
572 req->r_linger = linger;
573 req->r_request = request_msg;
574 req->r_reply = reply_msg;
575}
576
577struct ceph_osd_request *ceph_osdc_alloc_request(struct ceph_osd_client *osdc,540struct ceph_osd_request *ceph_osdc_alloc_request(struct ceph_osd_client *osdc,
578 struct ceph_snap_context *snapc,541 struct ceph_snap_context *snapc,
579 unsigned int num_ops,542 unsigned int num_ops,
@@ -918,14 +881,30 @@ EXPORT_SYMBOL(osd_req_op_xattr_init); · 338 unmodified lines
918 * @watch_opcode: CEPH_OSD_WATCH_OP_*881 * @watch_opcode: CEPH_OSD_WATCH_OP_*
919 */882 */
920static void osd_req_op_watch_init(struct ceph_osd_request *req, int which,883static void osd_req_op_watch_init(struct ceph_osd_request *req, int which,
921 u64 cookie, u8 watch_opcode)884 u8 watch_opcode, u64 cookie, u32 gen)
922{885{
923 struct ceph_osd_req_op *op;886 struct ceph_osd_req_op *op;
924 887
925 op = osd_req_op_init(req, which, CEPH_OSD_OP_WATCH, 0);888 op = osd_req_op_init(req, which, CEPH_OSD_OP_WATCH, 0);
926 op->watch.cookie = cookie;889 op->watch.cookie = cookie;
927 op->watch.op = watch_opcode;890 op->watch.op = watch_opcode;
928 op->watch.gen = 0;891 op->watch.gen = gen;
892}
893
894/*
895 * prot_ver, timeout and notify payload (may be empty) should already be
896 * encoded in @request_pl
897 */
898static void osd_req_op_notify_init(struct ceph_osd_request *req, int which,
899 u64 cookie, struct ceph_pagelist *request_pl)
900{
901 struct ceph_osd_req_op *op;
902
903 op = osd_req_op_init(req, which, CEPH_OSD_OP_NOTIFY, 0);
904 op->notify.cookie = cookie;
905
906 ceph_osd_data_pagelist_init(&op->notify.request_data, request_pl);
907 op->indata_len = request_pl->length;
929}908}
930 909
931/*910/*
@@ -2731,10 +2710,13 @@ static void linger_release(struct kref *kref) · 1799 unmodified lines
2731 WARN_ON(!list_empty(&lreq->pending_lworks));2710 WARN_ON(!list_empty(&lreq->pending_lworks));
2732 WARN_ON(lreq->osd);2711 WARN_ON(lreq->osd);
2733 2712
2734 if (lreq->reg_req)2713 if (lreq->request_pl)
2735 ceph_osdc_put_request(lreq->reg_req);2714 ceph_pagelist_release(lreq->request_pl);
2736 if (lreq->ping_req)2715 if (lreq->notify_id_pages)
2737 ceph_osdc_put_request(lreq->ping_req);2716 ceph_release_page_vector(lreq->notify_id_pages, 1);
2717
2718 ceph_osdc_put_request(lreq->reg_req);
2719 ceph_osdc_put_request(lreq->ping_req);
2738 target_destroy(&lreq->t);2720 target_destroy(&lreq->t);
2739 kfree(lreq);2721 kfree(lreq);
2740}2722}
@@ -3003,6 +2985,12 @@ static void linger_commit_cb(struct ceph_osd_request *req) · 262 unmodified lines
3003 struct ceph_osd_linger_request *lreq = req->r_priv;2985 struct ceph_osd_linger_request *lreq = req->r_priv;
3004 2986
3005 mutex_lock(&lreq->lock);2987 mutex_lock(&lreq->lock);
2988 if (req != lreq->reg_req) {
2989 dout("%s lreq %p linger_id %llu unknown req (%p != %p)\n",
2990 __func__, lreq, lreq->linger_id, req, lreq->reg_req);
2991 goto out;
2992 }
2993
3006 dout("%s lreq %p linger_id %llu result %d\n", __func__, lreq,2994 dout("%s lreq %p linger_id %llu result %d\n", __func__, lreq,
3007 lreq->linger_id, req->r_result);2995 lreq->linger_id, req->r_result);
3008 linger_reg_commit_complete(lreq, req->r_result);2996 linger_reg_commit_complete(lreq, req->r_result);
@@ -3026,6 +3014,7 @@ static void linger_commit_cb(struct ceph_osd_request *req) · 17 unmodified lines
3026 }3014 }
3027 }3015 }
3028 3016
3017out:
3029 mutex_unlock(&lreq->lock);3018 mutex_unlock(&lreq->lock);
3030 linger_put(lreq);3019 linger_put(lreq);
3031}3020}
@@ -3048,6 +3037,12 @@ static void linger_reconnect_cb(struct ceph_osd_request *req) · 16 unmodified lines
3048 struct ceph_osd_linger_request *lreq = req->r_priv;3037 struct ceph_osd_linger_request *lreq = req->r_priv;
3049 3038
3050 mutex_lock(&lreq->lock);3039 mutex_lock(&lreq->lock);
3040 if (req != lreq->reg_req) {
3041 dout("%s lreq %p linger_id %llu unknown req (%p != %p)\n",
3042 __func__, lreq, lreq->linger_id, req, lreq->reg_req);
3043 goto out;
3044 }
3045
3051 dout("%s lreq %p linger_id %llu result %d last_error %d\n", __func__,3046 dout("%s lreq %p linger_id %llu result %d last_error %d\n", __func__,
3052 lreq, lreq->linger_id, req->r_result, lreq->last_error);3047 lreq, lreq->linger_id, req->r_result, lreq->last_error);
3053 if (req->r_result < 0) {3048 if (req->r_result < 0) {
@@ -3057,46 +3052,64 @@ static void linger_reconnect_cb(struct ceph_osd_request *req) · 3 unmodified lines
3057 }3052 }
3058 }3053 }
3059 3054
3055out:
3060 mutex_unlock(&lreq->lock);3056 mutex_unlock(&lreq->lock);
3061 linger_put(lreq);3057 linger_put(lreq);
3062}3058}
3063 3059
3064static void send_linger(struct ceph_osd_linger_request *lreq)3060static void send_linger(struct ceph_osd_linger_request *lreq)
3065{3061{
3066 struct ceph_osd_request *req = lreq->reg_req;3062 struct ceph_osd_client *osdc = lreq->osdc;
3067 struct ceph_osd_req_op *op = &req->r_ops[0];3063 struct ceph_osd_request *req;
3064 int ret;
3068 3065
3069 verify_osdc_wrlocked(req->r_osdc);3066 verify_osdc_wrlocked(osdc);
3067 mutex_lock(&lreq->lock);
3070 dout("%s lreq %p linger_id %llu\n", __func__, lreq, lreq->linger_id);3068 dout("%s lreq %p linger_id %llu\n", __func__, lreq, lreq->linger_id);
3071 3069
3072 if (req->r_osd)3070 if (lreq->reg_req) {
3073 cancel_linger_request(req);3071 if (lreq->reg_req->r_osd)
3072 cancel_linger_request(lreq->reg_req);
3073 ceph_osdc_put_request(lreq->reg_req);
3074 }
3075
3076 req = ceph_osdc_alloc_request(osdc, NULL, 1, true, GFP_NOIO);
3077 BUG_ON(!req);
3074 3078
3075 request_reinit(req);
3076 target_copy(&req->r_t, &lreq->t);3079 target_copy(&req->r_t, &lreq->t);
3077 req->r_mtime = lreq->mtime;3080 req->r_mtime = lreq->mtime;
3078 3081
3079 mutex_lock(&lreq->lock);
3080 if (lreq->is_watch && lreq->committed) {3082 if (lreq->is_watch && lreq->committed) {
3081 WARN_ON(op->op != CEPH_OSD_OP_WATCH ||3083 osd_req_op_watch_init(req, 0, CEPH_OSD_WATCH_OP_RECONNECT,
3082 op->watch.cookie != lreq->linger_id);3084 lreq->linger_id, ++lreq->register_gen);
3083 op->watch.op = CEPH_OSD_WATCH_OP_RECONNECT;
3084 op->watch.gen = ++lreq->register_gen;
3085 dout("lreq %p reconnect register_gen %u\n", lreq,3085 dout("lreq %p reconnect register_gen %u\n", lreq,
3086 op->watch.gen);3086 req->r_ops[0].watch.gen);
3087 req->r_callback = linger_reconnect_cb;3087 req->r_callback = linger_reconnect_cb;
3088 } else {3088 } else {
3089 if (!lreq->is_watch)3089 if (lreq->is_watch) {
3090 osd_req_op_watch_init(req, 0, CEPH_OSD_WATCH_OP_WATCH,
3091 lreq->linger_id, 0);
3092 } else {
3090 lreq->notify_id = 0;3093 lreq->notify_id = 0;
3091 else3094
3092 WARN_ON(op->watch.op != CEPH_OSD_WATCH_OP_WATCH);3095 refcount_inc(&lreq->request_pl->refcnt);
3096 osd_req_op_notify_init(req, 0, lreq->linger_id,
3097 lreq->request_pl);
3098 ceph_osd_data_pages_init(
3099 osd_req_op_data(req, 0, notify, response_data),
3100 lreq->notify_id_pages, PAGE_SIZE, 0, false, false);
3101 }
3093 dout("lreq %p register\n", lreq);3102 dout("lreq %p register\n", lreq);
3094 req->r_callback = linger_commit_cb;3103 req->r_callback = linger_commit_cb;
3095 }3104 }
3096 mutex_unlock(&lreq->lock);3105
3106 ret = ceph_osdc_alloc_messages(req, GFP_NOIO);
3107 BUG_ON(ret);
3097 3108
3098 req->r_priv = linger_get(lreq);3109 req->r_priv = linger_get(lreq);
3099 req->r_linger = true;3110 req->r_linger = true;
3111 lreq->reg_req = req;
3112 mutex_unlock(&lreq->lock);
3100 3113
3101 submit_request(req, true);3114 submit_request(req, true);
3102}3115}
@@ -3106,6 +3119,12 @@ static void linger_ping_cb(struct ceph_osd_request *req) · 3 unmodified lines
3106 struct ceph_osd_linger_request *lreq = req->r_priv;3119 struct ceph_osd_linger_request *lreq = req->r_priv;
3107 3120
3108 mutex_lock(&lreq->lock);3121 mutex_lock(&lreq->lock);
3122 if (req != lreq->ping_req) {
3123 dout("%s lreq %p linger_id %llu unknown req (%p != %p)\n",
3124 __func__, lreq, lreq->linger_id, req, lreq->ping_req);
3125 goto out;
3126 }
3127
3109 dout("%s lreq %p linger_id %llu result %d ping_sent %lu last_error %d\n",3128 dout("%s lreq %p linger_id %llu result %d ping_sent %lu last_error %d\n",
3110 __func__, lreq, lreq->linger_id, req->r_result, lreq->ping_sent,3129 __func__, lreq, lreq->linger_id, req->r_result, lreq->ping_sent,
3111 lreq->last_error);3130 lreq->last_error);
@@ -3121,6 +3140,7 @@ static void linger_ping_cb(struct ceph_osd_request *req) · 9 unmodified lines
3121 lreq->register_gen, req->r_ops[0].watch.gen);3140 lreq->register_gen, req->r_ops[0].watch.gen);
3122 }3141 }
3123 3142
3143out:
3124 mutex_unlock(&lreq->lock);3144 mutex_unlock(&lreq->lock);
3125 linger_put(lreq);3145 linger_put(lreq);
3126}3146}
@@ -3128,8 +3148,8 @@ static void linger_ping_cb(struct ceph_osd_request *req) · 1 unmodified line
3128static void send_linger_ping(struct ceph_osd_linger_request *lreq)3148static void send_linger_ping(struct ceph_osd_linger_request *lreq)
3129{3149{
3130 struct ceph_osd_client *osdc = lreq->osdc;3150 struct ceph_osd_client *osdc = lreq->osdc;
3131 struct ceph_osd_request *req = lreq->ping_req;3151 struct ceph_osd_request *req;
3132 struct ceph_osd_req_op *op = &req->r_ops[0];3152 int ret;
3133 3153
3134 if (ceph_osdmap_flag(osdc, CEPH_OSDMAP_PAUSERD)) {3154 if (ceph_osdmap_flag(osdc, CEPH_OSDMAP_PAUSERD)) {
3135 dout("%s PAUSERD\n", __func__);3155 dout("%s PAUSERD\n", __func__);
@@ -3141,19 +3161,26 @@ static void send_linger_ping(struct ceph_osd_linger_request *lreq) · 5 unmodified lines
3141 __func__, lreq, lreq->linger_id, lreq->ping_sent,3161 __func__, lreq, lreq->linger_id, lreq->ping_sent,
3142 lreq->register_gen);3162 lreq->register_gen);
3143 3163
3144 if (req->r_osd)3164 if (lreq->ping_req) {
3145 cancel_linger_request(req);3165 if (lreq->ping_req->r_osd)
3166 cancel_linger_request(lreq->ping_req);
3167 ceph_osdc_put_request(lreq->ping_req);
3168 }
3146 3169
3147 request_reinit(req);3170 req = ceph_osdc_alloc_request(osdc, NULL, 1, true, GFP_NOIO);
3148 target_copy(&req->r_t, &lreq->t);3171 BUG_ON(!req);
3149 3172
3150 WARN_ON(op->op != CEPH_OSD_OP_WATCH ||3173 target_copy(&req->r_t, &lreq->t);
3151 op->watch.cookie != lreq->linger_id ||3174 osd_req_op_watch_init(req, 0, CEPH_OSD_WATCH_OP_PING, lreq->linger_id,
3152 op->watch.op != CEPH_OSD_WATCH_OP_PING);3175 lreq->register_gen);
3153 op->watch.gen = lreq->register_gen;
3154 req->r_callback = linger_ping_cb;3176 req->r_callback = linger_ping_cb;
3177
3178 ret = ceph_osdc_alloc_messages(req, GFP_NOIO);
3179 BUG_ON(ret);
3180
3155 req->r_priv = linger_get(lreq);3181 req->r_priv = linger_get(lreq);
3156 req->r_linger = true;3182 req->r_linger = true;
3183 lreq->ping_req = req;
3157 3184
3158 ceph_osdc_get_request(req);3185 ceph_osdc_get_request(req);
3159 account_request(req);3186 account_request(req);
@@ -3169,12 +3196,6 @@ static void linger_submit(struct ceph_osd_linger_request *lreq) · 9 unmodified lines
3169 3196
3170 down_write(&osdc->lock);3197 down_write(&osdc->lock);
3171 linger_register(lreq);3198 linger_register(lreq);
3172 if (lreq->is_watch) {
3173 lreq->reg_req->r_ops[0].watch.cookie = lreq->linger_id;
3174 lreq->ping_req->r_ops[0].watch.cookie = lreq->linger_id;
3175 } else {
3176 lreq->reg_req->r_ops[0].notify.cookie = lreq->linger_id;
3177 }
3178 3199
3179 calc_target(osdc, &lreq->t, false);3200 calc_target(osdc, &lreq->t, false);
3180 osd = lookup_create_osd(osdc, lreq->t.osd, true);3201 osd = lookup_create_osd(osdc, lreq->t.osd, true);
@@ -3206,9 +3227,9 @@ static void cancel_linger_map_check(struct ceph_osd_linger_request *lreq) · 25 unmodified lines
3206 */3227 */
3207static void __linger_cancel(struct ceph_osd_linger_request *lreq)3228static void __linger_cancel(struct ceph_osd_linger_request *lreq)
3208{3229{
3209 if (lreq->is_watch && lreq->ping_req->r_osd)3230 if (lreq->ping_req && lreq->ping_req->r_osd)
3210 cancel_linger_request(lreq->ping_req);3231 cancel_linger_request(lreq->ping_req);
3211 if (lreq->reg_req->r_osd)3232 if (lreq->reg_req && lreq->reg_req->r_osd)
3212 cancel_linger_request(lreq->reg_req);3233 cancel_linger_request(lreq->reg_req);
3213 cancel_linger_map_check(lreq);3234 cancel_linger_map_check(lreq);
3214 unlink_linger(lreq->osd, lreq);3235 unlink_linger(lreq->osd, lreq);
@@ -4657,43 +4678,6 @@ again: · 1442 unmodified lines
4657}4678}
4658EXPORT_SYMBOL(ceph_osdc_sync);4679EXPORT_SYMBOL(ceph_osdc_sync);
4659 4680
4660static struct ceph_osd_request *
4661alloc_linger_request(struct ceph_osd_linger_request *lreq)
4662{
4663 struct ceph_osd_request *req;
4664
4665 req = ceph_osdc_alloc_request(lreq->osdc, NULL, 1, false, GFP_NOIO);
4666 if (!req)
4667 return NULL;
4668
4669 ceph_oid_copy(&req->r_base_oid, &lreq->t.base_oid);
4670 ceph_oloc_copy(&req->r_base_oloc, &lreq->t.base_oloc);
4671 return req;
4672}
4673
4674static struct ceph_osd_request *
4675alloc_watch_request(struct ceph_osd_linger_request *lreq, u8 watch_opcode)
4676{
4677 struct ceph_osd_request *req;
4678
4679 req = alloc_linger_request(lreq);
4680 if (!req)
4681 return NULL;
4682
4683 /*
4684 * Pass 0 for cookie because we don't know it yet, it will be
4685 * filled in by linger_submit().
4686 */
4687 osd_req_op_watch_init(req, 0, 0, watch_opcode);
4688
4689 if (ceph_osdc_alloc_messages(req, GFP_NOIO)) {
4690 ceph_osdc_put_request(req);
4691 return NULL;
4692 }
4693
4694 return req;
4695}
4696
4697/*4681/*
4698 * Returns a handle, caller owns a ref.4682 * Returns a handle, caller owns a ref.
4699 */4683 */
@@ -4723,18 +4707,6 @@ ceph_osdc_watch(struct ceph_osd_client *osdc, · 23 unmodified lines
4723 lreq->t.flags = CEPH_OSD_FLAG_WRITE;4707 lreq->t.flags = CEPH_OSD_FLAG_WRITE;
4724 ktime_get_real_ts64(&lreq->mtime);4708 ktime_get_real_ts64(&lreq->mtime);
4725 4709
4726 lreq->reg_req = alloc_watch_request(lreq, CEPH_OSD_WATCH_OP_WATCH);
4727 if (!lreq->reg_req) {
4728 ret = -ENOMEM;
4729 goto err_put_lreq;
4730 }
4731
4732 lreq->ping_req = alloc_watch_request(lreq, CEPH_OSD_WATCH_OP_PING);
4733 if (!lreq->ping_req) {
4734 ret = -ENOMEM;
4735 goto err_put_lreq;
4736 }
4737
4738 linger_submit(lreq);4710 linger_submit(lreq);
4739 ret = linger_reg_commit_wait(lreq);4711 ret = linger_reg_commit_wait(lreq);
4740 if (ret) {4712 if (ret) {
@@ -4772,8 +4744,8 @@ int ceph_osdc_unwatch(struct ceph_osd_client *osdc, · 31 unmodified lines
4772 ceph_oloc_copy(&req->r_base_oloc, &lreq->t.base_oloc);4744 ceph_oloc_copy(&req->r_base_oloc, &lreq->t.base_oloc);
4773 req->r_flags = CEPH_OSD_FLAG_WRITE;4745 req->r_flags = CEPH_OSD_FLAG_WRITE;
4774 ktime_get_real_ts64(&req->r_mtime);4746 ktime_get_real_ts64(&req->r_mtime);
4775 osd_req_op_watch_init(req, 0, lreq->linger_id,4747 osd_req_op_watch_init(req, 0, CEPH_OSD_WATCH_OP_UNWATCH,
4776 CEPH_OSD_WATCH_OP_UNWATCH);4748 lreq->linger_id, 0);
4777 4749
4778 ret = ceph_osdc_alloc_messages(req, GFP_NOIO);4750 ret = ceph_osdc_alloc_messages(req, GFP_NOIO);
4779 if (ret)4751 if (ret)
@@ -4859,35 +4831,6 @@ out_put_req: · 79 unmodified lines
4859}4831}
4860EXPORT_SYMBOL(ceph_osdc_notify_ack);4832EXPORT_SYMBOL(ceph_osdc_notify_ack);
4861 4833
4862static int osd_req_op_notify_init(struct ceph_osd_request *req, int which,
4863 u64 cookie, u32 prot_ver, u32 timeout,
4864 void *payload, u32 payload_len)
4865{
4866 struct ceph_osd_req_op *op;
4867 struct ceph_pagelist *pl;
4868 int ret;
4869
4870 op = osd_req_op_init(req, which, CEPH_OSD_OP_NOTIFY, 0);
4871 op->notify.cookie = cookie;
4872
4873 pl = ceph_pagelist_alloc(GFP_NOIO);
4874 if (!pl)
4875 return -ENOMEM;
4876
4877 ret = ceph_pagelist_encode_32(pl, 1); /* prot_ver */
4878 ret |= ceph_pagelist_encode_32(pl, timeout);
4879 ret |= ceph_pagelist_encode_32(pl, payload_len);
4880 ret |= ceph_pagelist_append(pl, payload, payload_len);
4881 if (ret) {
4882 ceph_pagelist_release(pl);
4883 return -ENOMEM;
4884 }
4885
4886 ceph_osd_data_pagelist_init(&op->notify.request_data, pl);
4887 op->indata_len = pl->length;
4888 return 0;
4889}
4890
4891/*4834/*
4892 * @timeout: in seconds4835 * @timeout: in seconds
4893 *4836 *
@@ -4906,7 +4849,6 @@ int ceph_osdc_notify(struct ceph_osd_client *osdc, · 12 unmodified lines
4906 size_t *preply_len)4849 size_t *preply_len)
4907{4850{
4908 struct ceph_osd_linger_request *lreq;4851 struct ceph_osd_linger_request *lreq;
4909 struct page **pages;
4910 int ret;4852 int ret;
4911 4853
4912 WARN_ON(!timeout);4854 WARN_ON(!timeout);
@@ -4919,41 +4861,35 @@ int ceph_osdc_notify(struct ceph_osd_client *osdc, · 6 unmodified lines
4919 if (!lreq)4861 if (!lreq)
4920 return -ENOMEM;4862 return -ENOMEM;
4921 4863
4922 lreq->preply_pages = preply_pages;4864 lreq->request_pl = ceph_pagelist_alloc(GFP_NOIO);
4923 lreq->preply_len = preply_len;4865 if (!lreq->request_pl) {
4924
4925 ceph_oid_copy(&lreq->t.base_oid, oid);
4926 ceph_oloc_copy(&lreq->t.base_oloc, oloc);
4927 lreq->t.flags = CEPH_OSD_FLAG_READ;
4928
4929 lreq->reg_req = alloc_linger_request(lreq);
4930 if (!lreq->reg_req) {
4931 ret = -ENOMEM;4866 ret = -ENOMEM;
4932 goto out_put_lreq;4867 goto out_put_lreq;
4933 }4868 }
4934 4869
4935 /*4870 ret = ceph_pagelist_encode_32(lreq->request_pl, 1); /* prot_ver */
4936 * Pass 0 for cookie because we don't know it yet, it will be4871 ret |= ceph_pagelist_encode_32(lreq->request_pl, timeout);
4937 * filled in by linger_submit().4872 ret |= ceph_pagelist_encode_32(lreq->request_pl, payload_len);
4938 */4873 ret |= ceph_pagelist_append(lreq->request_pl, payload, payload_len);
4939 ret = osd_req_op_notify_init(lreq->reg_req, 0, 0, 1, timeout,4874 if (ret) {
4940 payload, payload_len);4875 ret = -ENOMEM;
4941 if (ret)
4942 goto out_put_lreq;4876 goto out_put_lreq;
4877 }
4943 4878
4944 /* for notify_id */4879 /* for notify_id */
4945 pages = ceph_alloc_page_vector(1, GFP_NOIO);4880 lreq->notify_id_pages = ceph_alloc_page_vector(1, GFP_NOIO);
4946 if (IS_ERR(pages)) {4881 if (IS_ERR(lreq->notify_id_pages)) {
4947 ret = PTR_ERR(pages);4882 ret = PTR_ERR(lreq->notify_id_pages);
4883 lreq->notify_id_pages = NULL;
4948 goto out_put_lreq;4884 goto out_put_lreq;
4949 }4885 }
4950 ceph_osd_data_pages_init(osd_req_op_data(lreq->reg_req, 0, notify,
4951 response_data),
4952 pages, PAGE_SIZE, 0, false, true);
4953 4886
4954 ret = ceph_osdc_alloc_messages(lreq->reg_req, GFP_NOIO);4887 lreq->preply_pages = preply_pages;
4955 if (ret)4888 lreq->preply_len = preply_len;
4956 goto out_put_lreq;4889
4890 ceph_oid_copy(&lreq->t.base_oid, oid);
4891 ceph_oloc_copy(&lreq->t.base_oloc, oloc);
4892 lreq->t.flags = CEPH_OSD_FLAG_READ;
4957 4893
4958 linger_submit(lreq);4894 linger_submit(lreq);
4959 ret = linger_reg_commit_wait(lreq);4895 ret = linger_reg_commit_wait(lreq);