History

libceph: fix double __remove_osd() problem

It turns out it's possible to get __remove_osd() called twice on the same OSD. That doesn't sit well with rb_erase() - depending on the shape of the tree we can get a NULL dereference, a soft lockup or a random crash at some point in the future as we end up touching freed memory. One scenario that I was able to reproduce is as follows: <osd3 is idle, on the osd lru list> <con reset - osd3> con_fault_finish() osd_reset() <osdmap - osd3 down> ceph_osdc_handle_map() <takes map_sem> kick_requests() <takes request_mutex> reset_changed_osds() __reset_osd() __remove_osd() <releases request_mutex> <releases map_sem> <takes map_sem> <takes request_mutex> __kick_osd_requests() __reset_osd() __remove_osd() <-- !!! A case can be made that osd refcounting is imperfect and reworking it would be a proper resolution, but for now Sage and I decided to fix this by adding a safe guard around __remove_osd(). Fixes: http://tracker.ceph.com/issues/8087

Ilya Dryomov committed 11 years ago

Commit 7eb71e01 parent 7ad18af1 file · +18 −8Browse files

Unified · Split

net/ceph/osd_client.c+18−8
@@ -1048,14 +1048,24 @@ static void put_osd(struct ceph_osd *osd) · 1047 unmodified lines
10481048 */
10491049 static void __remove_osd(struct ceph_osd_client *osdc, struct ceph_osd *osd)
10501050 {
1051− dout("__remove_osd %p\n", osd);
1051+ dout("%s %p osd%d\n", __func__, osd, osd->o_osd);
10521052 WARN_ON(!list_empty(&osd->o_requests));
10531053 WARN_ON(!list_empty(&osd->o_linger_requests));
10541054
1055− rb_erase(&osd->o_node, &osdc->osds);
10561055 list_del_init(&osd->o_osd_lru);
1057− ceph_con_close(&osd->o_con);
1058− put_osd(osd);
1056+ rb_erase(&osd->o_node, &osdc->osds);
1057+ RB_CLEAR_NODE(&osd->o_node);
1058+}
1059+
1060+static void remove_osd(struct ceph_osd_client *osdc, struct ceph_osd *osd)
1061+{
1062+ dout("%s %p osd%d\n", __func__, osd, osd->o_osd);
1063+
1064+ if (!RB_EMPTY_NODE(&osd->o_node)) {
1065+ ceph_con_close(&osd->o_con);
1066+ __remove_osd(osdc, osd);
1067+ put_osd(osd);
1068+ }
10591069 }
10601070
10611071 static void remove_all_osds(struct ceph_osd_client *osdc)
@@ -1065,7 +1075,7 @@ static void remove_all_osds(struct ceph_osd_client *osdc) · 3 unmodified lines
10651075 while (!RB_EMPTY_ROOT(&osdc->osds)) {
10661076 struct ceph_osd *osd = rb_entry(rb_first(&osdc->osds),
10671077 struct ceph_osd, o_node);
1068− __remove_osd(osdc, osd);
1078+ remove_osd(osdc, osd);
10691079 }
10701080 mutex_unlock(&osdc->request_mutex);
10711081 }
@@ -1106,7 +1116,7 @@ static void remove_old_osds(struct ceph_osd_client *osdc) · 34 unmodified lines
11061116 list_for_each_entry_safe(osd, nosd, &osdc->osd_lru, o_osd_lru) {
11071117 if (time_before(jiffies, osd->lru_ttl))
11081118 break;
1109− __remove_osd(osdc, osd);
1119+ remove_osd(osdc, osd);
11101120 }
11111121 mutex_unlock(&osdc->request_mutex);
11121122 }
@@ -1121,8 +1131,7 @@ static int __reset_osd(struct ceph_osd_client *osdc, struct ceph_osd *osd) · 8 unmodified lines
11211131 dout("__reset_osd %p osd%d\n", osd, osd->o_osd);
11221132 if (list_empty(&osd->o_requests) &&
11231133 list_empty(&osd->o_linger_requests)) {
1124− __remove_osd(osdc, osd);
1125−
1134+ remove_osd(osdc, osd);
11261135 return -ENODEV;
11271136 }
11281137
@@ -1926,6 +1935,7 @@ static void reset_changed_osds(struct ceph_osd_client *osdc) · 797 unmodified lines
19261935 {
19271936 struct rb_node *p, *n;
19281937
1938+ dout("%s %p\n", __func__, osdc);
19291939 for (p = rb_first(&osdc->osds); p; p = n) {
19301940 struct ceph_osd *osd = rb_entry(p, struct ceph_osd, o_node);
19311941

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t