diff options
author | Philipp Reisner <philipp.reisner@linbit.com> | 2011-02-22 02:07:03 -0500 |
---|---|---|
committer | Philipp Reisner <philipp.reisner@linbit.com> | 2011-10-14 10:48:00 -0400 |
commit | 774b305518a68a50df4f479bcf79da2add724e6e (patch) | |
tree | 19b0322feebe2aed18cee0eb7ffa3f5e0ad3addc | |
parent | 80883197da071239ed9e76bd3b9d8c9c5e19e4e6 (diff) |
drbd: Implemented new commands to create/delete connections/minors
Signed-off-by: Philipp Reisner <philipp.reisner@linbit.com>
Signed-off-by: Lars Ellenberg <lars.ellenberg@linbit.com>
-rw-r--r-- | drivers/block/drbd/drbd_int.h | 4 | ||||
-rw-r--r-- | drivers/block/drbd/drbd_main.c | 68 | ||||
-rw-r--r-- | drivers/block/drbd/drbd_nl.c | 106 | ||||
-rw-r--r-- | include/linux/drbd.h | 3 | ||||
-rw-r--r-- | include/linux/drbd_nl.h | 12 |
5 files changed, 120 insertions, 73 deletions
diff --git a/drivers/block/drbd/drbd_int.h b/drivers/block/drbd/drbd_int.h index a27e2a4e038d..535d503886d8 100644 --- a/drivers/block/drbd/drbd_int.h +++ b/drivers/block/drbd/drbd_int.h | |||
@@ -1258,7 +1258,6 @@ extern int drbd_bmio_clear_n_write(struct drbd_conf *mdev); | |||
1258 | extern void drbd_go_diskless(struct drbd_conf *mdev); | 1258 | extern void drbd_go_diskless(struct drbd_conf *mdev); |
1259 | extern void drbd_ldev_destroy(struct drbd_conf *mdev); | 1259 | extern void drbd_ldev_destroy(struct drbd_conf *mdev); |
1260 | 1260 | ||
1261 | |||
1262 | /* Meta data layout | 1261 | /* Meta data layout |
1263 | We reserve a 128MB Block (4k aligned) | 1262 | We reserve a 128MB Block (4k aligned) |
1264 | * either at the end of the backing device | 1263 | * either at the end of the backing device |
@@ -1476,8 +1475,9 @@ extern wait_queue_head_t drbd_pp_wait; | |||
1476 | extern rwlock_t global_state_lock; | 1475 | extern rwlock_t global_state_lock; |
1477 | 1476 | ||
1478 | extern int conn_lowest_minor(struct drbd_tconn *tconn); | 1477 | extern int conn_lowest_minor(struct drbd_tconn *tconn); |
1479 | extern struct drbd_conf *drbd_new_device(unsigned int minor); | 1478 | enum drbd_ret_code conn_new_minor(struct drbd_tconn *tconn, unsigned int minor, int vnr); |
1480 | extern void drbd_free_mdev(struct drbd_conf *mdev); | 1479 | extern void drbd_free_mdev(struct drbd_conf *mdev); |
1480 | extern void drbd_delete_device(unsigned int minor); | ||
1481 | 1481 | ||
1482 | struct drbd_tconn *drbd_new_tconn(char *name); | 1482 | struct drbd_tconn *drbd_new_tconn(char *name); |
1483 | extern void drbd_free_tconn(struct drbd_tconn *tconn); | 1483 | extern void drbd_free_tconn(struct drbd_tconn *tconn); |
diff --git a/drivers/block/drbd/drbd_main.c b/drivers/block/drbd/drbd_main.c index 2bfd63058f40..ec7d0d98657c 100644 --- a/drivers/block/drbd/drbd_main.c +++ b/drivers/block/drbd/drbd_main.c | |||
@@ -614,13 +614,16 @@ char *drbd_task_to_thread_name(struct drbd_tconn *tconn, struct task_struct *tas | |||
614 | return thi ? thi->name : task->comm; | 614 | return thi ? thi->name : task->comm; |
615 | } | 615 | } |
616 | 616 | ||
617 | #ifdef CONFIG_SMP | ||
618 | int conn_lowest_minor(struct drbd_tconn *tconn) | 617 | int conn_lowest_minor(struct drbd_tconn *tconn) |
619 | { | 618 | { |
620 | int minor = 0; | 619 | int minor = 0; |
621 | idr_get_next(&tconn->volumes, &minor); | 620 | |
621 | if (!idr_get_next(&tconn->volumes, &minor)) | ||
622 | return -1; | ||
622 | return minor; | 623 | return minor; |
623 | } | 624 | } |
625 | |||
626 | #ifdef CONFIG_SMP | ||
624 | /** | 627 | /** |
625 | * drbd_calc_cpu_mask() - Generate CPU masks, spread over all CPUs | 628 | * drbd_calc_cpu_mask() - Generate CPU masks, spread over all CPUs |
626 | * @mdev: DRBD device. | 629 | * @mdev: DRBD device. |
@@ -2078,15 +2081,16 @@ static void drbd_release_ee_lists(struct drbd_conf *mdev) | |||
2078 | dev_err(DEV, "%d EEs in net list found!\n", rr); | 2081 | dev_err(DEV, "%d EEs in net list found!\n", rr); |
2079 | } | 2082 | } |
2080 | 2083 | ||
2081 | /* caution. no locking. | 2084 | /* caution. no locking. */ |
2082 | * currently only used from module cleanup code. */ | 2085 | void drbd_delete_device(unsigned int minor) |
2083 | static void drbd_delete_device(unsigned int minor) | ||
2084 | { | 2086 | { |
2085 | struct drbd_conf *mdev = minor_to_mdev(minor); | 2087 | struct drbd_conf *mdev = minor_to_mdev(minor); |
2086 | 2088 | ||
2087 | if (!mdev) | 2089 | if (!mdev) |
2088 | return; | 2090 | return; |
2089 | 2091 | ||
2092 | idr_remove(&mdev->tconn->volumes, minor); | ||
2093 | |||
2090 | /* paranoia asserts */ | 2094 | /* paranoia asserts */ |
2091 | D_ASSERT(mdev->open_cnt == 0); | 2095 | D_ASSERT(mdev->open_cnt == 0); |
2092 | D_ASSERT(list_empty(&mdev->tconn->data.work.q)); | 2096 | D_ASSERT(list_empty(&mdev->tconn->data.work.q)); |
@@ -2101,7 +2105,6 @@ static void drbd_delete_device(unsigned int minor) | |||
2101 | bdput(mdev->this_bdev); | 2105 | bdput(mdev->this_bdev); |
2102 | 2106 | ||
2103 | drbd_free_resources(mdev); | 2107 | drbd_free_resources(mdev); |
2104 | drbd_free_tconn(mdev->tconn); | ||
2105 | 2108 | ||
2106 | drbd_release_ee_lists(mdev); | 2109 | drbd_release_ee_lists(mdev); |
2107 | 2110 | ||
@@ -2223,6 +2226,9 @@ struct drbd_tconn *drbd_new_tconn(char *name) | |||
2223 | if (!tconn->name) | 2226 | if (!tconn->name) |
2224 | goto fail; | 2227 | goto fail; |
2225 | 2228 | ||
2229 | if (!zalloc_cpumask_var(&tconn->cpu_mask, GFP_KERNEL)) | ||
2230 | goto fail; | ||
2231 | |||
2226 | if (!tl_init(tconn)) | 2232 | if (!tl_init(tconn)) |
2227 | goto fail; | 2233 | goto fail; |
2228 | 2234 | ||
@@ -2252,6 +2258,7 @@ struct drbd_tconn *drbd_new_tconn(char *name) | |||
2252 | 2258 | ||
2253 | fail: | 2259 | fail: |
2254 | tl_cleanup(tconn); | 2260 | tl_cleanup(tconn); |
2261 | free_cpumask_var(tconn->cpu_mask); | ||
2255 | kfree(tconn->name); | 2262 | kfree(tconn->name); |
2256 | kfree(tconn); | 2263 | kfree(tconn); |
2257 | 2264 | ||
@@ -2265,6 +2272,7 @@ void drbd_free_tconn(struct drbd_tconn *tconn) | |||
2265 | write_unlock_irq(&global_state_lock); | 2272 | write_unlock_irq(&global_state_lock); |
2266 | idr_destroy(&tconn->volumes); | 2273 | idr_destroy(&tconn->volumes); |
2267 | 2274 | ||
2275 | free_cpumask_var(tconn->cpu_mask); | ||
2268 | kfree(tconn->name); | 2276 | kfree(tconn->name); |
2269 | kfree(tconn->int_dig_out); | 2277 | kfree(tconn->int_dig_out); |
2270 | kfree(tconn->int_dig_in); | 2278 | kfree(tconn->int_dig_in); |
@@ -2272,32 +2280,31 @@ void drbd_free_tconn(struct drbd_tconn *tconn) | |||
2272 | kfree(tconn); | 2280 | kfree(tconn); |
2273 | } | 2281 | } |
2274 | 2282 | ||
2275 | struct drbd_conf *drbd_new_device(unsigned int minor) | 2283 | enum drbd_ret_code conn_new_minor(struct drbd_tconn *tconn, unsigned int minor, int vnr) |
2276 | { | 2284 | { |
2277 | struct drbd_conf *mdev; | 2285 | struct drbd_conf *mdev; |
2278 | struct gendisk *disk; | 2286 | struct gendisk *disk; |
2279 | struct request_queue *q; | 2287 | struct request_queue *q; |
2280 | char conn_name[9]; /* drbd1234N */ | 2288 | int vnr_got = vnr; |
2281 | int vnr; | 2289 | |
2290 | mdev = minor_to_mdev(minor); | ||
2291 | if (mdev) | ||
2292 | return ERR_MINOR_EXISTS; | ||
2282 | 2293 | ||
2283 | /* GFP_KERNEL, we are outside of all write-out paths */ | 2294 | /* GFP_KERNEL, we are outside of all write-out paths */ |
2284 | mdev = kzalloc(sizeof(struct drbd_conf), GFP_KERNEL); | 2295 | mdev = kzalloc(sizeof(struct drbd_conf), GFP_KERNEL); |
2285 | if (!mdev) | 2296 | if (!mdev) |
2286 | return NULL; | 2297 | return ERR_NOMEM; |
2287 | sprintf(conn_name, "drbd%d", minor); | 2298 | |
2288 | mdev->tconn = drbd_new_tconn(conn_name); | 2299 | mdev->tconn = tconn; |
2289 | if (!mdev->tconn) | 2300 | if (!idr_pre_get(&tconn->volumes, GFP_KERNEL)) |
2290 | goto out_no_tconn; | 2301 | goto out_no_idr; |
2291 | if (!idr_pre_get(&mdev->tconn->volumes, GFP_KERNEL)) | 2302 | if (idr_get_new(&tconn->volumes, mdev, &vnr_got)) |
2292 | goto out_no_cpumask; | 2303 | goto out_no_idr; |
2293 | if (idr_get_new(&mdev->tconn->volumes, mdev, &vnr)) | 2304 | if (vnr_got != vnr) { |
2294 | goto out_no_cpumask; | 2305 | dev_err(DEV, "vnr_got (%d) != vnr (%d)\n", vnr_got, vnr); |
2295 | if (vnr != 0) { | 2306 | goto out_no_q; |
2296 | dev_err(DEV, "vnr = %d\n", vnr); | 2307 | } |
2297 | goto out_no_cpumask; | ||
2298 | } | ||
2299 | if (!zalloc_cpumask_var(&mdev->tconn->cpu_mask, GFP_KERNEL)) | ||
2300 | goto out_no_cpumask; | ||
2301 | 2308 | ||
2302 | mdev->minor = minor; | 2309 | mdev->minor = minor; |
2303 | 2310 | ||
@@ -2354,7 +2361,10 @@ struct drbd_conf *drbd_new_device(unsigned int minor) | |||
2354 | INIT_LIST_HEAD(&mdev->current_epoch->list); | 2361 | INIT_LIST_HEAD(&mdev->current_epoch->list); |
2355 | mdev->epochs = 1; | 2362 | mdev->epochs = 1; |
2356 | 2363 | ||
2357 | return mdev; | 2364 | minor_table[minor] = mdev; |
2365 | add_disk(disk); | ||
2366 | |||
2367 | return NO_ERROR; | ||
2358 | 2368 | ||
2359 | /* out_whatever_else: | 2369 | /* out_whatever_else: |
2360 | kfree(mdev->current_epoch); */ | 2370 | kfree(mdev->current_epoch); */ |
@@ -2367,12 +2377,10 @@ out_no_io_page: | |||
2367 | out_no_disk: | 2377 | out_no_disk: |
2368 | blk_cleanup_queue(q); | 2378 | blk_cleanup_queue(q); |
2369 | out_no_q: | 2379 | out_no_q: |
2370 | free_cpumask_var(mdev->tconn->cpu_mask); | 2380 | idr_remove(&tconn->volumes, vnr_got); |
2371 | out_no_cpumask: | 2381 | out_no_idr: |
2372 | drbd_free_tconn(mdev->tconn); | ||
2373 | out_no_tconn: | ||
2374 | kfree(mdev); | 2382 | kfree(mdev); |
2375 | return NULL; | 2383 | return ERR_NOMEM; |
2376 | } | 2384 | } |
2377 | 2385 | ||
2378 | /* counterpart of drbd_new_device. | 2386 | /* counterpart of drbd_new_device. |
diff --git a/drivers/block/drbd/drbd_nl.c b/drivers/block/drbd/drbd_nl.c index 455a51dd364d..f2739fd188a0 100644 --- a/drivers/block/drbd/drbd_nl.c +++ b/drivers/block/drbd/drbd_nl.c | |||
@@ -443,40 +443,6 @@ drbd_set_role(struct drbd_conf *mdev, enum drbd_role new_role, int force) | |||
443 | return rv; | 443 | return rv; |
444 | } | 444 | } |
445 | 445 | ||
446 | static struct drbd_conf *ensure_mdev(int minor, int create) | ||
447 | { | ||
448 | struct drbd_conf *mdev; | ||
449 | |||
450 | if (minor >= minor_count) | ||
451 | return NULL; | ||
452 | |||
453 | mdev = minor_to_mdev(minor); | ||
454 | |||
455 | if (!mdev && create) { | ||
456 | struct gendisk *disk = NULL; | ||
457 | mdev = drbd_new_device(minor); | ||
458 | |||
459 | spin_lock_irq(&drbd_pp_lock); | ||
460 | if (minor_table[minor] == NULL) { | ||
461 | minor_table[minor] = mdev; | ||
462 | disk = mdev->vdisk; | ||
463 | mdev = NULL; | ||
464 | } /* else: we lost the race */ | ||
465 | spin_unlock_irq(&drbd_pp_lock); | ||
466 | |||
467 | if (disk) /* we won the race above */ | ||
468 | /* in case we ever add a drbd_delete_device(), | ||
469 | * don't forget the del_gendisk! */ | ||
470 | add_disk(disk); | ||
471 | else /* we lost the race above */ | ||
472 | drbd_free_mdev(mdev); | ||
473 | |||
474 | mdev = minor_to_mdev(minor); | ||
475 | } | ||
476 | |||
477 | return mdev; | ||
478 | } | ||
479 | |||
480 | static int drbd_nl_primary(struct drbd_conf *mdev, struct drbd_nl_cfg_req *nlp, | 446 | static int drbd_nl_primary(struct drbd_conf *mdev, struct drbd_nl_cfg_req *nlp, |
481 | struct drbd_nl_cfg_reply *reply) | 447 | struct drbd_nl_cfg_reply *reply) |
482 | { | 448 | { |
@@ -1789,12 +1755,6 @@ static int drbd_nl_syncer_conf(struct drbd_conf *mdev, struct drbd_nl_cfg_req *n | |||
1789 | if (!expect(sc.al_extents <= DRBD_AL_EXTENTS_MAX)) | 1755 | if (!expect(sc.al_extents <= DRBD_AL_EXTENTS_MAX)) |
1790 | sc.al_extents = DRBD_AL_EXTENTS_MAX; | 1756 | sc.al_extents = DRBD_AL_EXTENTS_MAX; |
1791 | 1757 | ||
1792 | /* to avoid spurious errors when configuring minors before configuring | ||
1793 | * the minors they depend on: if necessary, first create the minor we | ||
1794 | * depend on */ | ||
1795 | if (sc.after >= 0) | ||
1796 | ensure_mdev(sc.after, 1); | ||
1797 | |||
1798 | /* most sanity checks done, try to assign the new sync-after | 1758 | /* most sanity checks done, try to assign the new sync-after |
1799 | * dependency. need to hold the global lock in there, | 1759 | * dependency. need to hold the global lock in there, |
1800 | * to avoid a race in the dependency loop check. */ | 1760 | * to avoid a race in the dependency loop check. */ |
@@ -2184,13 +2144,73 @@ out: | |||
2184 | return 0; | 2144 | return 0; |
2185 | } | 2145 | } |
2186 | 2146 | ||
2147 | static int drbd_nl_new_conn(struct drbd_nl_cfg_req *nlp, struct drbd_nl_cfg_reply *reply) | ||
2148 | { | ||
2149 | struct new_connection args; | ||
2150 | |||
2151 | if (!new_connection_from_tags(nlp->tag_list, &args)) { | ||
2152 | reply->ret_code = ERR_MANDATORY_TAG; | ||
2153 | return 0; | ||
2154 | } | ||
2155 | |||
2156 | reply->ret_code = NO_ERROR; | ||
2157 | if (!drbd_new_tconn(args.name)) | ||
2158 | reply->ret_code = ERR_NOMEM; | ||
2159 | |||
2160 | return 0; | ||
2161 | } | ||
2162 | |||
2163 | static int drbd_nl_new_minor(struct drbd_tconn *tconn, | ||
2164 | struct drbd_nl_cfg_req *nlp, struct drbd_nl_cfg_reply *reply) | ||
2165 | { | ||
2166 | struct new_minor args; | ||
2167 | |||
2168 | args.vol_nr = 0; | ||
2169 | args.minor = 0; | ||
2170 | |||
2171 | if (!new_minor_from_tags(nlp->tag_list, &args)) { | ||
2172 | reply->ret_code = ERR_MANDATORY_TAG; | ||
2173 | return 0; | ||
2174 | } | ||
2175 | |||
2176 | reply->ret_code = conn_new_minor(tconn, args.minor, args.vol_nr); | ||
2177 | |||
2178 | return 0; | ||
2179 | } | ||
2180 | |||
2181 | static int drbd_nl_del_minor(struct drbd_conf *mdev, struct drbd_nl_cfg_req *nlp, | ||
2182 | struct drbd_nl_cfg_reply *reply) | ||
2183 | { | ||
2184 | if (mdev->state.disk == D_DISKLESS && | ||
2185 | mdev->state.conn == C_STANDALONE && | ||
2186 | mdev->state.role == R_SECONDARY) { | ||
2187 | drbd_delete_device(mdev_to_minor(mdev)); | ||
2188 | reply->ret_code = NO_ERROR; | ||
2189 | } else { | ||
2190 | reply->ret_code = ERR_MINOR_CONFIGURED; | ||
2191 | } | ||
2192 | return 0; | ||
2193 | } | ||
2194 | |||
2195 | static int drbd_nl_del_conn(struct drbd_tconn *tconn, | ||
2196 | struct drbd_nl_cfg_req *nlp, struct drbd_nl_cfg_reply *reply) | ||
2197 | { | ||
2198 | if (conn_lowest_minor(tconn) < 0) { | ||
2199 | drbd_free_tconn(tconn); | ||
2200 | reply->ret_code = NO_ERROR; | ||
2201 | } else { | ||
2202 | reply->ret_code = ERR_CONN_IN_USE; | ||
2203 | } | ||
2204 | |||
2205 | return 0; | ||
2206 | } | ||
2207 | |||
2187 | enum cn_handler_type { | 2208 | enum cn_handler_type { |
2188 | CHT_MINOR, | 2209 | CHT_MINOR, |
2189 | CHT_CONN, | 2210 | CHT_CONN, |
2190 | CHT_CTOR, | 2211 | CHT_CTOR, |
2191 | /* CHT_RES, later */ | 2212 | /* CHT_RES, later */ |
2192 | }; | 2213 | }; |
2193 | |||
2194 | struct cn_handler_struct { | 2214 | struct cn_handler_struct { |
2195 | enum cn_handler_type type; | 2215 | enum cn_handler_type type; |
2196 | union { | 2216 | union { |
@@ -2235,6 +2255,10 @@ static struct cn_handler_struct cnd_table[] = { | |||
2235 | sizeof(struct get_timeout_flag_tag_len_struct)}, | 2255 | sizeof(struct get_timeout_flag_tag_len_struct)}, |
2236 | [ P_start_ov ] = { CHT_MINOR, { &drbd_nl_start_ov }, 0 }, | 2256 | [ P_start_ov ] = { CHT_MINOR, { &drbd_nl_start_ov }, 0 }, |
2237 | [ P_new_c_uuid ] = { CHT_MINOR, { &drbd_nl_new_c_uuid }, 0 }, | 2257 | [ P_new_c_uuid ] = { CHT_MINOR, { &drbd_nl_new_c_uuid }, 0 }, |
2258 | [ P_new_connection ] = { CHT_CTOR, { .constructor = &drbd_nl_new_conn }, 0 }, | ||
2259 | [ P_new_minor ] = { CHT_CONN, { .conn_based = &drbd_nl_new_minor }, 0 }, | ||
2260 | [ P_del_minor ] = { CHT_MINOR, { &drbd_nl_del_minor }, 0 }, | ||
2261 | [ P_del_connection ] = { CHT_CONN, { .conn_based = &drbd_nl_del_conn }, 0 }, | ||
2238 | }; | 2262 | }; |
2239 | 2263 | ||
2240 | static void drbd_connector_callback(struct cn_msg *req, struct netlink_skb_parms *nsp) | 2264 | static void drbd_connector_callback(struct cn_msg *req, struct netlink_skb_parms *nsp) |
diff --git a/include/linux/drbd.h b/include/linux/drbd.h index 7683b4ab6583..e192167e6145 100644 --- a/include/linux/drbd.h +++ b/include/linux/drbd.h | |||
@@ -156,6 +156,9 @@ enum drbd_ret_code { | |||
156 | ERR_PIC_AFTER_DEP = 156, | 156 | ERR_PIC_AFTER_DEP = 156, |
157 | ERR_PIC_PEER_DEP = 157, | 157 | ERR_PIC_PEER_DEP = 157, |
158 | ERR_CONN_NOT_KNOWN = 158, | 158 | ERR_CONN_NOT_KNOWN = 158, |
159 | ERR_CONN_IN_USE = 159, | ||
160 | ERR_MINOR_CONFIGURED = 160, | ||
161 | ERR_MINOR_EXISTS = 161, | ||
159 | 162 | ||
160 | /* insert new ones above this line */ | 163 | /* insert new ones above this line */ |
161 | AFTER_LAST_ERR_CODE | 164 | AFTER_LAST_ERR_CODE |
diff --git a/include/linux/drbd_nl.h b/include/linux/drbd_nl.h index ab6159e4fcf0..1216c7a432c5 100644 --- a/include/linux/drbd_nl.h +++ b/include/linux/drbd_nl.h | |||
@@ -152,6 +152,18 @@ NL_PACKET(new_c_uuid, 26, | |||
152 | NL_RESPONSE(return_code_only, 27) | 152 | NL_RESPONSE(return_code_only, 27) |
153 | #endif | 153 | #endif |
154 | 154 | ||
155 | NL_PACKET(new_connection, 28, /* CHT_CTOR */ | ||
156 | NL_STRING( 85, T_MANDATORY, name, DRBD_NL_OBJ_NAME_LEN) | ||
157 | ) | ||
158 | |||
159 | NL_PACKET(new_minor, 29, /* CHT_CONN */ | ||
160 | NL_INTEGER( 86, T_MANDATORY, minor) | ||
161 | NL_INTEGER( 87, T_MANDATORY, vol_nr) | ||
162 | ) | ||
163 | |||
164 | NL_PACKET(del_minor, 30, ) /* CHT_MINOR */ | ||
165 | NL_PACKET(del_connection, 31, ) /* CHT_CONN */ | ||
166 | |||
155 | #undef NL_PACKET | 167 | #undef NL_PACKET |
156 | #undef NL_INTEGER | 168 | #undef NL_INTEGER |
157 | #undef NL_INT64 | 169 | #undef NL_INT64 |