Skip to content

Commit e886274

Browse files
committed
libceph: fix alloc_msg_with_page_vector() memory leaks
Make it so that CEPH_MSG_DATA_PAGES data item can own pages, fixing a bunch of memory leaks for a page vector allocated in alloc_msg_with_page_vector(). Currently, only watch-notify messages trigger this allocation, and normally the page vector is freed either in handle_watch_notify() or by the caller of ceph_osdc_notify(). But if the message is freed before that (e.g. if the session faults while reading in the message or if the notify is stale), we leak the page vector. This was supposed to be fixed by switching to a message-owned pagelist, but that never happened. Fixes: 1907920 ("libceph: support for sending notifies") Reported-by: Roman Penyaev <[email protected]> Signed-off-by: Ilya Dryomov <[email protected]> Reviewed-by: Roman Penyaev <[email protected]>
1 parent 7614209 commit e886274

File tree

3 files changed

+14
-16
lines changed

3 files changed

+14
-16
lines changed

include/linux/ceph/messenger.h

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -175,9 +175,10 @@ struct ceph_msg_data {
175175
#endif /* CONFIG_BLOCK */
176176
struct ceph_bvec_iter bvec_pos;
177177
struct {
178-
struct page **pages; /* NOT OWNER. */
178+
struct page **pages;
179179
size_t length; /* total # bytes */
180180
unsigned int alignment; /* first page */
181+
bool own_pages;
181182
};
182183
struct ceph_pagelist *pagelist;
183184
};
@@ -356,8 +357,8 @@ extern void ceph_con_keepalive(struct ceph_connection *con);
356357
extern bool ceph_con_keepalive_expired(struct ceph_connection *con,
357358
unsigned long interval);
358359

359-
extern void ceph_msg_data_add_pages(struct ceph_msg *msg, struct page **pages,
360-
size_t length, size_t alignment);
360+
void ceph_msg_data_add_pages(struct ceph_msg *msg, struct page **pages,
361+
size_t length, size_t alignment, bool own_pages);
361362
extern void ceph_msg_data_add_pagelist(struct ceph_msg *msg,
362363
struct ceph_pagelist *pagelist);
363364
#ifdef CONFIG_BLOCK

net/ceph/messenger.c

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3248,12 +3248,16 @@ static struct ceph_msg_data *ceph_msg_data_add(struct ceph_msg *msg)
32483248

32493249
static void ceph_msg_data_destroy(struct ceph_msg_data *data)
32503250
{
3251-
if (data->type == CEPH_MSG_DATA_PAGELIST)
3251+
if (data->type == CEPH_MSG_DATA_PAGES && data->own_pages) {
3252+
int num_pages = calc_pages_for(data->alignment, data->length);
3253+
ceph_release_page_vector(data->pages, num_pages);
3254+
} else if (data->type == CEPH_MSG_DATA_PAGELIST) {
32523255
ceph_pagelist_release(data->pagelist);
3256+
}
32533257
}
32543258

32553259
void ceph_msg_data_add_pages(struct ceph_msg *msg, struct page **pages,
3256-
size_t length, size_t alignment)
3260+
size_t length, size_t alignment, bool own_pages)
32573261
{
32583262
struct ceph_msg_data *data;
32593263

@@ -3265,6 +3269,7 @@ void ceph_msg_data_add_pages(struct ceph_msg *msg, struct page **pages,
32653269
data->pages = pages;
32663270
data->length = length;
32673271
data->alignment = alignment & ~PAGE_MASK;
3272+
data->own_pages = own_pages;
32683273

32693274
msg->data_length += length;
32703275
}

net/ceph/osd_client.c

Lines changed: 3 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -962,7 +962,7 @@ static void ceph_osdc_msg_data_add(struct ceph_msg *msg,
962962
BUG_ON(length > (u64) SIZE_MAX);
963963
if (length)
964964
ceph_msg_data_add_pages(msg, osd_data->pages,
965-
length, osd_data->alignment);
965+
length, osd_data->alignment, false);
966966
} else if (osd_data->type == CEPH_OSD_DATA_TYPE_PAGELIST) {
967967
BUG_ON(!length);
968968
ceph_msg_data_add_pagelist(msg, osd_data->pagelist);
@@ -4436,9 +4436,7 @@ static void handle_watch_notify(struct ceph_osd_client *osdc,
44364436
CEPH_MSG_DATA_PAGES);
44374437
*lreq->preply_pages = data->pages;
44384438
*lreq->preply_len = data->length;
4439-
} else {
4440-
ceph_release_page_vector(data->pages,
4441-
calc_pages_for(0, data->length));
4439+
data->own_pages = false;
44424440
}
44434441
}
44444442
lreq->notify_finish_error = return_code;
@@ -5506,9 +5504,6 @@ static struct ceph_msg *get_reply(struct ceph_connection *con,
55065504
return m;
55075505
}
55085506

5509-
/*
5510-
* TODO: switch to a msg-owned pagelist
5511-
*/
55125507
static struct ceph_msg *alloc_msg_with_page_vector(struct ceph_msg_header *hdr)
55135508
{
55145509
struct ceph_msg *m;
@@ -5522,7 +5517,6 @@ static struct ceph_msg *alloc_msg_with_page_vector(struct ceph_msg_header *hdr)
55225517

55235518
if (data_len) {
55245519
struct page **pages;
5525-
struct ceph_osd_data osd_data;
55265520

55275521
pages = ceph_alloc_page_vector(calc_pages_for(0, data_len),
55285522
GFP_NOIO);
@@ -5531,9 +5525,7 @@ static struct ceph_msg *alloc_msg_with_page_vector(struct ceph_msg_header *hdr)
55315525
return NULL;
55325526
}
55335527

5534-
ceph_osd_data_pages_init(&osd_data, pages, data_len, 0, false,
5535-
false);
5536-
ceph_osdc_msg_data_add(m, &osd_data);
5528+
ceph_msg_data_add_pages(m, pages, data_len, 0, true);
55375529
}
55385530

55395531
return m;

0 commit comments

Comments
 (0)