Commit d4b95eef4dc4a59bcd42bdf783638a2eaa57b4c8
Committed by
Mark Fasheh
1 parent
3cfd4ab6b6
Exists in
master
and in
7 other branches
ocfs2: Add the 'set version' message to the ocfs2_control device.
The "SETV" message sets the filesystem locking protocol version as negotiated by the client. The client negotiates based on the maximum version advertised in /sys/fs/ocfs2/max_locking_protocol. Signed-off-by: Joel Becker <joel.becker@oracle.com> Signed-off-by: Mark Fasheh <mfasheh@suse.com>
Showing 1 changed file with 119 additions and 12 deletions Side-by-side Diff
fs/ocfs2/stack_user.c
... | ... | @@ -40,7 +40,7 @@ |
40 | 40 | * unknown, -EINVAL is returned. Once the negotiation is complete, the |
41 | 41 | * client can start sending messages. |
42 | 42 | * |
43 | - * The T01 protocol only has two messages. First is the "SETN" message. | |
43 | + * The T01 protocol has three messages. First is the "SETN" message. | |
44 | 44 | * It has the following syntax: |
45 | 45 | * |
46 | 46 | * SETN<space><8-char-hex-nodenum><newline> |
47 | 47 | |
... | ... | @@ -50,9 +50,23 @@ |
50 | 50 | * The "SETN" message must be the first message following the protocol. |
51 | 51 | * It tells ocfs2_control the local node number. |
52 | 52 | * |
53 | - * Once the local node number has been set, the "DOWN" message can be | |
54 | - * sent for node down notification. It has the following syntax: | |
53 | + * Next comes the "SETV" message. It has the following syntax: | |
55 | 54 | * |
55 | + * SETV<space><2-char-hex-major><space><2-char-hex-minor><newline> | |
56 | + * | |
57 | + * This is 11 characters. | |
58 | + * | |
59 | + * The "SETV" message sets the filesystem locking protocol version as | |
60 | + * negotiated by the client. The client negotiates based on the maximum | |
61 | + * version advertised in /sys/fs/ocfs2/max_locking_protocol. The major | |
62 | + * number from the "SETV" message must match | |
63 | + * user_stack.sp_proto->lp_max_version.pv_major, and the minor number | |
64 | + * must be less than or equal to ...->lp_max_version.pv_minor. | |
65 | + * | |
66 | + * Once this information has been set, mounts will be allowed. From this | |
67 | + * point on, the "DOWN" message can be sent for node down notification. | |
68 | + * It has the following syntax: | |
69 | + * | |
56 | 70 | * DOWN<space><32-char-cap-hex-uuid><space><8-char-hex-nodenum><newline> |
57 | 71 | * |
58 | 72 | * eg: |
59 | 73 | |
... | ... | @@ -79,9 +93,12 @@ |
79 | 93 | #define OCFS2_CONTROL_MESSAGE_OP_LEN 4 |
80 | 94 | #define OCFS2_CONTROL_MESSAGE_SETNODE_OP "SETN" |
81 | 95 | #define OCFS2_CONTROL_MESSAGE_SETNODE_TOTAL_LEN 14 |
96 | +#define OCFS2_CONTROL_MESSAGE_SETVERSION_OP "SETV" | |
97 | +#define OCFS2_CONTROL_MESSAGE_SETVERSION_TOTAL_LEN 11 | |
82 | 98 | #define OCFS2_CONTROL_MESSAGE_DOWN_OP "DOWN" |
83 | 99 | #define OCFS2_CONTROL_MESSAGE_DOWN_TOTAL_LEN 47 |
84 | 100 | #define OCFS2_TEXT_UUID_LEN 32 |
101 | +#define OCFS2_CONTROL_MESSAGE_VERNUM_LEN 2 | |
85 | 102 | #define OCFS2_CONTROL_MESSAGE_NODENUM_LEN 8 |
86 | 103 | |
87 | 104 | /* |
... | ... | @@ -97,6 +114,7 @@ |
97 | 114 | struct list_head op_list; |
98 | 115 | int op_state; |
99 | 116 | int op_this_node; |
117 | + struct ocfs2_protocol_version op_proto; | |
100 | 118 | }; |
101 | 119 | |
102 | 120 | /* SETN<space><8-char-hex-nodenum><newline> */ |
... | ... | @@ -107,6 +125,16 @@ |
107 | 125 | char newline; |
108 | 126 | }; |
109 | 127 | |
128 | +/* SETV<space><2-char-hex-major><space><2-char-hex-minor><newline> */ | |
129 | +struct ocfs2_control_message_setv { | |
130 | + char tag[OCFS2_CONTROL_MESSAGE_OP_LEN]; | |
131 | + char space1; | |
132 | + char major[OCFS2_CONTROL_MESSAGE_VERNUM_LEN]; | |
133 | + char space2; | |
134 | + char minor[OCFS2_CONTROL_MESSAGE_VERNUM_LEN]; | |
135 | + char newline; | |
136 | +}; | |
137 | + | |
110 | 138 | /* DOWN<space><32-char-cap-hex-uuid><space><8-char-hex-nodenum><newline> */ |
111 | 139 | struct ocfs2_control_message_down { |
112 | 140 | char tag[OCFS2_CONTROL_MESSAGE_OP_LEN]; |
113 | 141 | |
... | ... | @@ -120,11 +148,13 @@ |
120 | 148 | union ocfs2_control_message { |
121 | 149 | char tag[OCFS2_CONTROL_MESSAGE_OP_LEN]; |
122 | 150 | struct ocfs2_control_message_setn u_setn; |
151 | + struct ocfs2_control_message_setv u_setv; | |
123 | 152 | struct ocfs2_control_message_down u_down; |
124 | 153 | }; |
125 | 154 | |
126 | 155 | static atomic_t ocfs2_control_opened; |
127 | 156 | static int ocfs2_control_this_node = -1; |
157 | +static struct ocfs2_protocol_version running_proto; | |
128 | 158 | |
129 | 159 | static LIST_HEAD(ocfs2_live_connection_list); |
130 | 160 | static LIST_HEAD(ocfs2_control_private_list); |
... | ... | @@ -264,8 +294,9 @@ |
264 | 294 | /* |
265 | 295 | * Called whenever configuration elements are sent to /dev/ocfs2_control. |
266 | 296 | * If all configuration elements are present, try to set the global |
267 | - * values. If not, return -EAGAIN. If there is a problem, return a | |
268 | - * different error. | |
297 | + * values. If there is a problem, return an error. Skip any missing | |
298 | + * elements, and only bump ocfs2_control_opened when we have all elements | |
299 | + * and are successful. | |
269 | 300 | */ |
270 | 301 | static int ocfs2_control_install_private(struct file *file) |
271 | 302 | { |
272 | 303 | |
273 | 304 | |
274 | 305 | |
... | ... | @@ -275,15 +306,32 @@ |
275 | 306 | |
276 | 307 | BUG_ON(p->op_state != OCFS2_CONTROL_HANDSHAKE_PROTOCOL); |
277 | 308 | |
278 | - if (p->op_this_node < 0) | |
309 | + mutex_lock(&ocfs2_control_lock); | |
310 | + | |
311 | + if (p->op_this_node < 0) { | |
279 | 312 | set_p = 0; |
313 | + } else if ((ocfs2_control_this_node >= 0) && | |
314 | + (ocfs2_control_this_node != p->op_this_node)) { | |
315 | + rc = -EINVAL; | |
316 | + goto out_unlock; | |
317 | + } | |
280 | 318 | |
281 | - mutex_lock(&ocfs2_control_lock); | |
282 | - if (ocfs2_control_this_node < 0) { | |
283 | - if (set_p) | |
284 | - ocfs2_control_this_node = p->op_this_node; | |
285 | - } else if (ocfs2_control_this_node != p->op_this_node) | |
319 | + if (!p->op_proto.pv_major) { | |
320 | + set_p = 0; | |
321 | + } else if (!list_empty(&ocfs2_live_connection_list) && | |
322 | + ((running_proto.pv_major != p->op_proto.pv_major) || | |
323 | + (running_proto.pv_minor != p->op_proto.pv_minor))) { | |
286 | 324 | rc = -EINVAL; |
325 | + goto out_unlock; | |
326 | + } | |
327 | + | |
328 | + if (set_p) { | |
329 | + ocfs2_control_this_node = p->op_this_node; | |
330 | + running_proto.pv_major = p->op_proto.pv_major; | |
331 | + running_proto.pv_minor = p->op_proto.pv_minor; | |
332 | + } | |
333 | + | |
334 | +out_unlock: | |
287 | 335 | mutex_unlock(&ocfs2_control_lock); |
288 | 336 | |
289 | 337 | if (!rc && set_p) { |
... | ... | @@ -327,6 +375,56 @@ |
327 | 375 | return ocfs2_control_install_private(file); |
328 | 376 | } |
329 | 377 | |
378 | +static int ocfs2_control_do_setversion_msg(struct file *file, | |
379 | + struct ocfs2_control_message_setv *msg) | |
380 | + { | |
381 | + long major, minor; | |
382 | + char *ptr = NULL; | |
383 | + struct ocfs2_control_private *p = file->private_data; | |
384 | + struct ocfs2_protocol_version *max = | |
385 | + &user_stack.sp_proto->lp_max_version; | |
386 | + | |
387 | + if (ocfs2_control_get_handshake_state(file) != | |
388 | + OCFS2_CONTROL_HANDSHAKE_PROTOCOL) | |
389 | + return -EINVAL; | |
390 | + | |
391 | + if (strncmp(msg->tag, OCFS2_CONTROL_MESSAGE_SETVERSION_OP, | |
392 | + OCFS2_CONTROL_MESSAGE_OP_LEN)) | |
393 | + return -EINVAL; | |
394 | + | |
395 | + if ((msg->space1 != ' ') || (msg->space2 != ' ') || | |
396 | + (msg->newline != '\n')) | |
397 | + return -EINVAL; | |
398 | + msg->space1 = msg->space2 = msg->newline = '\0'; | |
399 | + | |
400 | + major = simple_strtol(msg->major, &ptr, 16); | |
401 | + if (!ptr || *ptr) | |
402 | + return -EINVAL; | |
403 | + minor = simple_strtol(msg->minor, &ptr, 16); | |
404 | + if (!ptr || *ptr) | |
405 | + return -EINVAL; | |
406 | + | |
407 | + /* | |
408 | + * The major must be between 1 and 255, inclusive. The minor | |
409 | + * must be between 0 and 255, inclusive. The version passed in | |
410 | + * must be within the maximum version supported by the filesystem. | |
411 | + */ | |
412 | + if ((major == LONG_MIN) || (major == LONG_MAX) || | |
413 | + (major > (u8)-1) || (major < 1)) | |
414 | + return -ERANGE; | |
415 | + if ((minor == LONG_MIN) || (minor == LONG_MAX) || | |
416 | + (minor > (u8)-1) || (minor < 0)) | |
417 | + return -ERANGE; | |
418 | + if ((major != max->pv_major) || | |
419 | + (minor > max->pv_minor)) | |
420 | + return -EINVAL; | |
421 | + | |
422 | + p->op_proto.pv_major = major; | |
423 | + p->op_proto.pv_minor = minor; | |
424 | + | |
425 | + return ocfs2_control_install_private(file); | |
426 | +} | |
427 | + | |
330 | 428 | static int ocfs2_control_do_down_msg(struct file *file, |
331 | 429 | struct ocfs2_control_message_down *msg) |
332 | 430 | { |
... | ... | @@ -379,6 +477,10 @@ |
379 | 477 | !strncmp(msg.tag, OCFS2_CONTROL_MESSAGE_SETNODE_OP, |
380 | 478 | OCFS2_CONTROL_MESSAGE_OP_LEN)) |
381 | 479 | ret = ocfs2_control_do_setnode_msg(file, &msg.u_setn); |
480 | + else if ((count == OCFS2_CONTROL_MESSAGE_SETVERSION_TOTAL_LEN) && | |
481 | + !strncmp(msg.tag, OCFS2_CONTROL_MESSAGE_SETVERSION_OP, | |
482 | + OCFS2_CONTROL_MESSAGE_OP_LEN)) | |
483 | + ret = ocfs2_control_do_setversion_msg(file, &msg.u_setv); | |
382 | 484 | else if ((count == OCFS2_CONTROL_MESSAGE_DOWN_TOTAL_LEN) && |
383 | 485 | !strncmp(msg.tag, OCFS2_CONTROL_MESSAGE_DOWN_OP, |
384 | 486 | OCFS2_CONTROL_MESSAGE_OP_LEN)) |
385 | 487 | |
... | ... | @@ -471,8 +573,13 @@ |
471 | 573 | "an emergency restart!\n"); |
472 | 574 | emergency_restart(); |
473 | 575 | } |
474 | - /* Last valid close clears the node number */ | |
576 | + /* | |
577 | + * Last valid close clears the node number and resets | |
578 | + * the locking protocol version | |
579 | + */ | |
475 | 580 | ocfs2_control_this_node = -1; |
581 | + running_proto.pv_major = 0; | |
582 | + running_proto.pv_major = 0; | |
476 | 583 | } |
477 | 584 | |
478 | 585 | out: |