diff options
Diffstat (limited to 'fs/dlm/user.c')
| -rw-r--r-- | fs/dlm/user.c | 163 |
1 files changed, 107 insertions, 56 deletions
diff --git a/fs/dlm/user.c b/fs/dlm/user.c index 3870150b83a4..b0201ec325a7 100644 --- a/fs/dlm/user.c +++ b/fs/dlm/user.c | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | /* | 1 | /* |
| 2 | * Copyright (C) 2006 Red Hat, Inc. All rights reserved. | 2 | * Copyright (C) 2006-2007 Red Hat, Inc. All rights reserved. |
| 3 | * | 3 | * |
| 4 | * This copyrighted material is made available to anyone wishing to use, | 4 | * This copyrighted material is made available to anyone wishing to use, |
| 5 | * modify, copy, or redistribute it subject to the terms and conditions | 5 | * modify, copy, or redistribute it subject to the terms and conditions |
| @@ -56,6 +56,7 @@ struct dlm_write_request32 { | |||
| 56 | union { | 56 | union { |
| 57 | struct dlm_lock_params32 lock; | 57 | struct dlm_lock_params32 lock; |
| 58 | struct dlm_lspace_params lspace; | 58 | struct dlm_lspace_params lspace; |
| 59 | struct dlm_purge_params purge; | ||
| 59 | } i; | 60 | } i; |
| 60 | }; | 61 | }; |
| 61 | 62 | ||
| @@ -92,6 +93,9 @@ static void compat_input(struct dlm_write_request *kb, | |||
| 92 | kb->i.lspace.flags = kb32->i.lspace.flags; | 93 | kb->i.lspace.flags = kb32->i.lspace.flags; |
| 93 | kb->i.lspace.minor = kb32->i.lspace.minor; | 94 | kb->i.lspace.minor = kb32->i.lspace.minor; |
| 94 | strcpy(kb->i.lspace.name, kb32->i.lspace.name); | 95 | strcpy(kb->i.lspace.name, kb32->i.lspace.name); |
| 96 | } else if (kb->cmd == DLM_USER_PURGE) { | ||
| 97 | kb->i.purge.nodeid = kb32->i.purge.nodeid; | ||
| 98 | kb->i.purge.pid = kb32->i.purge.pid; | ||
| 95 | } else { | 99 | } else { |
| 96 | kb->i.lock.mode = kb32->i.lock.mode; | 100 | kb->i.lock.mode = kb32->i.lock.mode; |
| 97 | kb->i.lock.namelen = kb32->i.lock.namelen; | 101 | kb->i.lock.namelen = kb32->i.lock.namelen; |
| @@ -111,8 +115,6 @@ static void compat_input(struct dlm_write_request *kb, | |||
| 111 | static void compat_output(struct dlm_lock_result *res, | 115 | static void compat_output(struct dlm_lock_result *res, |
| 112 | struct dlm_lock_result32 *res32) | 116 | struct dlm_lock_result32 *res32) |
| 113 | { | 117 | { |
| 114 | res32->length = res->length - (sizeof(struct dlm_lock_result) - | ||
| 115 | sizeof(struct dlm_lock_result32)); | ||
| 116 | res32->user_astaddr = (__u32)(long)res->user_astaddr; | 118 | res32->user_astaddr = (__u32)(long)res->user_astaddr; |
| 117 | res32->user_astparam = (__u32)(long)res->user_astparam; | 119 | res32->user_astparam = (__u32)(long)res->user_astparam; |
| 118 | res32->user_lksb = (__u32)(long)res->user_lksb; | 120 | res32->user_lksb = (__u32)(long)res->user_lksb; |
| @@ -128,35 +130,30 @@ static void compat_output(struct dlm_lock_result *res, | |||
| 128 | } | 130 | } |
| 129 | #endif | 131 | #endif |
| 130 | 132 | ||
| 133 | /* we could possibly check if the cancel of an orphan has resulted in the lkb | ||
| 134 | being removed and then remove that lkb from the orphans list and free it */ | ||
| 131 | 135 | ||
| 132 | void dlm_user_add_ast(struct dlm_lkb *lkb, int type) | 136 | void dlm_user_add_ast(struct dlm_lkb *lkb, int type) |
| 133 | { | 137 | { |
| 134 | struct dlm_ls *ls; | 138 | struct dlm_ls *ls; |
| 135 | struct dlm_user_args *ua; | 139 | struct dlm_user_args *ua; |
| 136 | struct dlm_user_proc *proc; | 140 | struct dlm_user_proc *proc; |
| 137 | int remove_ownqueue = 0; | 141 | int eol = 0, ast_type; |
| 138 | 142 | ||
| 139 | /* dlm_clear_proc_locks() sets ORPHAN/DEAD flag on each | 143 | if (lkb->lkb_flags & (DLM_IFL_ORPHAN | DLM_IFL_DEAD)) |
| 140 | lkb before dealing with it. We need to check this | ||
| 141 | flag before taking ls_clear_proc_locks mutex because if | ||
| 142 | it's set, dlm_clear_proc_locks() holds the mutex. */ | ||
| 143 | |||
| 144 | if (lkb->lkb_flags & (DLM_IFL_ORPHAN | DLM_IFL_DEAD)) { | ||
| 145 | /* log_print("user_add_ast skip1 %x", lkb->lkb_flags); */ | ||
| 146 | return; | 144 | return; |
| 147 | } | ||
| 148 | 145 | ||
| 149 | ls = lkb->lkb_resource->res_ls; | 146 | ls = lkb->lkb_resource->res_ls; |
| 150 | mutex_lock(&ls->ls_clear_proc_locks); | 147 | mutex_lock(&ls->ls_clear_proc_locks); |
| 151 | 148 | ||
| 152 | /* If ORPHAN/DEAD flag is set, it means the process is dead so an ast | 149 | /* If ORPHAN/DEAD flag is set, it means the process is dead so an ast |
| 153 | can't be delivered. For ORPHAN's, dlm_clear_proc_locks() freed | 150 | can't be delivered. For ORPHAN's, dlm_clear_proc_locks() freed |
| 154 | lkb->ua so we can't try to use it. */ | 151 | lkb->ua so we can't try to use it. This second check is necessary |
| 152 | for cases where a completion ast is received for an operation that | ||
| 153 | began before clear_proc_locks did its cancel/unlock. */ | ||
| 155 | 154 | ||
| 156 | if (lkb->lkb_flags & (DLM_IFL_ORPHAN | DLM_IFL_DEAD)) { | 155 | if (lkb->lkb_flags & (DLM_IFL_ORPHAN | DLM_IFL_DEAD)) |
| 157 | /* log_print("user_add_ast skip2 %x", lkb->lkb_flags); */ | ||
| 158 | goto out; | 156 | goto out; |
| 159 | } | ||
| 160 | 157 | ||
| 161 | DLM_ASSERT(lkb->lkb_astparam, dlm_print_lkb(lkb);); | 158 | DLM_ASSERT(lkb->lkb_astparam, dlm_print_lkb(lkb);); |
| 162 | ua = (struct dlm_user_args *)lkb->lkb_astparam; | 159 | ua = (struct dlm_user_args *)lkb->lkb_astparam; |
| @@ -166,28 +163,42 @@ void dlm_user_add_ast(struct dlm_lkb *lkb, int type) | |||
| 166 | goto out; | 163 | goto out; |
| 167 | 164 | ||
| 168 | spin_lock(&proc->asts_spin); | 165 | spin_lock(&proc->asts_spin); |
| 169 | if (!(lkb->lkb_ast_type & (AST_COMP | AST_BAST))) { | 166 | |
| 167 | ast_type = lkb->lkb_ast_type; | ||
| 168 | lkb->lkb_ast_type |= type; | ||
| 169 | |||
| 170 | if (!ast_type) { | ||
| 170 | kref_get(&lkb->lkb_ref); | 171 | kref_get(&lkb->lkb_ref); |
| 171 | list_add_tail(&lkb->lkb_astqueue, &proc->asts); | 172 | list_add_tail(&lkb->lkb_astqueue, &proc->asts); |
| 172 | lkb->lkb_ast_type |= type; | ||
| 173 | wake_up_interruptible(&proc->wait); | 173 | wake_up_interruptible(&proc->wait); |
| 174 | } | 174 | } |
| 175 | 175 | if (type == AST_COMP && (ast_type & AST_COMP)) | |
| 176 | /* noqueue requests that fail may need to be removed from the | 176 | log_debug(ls, "ast overlap %x status %x %x", |
| 177 | proc's locks list, there should be a better way of detecting | 177 | lkb->lkb_id, ua->lksb.sb_status, lkb->lkb_flags); |
| 178 | this situation than checking all these things... */ | 178 | |
| 179 | 179 | /* Figure out if this lock is at the end of its life and no longer | |
| 180 | if (type == AST_COMP && lkb->lkb_grmode == DLM_LOCK_IV && | 180 | available for the application to use. The lkb still exists until |
| 181 | ua->lksb.sb_status == -EAGAIN && !list_empty(&lkb->lkb_ownqueue)) | 181 | the final ast is read. A lock becomes EOL in three situations: |
| 182 | remove_ownqueue = 1; | 182 | 1. a noqueue request fails with EAGAIN |
| 183 | 183 | 2. an unlock completes with EUNLOCK | |
| 184 | /* unlocks or cancels of waiting requests need to be removed from the | 184 | 3. a cancel of a waiting request completes with ECANCEL |
| 185 | proc's unlocking list, again there must be a better way... */ | 185 | An EOL lock needs to be removed from the process's list of locks. |
| 186 | 186 | And we can't allow any new operation on an EOL lock. This is | |
| 187 | if (ua->lksb.sb_status == -DLM_EUNLOCK || | 187 | not related to the lifetime of the lkb struct which is managed |
| 188 | entirely by refcount. */ | ||
| 189 | |||
| 190 | if (type == AST_COMP && | ||
| 191 | lkb->lkb_grmode == DLM_LOCK_IV && | ||
| 192 | ua->lksb.sb_status == -EAGAIN) | ||
| 193 | eol = 1; | ||
| 194 | else if (ua->lksb.sb_status == -DLM_EUNLOCK || | ||
| 188 | (ua->lksb.sb_status == -DLM_ECANCEL && | 195 | (ua->lksb.sb_status == -DLM_ECANCEL && |
| 189 | lkb->lkb_grmode == DLM_LOCK_IV)) | 196 | lkb->lkb_grmode == DLM_LOCK_IV)) |
| 190 | remove_ownqueue = 1; | 197 | eol = 1; |
| 198 | if (eol) { | ||
| 199 | lkb->lkb_ast_type &= ~AST_BAST; | ||
| 200 | lkb->lkb_flags |= DLM_IFL_ENDOFLIFE; | ||
| 201 | } | ||
| 191 | 202 | ||
| 192 | /* We want to copy the lvb to userspace when the completion | 203 | /* We want to copy the lvb to userspace when the completion |
| 193 | ast is read if the status is 0, the lock has an lvb and | 204 | ast is read if the status is 0, the lock has an lvb and |
| @@ -204,11 +215,13 @@ void dlm_user_add_ast(struct dlm_lkb *lkb, int type) | |||
| 204 | 215 | ||
| 205 | spin_unlock(&proc->asts_spin); | 216 | spin_unlock(&proc->asts_spin); |
| 206 | 217 | ||
| 207 | if (remove_ownqueue) { | 218 | if (eol) { |
| 208 | spin_lock(&ua->proc->locks_spin); | 219 | spin_lock(&ua->proc->locks_spin); |
| 209 | list_del_init(&lkb->lkb_ownqueue); | 220 | if (!list_empty(&lkb->lkb_ownqueue)) { |
| 221 | list_del_init(&lkb->lkb_ownqueue); | ||
| 222 | dlm_put_lkb(lkb); | ||
| 223 | } | ||
| 210 | spin_unlock(&ua->proc->locks_spin); | 224 | spin_unlock(&ua->proc->locks_spin); |
| 211 | dlm_put_lkb(lkb); | ||
| 212 | } | 225 | } |
| 213 | out: | 226 | out: |
| 214 | mutex_unlock(&ls->ls_clear_proc_locks); | 227 | mutex_unlock(&ls->ls_clear_proc_locks); |
| @@ -286,47 +299,71 @@ static int device_user_unlock(struct dlm_user_proc *proc, | |||
| 286 | return error; | 299 | return error; |
| 287 | } | 300 | } |
| 288 | 301 | ||
| 289 | static int device_create_lockspace(struct dlm_lspace_params *params) | 302 | static int create_misc_device(struct dlm_ls *ls, char *name) |
| 290 | { | 303 | { |
| 291 | dlm_lockspace_t *lockspace; | ||
| 292 | struct dlm_ls *ls; | ||
| 293 | int error, len; | 304 | int error, len; |
| 294 | 305 | ||
| 295 | if (!capable(CAP_SYS_ADMIN)) | ||
| 296 | return -EPERM; | ||
| 297 | |||
| 298 | error = dlm_new_lockspace(params->name, strlen(params->name), | ||
| 299 | &lockspace, 0, DLM_USER_LVB_LEN); | ||
| 300 | if (error) | ||
| 301 | return error; | ||
| 302 | |||
| 303 | ls = dlm_find_lockspace_local(lockspace); | ||
| 304 | if (!ls) | ||
| 305 | return -ENOENT; | ||
| 306 | |||
| 307 | error = -ENOMEM; | 306 | error = -ENOMEM; |
| 308 | len = strlen(params->name) + strlen(name_prefix) + 2; | 307 | len = strlen(name) + strlen(name_prefix) + 2; |
| 309 | ls->ls_device.name = kzalloc(len, GFP_KERNEL); | 308 | ls->ls_device.name = kzalloc(len, GFP_KERNEL); |
| 310 | if (!ls->ls_device.name) | 309 | if (!ls->ls_device.name) |
| 311 | goto fail; | 310 | goto fail; |
| 311 | |||
| 312 | snprintf((char *)ls->ls_device.name, len, "%s_%s", name_prefix, | 312 | snprintf((char *)ls->ls_device.name, len, "%s_%s", name_prefix, |
| 313 | params->name); | 313 | name); |
| 314 | ls->ls_device.fops = &device_fops; | 314 | ls->ls_device.fops = &device_fops; |
| 315 | ls->ls_device.minor = MISC_DYNAMIC_MINOR; | 315 | ls->ls_device.minor = MISC_DYNAMIC_MINOR; |
| 316 | 316 | ||
| 317 | error = misc_register(&ls->ls_device); | 317 | error = misc_register(&ls->ls_device); |
| 318 | if (error) { | 318 | if (error) { |
| 319 | kfree(ls->ls_device.name); | 319 | kfree(ls->ls_device.name); |
| 320 | goto fail; | ||
| 321 | } | 320 | } |
| 321 | fail: | ||
| 322 | return error; | ||
| 323 | } | ||
| 324 | |||
| 325 | static int device_user_purge(struct dlm_user_proc *proc, | ||
| 326 | struct dlm_purge_params *params) | ||
| 327 | { | ||
| 328 | struct dlm_ls *ls; | ||
| 329 | int error; | ||
| 330 | |||
| 331 | ls = dlm_find_lockspace_local(proc->lockspace); | ||
| 332 | if (!ls) | ||
| 333 | return -ENOENT; | ||
| 334 | |||
| 335 | error = dlm_user_purge(ls, proc, params->nodeid, params->pid); | ||
| 322 | 336 | ||
| 323 | error = ls->ls_device.minor; | ||
| 324 | dlm_put_lockspace(ls); | 337 | dlm_put_lockspace(ls); |
| 325 | return error; | 338 | return error; |
| 339 | } | ||
| 340 | |||
| 341 | static int device_create_lockspace(struct dlm_lspace_params *params) | ||
| 342 | { | ||
| 343 | dlm_lockspace_t *lockspace; | ||
| 344 | struct dlm_ls *ls; | ||
| 345 | int error; | ||
| 326 | 346 | ||
| 327 | fail: | 347 | if (!capable(CAP_SYS_ADMIN)) |
| 348 | return -EPERM; | ||
| 349 | |||
| 350 | error = dlm_new_lockspace(params->name, strlen(params->name), | ||
| 351 | &lockspace, 0, DLM_USER_LVB_LEN); | ||
| 352 | if (error) | ||
| 353 | return error; | ||
| 354 | |||
| 355 | ls = dlm_find_lockspace_local(lockspace); | ||
| 356 | if (!ls) | ||
| 357 | return -ENOENT; | ||
| 358 | |||
| 359 | error = create_misc_device(ls, params->name); | ||
| 328 | dlm_put_lockspace(ls); | 360 | dlm_put_lockspace(ls); |
| 329 | dlm_release_lockspace(lockspace, 0); | 361 | |
| 362 | if (error) | ||
| 363 | dlm_release_lockspace(lockspace, 0); | ||
| 364 | else | ||
| 365 | error = ls->ls_device.minor; | ||
| 366 | |||
| 330 | return error; | 367 | return error; |
| 331 | } | 368 | } |
| 332 | 369 | ||
| @@ -343,6 +380,10 @@ static int device_remove_lockspace(struct dlm_lspace_params *params) | |||
| 343 | if (!ls) | 380 | if (!ls) |
| 344 | return -ENOENT; | 381 | return -ENOENT; |
| 345 | 382 | ||
| 383 | /* Deregister the misc device first, so we don't have | ||
| 384 | * a device that's not attached to a lockspace. If | ||
| 385 | * dlm_release_lockspace fails then we can recreate it | ||
| 386 | */ | ||
| 346 | error = misc_deregister(&ls->ls_device); | 387 | error = misc_deregister(&ls->ls_device); |
| 347 | if (error) { | 388 | if (error) { |
| 348 | dlm_put_lockspace(ls); | 389 | dlm_put_lockspace(ls); |
| @@ -361,6 +402,8 @@ static int device_remove_lockspace(struct dlm_lspace_params *params) | |||
| 361 | 402 | ||
| 362 | dlm_put_lockspace(ls); | 403 | dlm_put_lockspace(ls); |
| 363 | error = dlm_release_lockspace(lockspace, force); | 404 | error = dlm_release_lockspace(lockspace, force); |
| 405 | if (error) | ||
| 406 | create_misc_device(ls, ls->ls_name); | ||
| 364 | out: | 407 | out: |
| 365 | return error; | 408 | return error; |
| 366 | } | 409 | } |
| @@ -497,6 +540,14 @@ static ssize_t device_write(struct file *file, const char __user *buf, | |||
| 497 | error = device_remove_lockspace(&kbuf->i.lspace); | 540 | error = device_remove_lockspace(&kbuf->i.lspace); |
| 498 | break; | 541 | break; |
| 499 | 542 | ||
| 543 | case DLM_USER_PURGE: | ||
| 544 | if (!proc) { | ||
| 545 | log_print("no locking on control device"); | ||
| 546 | goto out_sig; | ||
| 547 | } | ||
| 548 | error = device_user_purge(proc, &kbuf->i.purge); | ||
| 549 | break; | ||
| 550 | |||
| 500 | default: | 551 | default: |
| 501 | log_print("Unknown command passed to DLM device : %d\n", | 552 | log_print("Unknown command passed to DLM device : %d\n", |
| 502 | kbuf->cmd); | 553 | kbuf->cmd); |
