diff options
| author | Vaibhav Jain <vaibhav@linux.vnet.ibm.com> | 2016-10-14 05:38:36 -0400 |
|---|---|---|
| committer | Michael Ellerman <mpe@ellerman.id.au> | 2016-10-19 05:35:39 -0400 |
| commit | 70b565bbdb911023373e035225ab10077e4ab937 (patch) | |
| tree | cc2d84c644b647a89b3ffa26953e4194127a95f4 /drivers/misc/cxl | |
| parent | 65bc3ece84ef6340cbd80eec10ab9be3426e1149 (diff) | |
cxl: Prevent adapter reset if an active context exists
This patch prevents resetting the cxl adapter via sysfs in presence of
one or more active cxl_context on it. This protects against an
unrecoverable error caused by PSL owning a dirty cache line even after
reset and host tries to touch the same cache line. In case a force reset
of the card is required irrespective of any active contexts, the int
value -1 can be stored in the 'reset' sysfs attribute of the card.
The patch introduces a new atomic_t member named contexts_num inside
struct cxl that holds the number of active context attached to the card
, which is checked against '0' before proceeding with the reset. To
prevent against a race condition where a context is activated just after
reset check is performed, the contexts_num is atomically set to '-1'
after reset-check to indicate that no more contexts can be activated on
the card anymore.
Before activating a context we atomically test if contexts_num is
non-negative and if so, increment its value by one. In case the value of
contexts_num is negative then it indicates that the card is about to be
reset and context activation is error-ed out at that point.
Fixes: 62fa19d4b4fd ("cxl: Add ability to reset the card")
Cc: stable@vger.kernel.org # v4.0+
Acked-by: Frederic Barrat <fbarrat@linux.vnet.ibm.com>
Reviewed-by: Andrew Donnellan <andrew.donnellan@au1.ibm.com>
Signed-off-by: Vaibhav Jain <vaibhav@linux.vnet.ibm.com>
Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>
Diffstat (limited to 'drivers/misc/cxl')
| -rw-r--r-- | drivers/misc/cxl/api.c | 9 | ||||
| -rw-r--r-- | drivers/misc/cxl/context.c | 3 | ||||
| -rw-r--r-- | drivers/misc/cxl/cxl.h | 24 | ||||
| -rw-r--r-- | drivers/misc/cxl/file.c | 11 | ||||
| -rw-r--r-- | drivers/misc/cxl/guest.c | 3 | ||||
| -rw-r--r-- | drivers/misc/cxl/main.c | 42 | ||||
| -rw-r--r-- | drivers/misc/cxl/pci.c | 2 | ||||
| -rw-r--r-- | drivers/misc/cxl/sysfs.c | 27 |
8 files changed, 116 insertions, 5 deletions
diff --git a/drivers/misc/cxl/api.c b/drivers/misc/cxl/api.c index f3d34b941f85..af23d7dfe752 100644 --- a/drivers/misc/cxl/api.c +++ b/drivers/misc/cxl/api.c | |||
| @@ -229,6 +229,14 @@ int cxl_start_context(struct cxl_context *ctx, u64 wed, | |||
| 229 | if (ctx->status == STARTED) | 229 | if (ctx->status == STARTED) |
| 230 | goto out; /* already started */ | 230 | goto out; /* already started */ |
| 231 | 231 | ||
| 232 | /* | ||
| 233 | * Increment the mapped context count for adapter. This also checks | ||
| 234 | * if adapter_context_lock is taken. | ||
| 235 | */ | ||
| 236 | rc = cxl_adapter_context_get(ctx->afu->adapter); | ||
| 237 | if (rc) | ||
| 238 | goto out; | ||
| 239 | |||
| 232 | if (task) { | 240 | if (task) { |
| 233 | ctx->pid = get_task_pid(task, PIDTYPE_PID); | 241 | ctx->pid = get_task_pid(task, PIDTYPE_PID); |
| 234 | ctx->glpid = get_task_pid(task->group_leader, PIDTYPE_PID); | 242 | ctx->glpid = get_task_pid(task->group_leader, PIDTYPE_PID); |
| @@ -240,6 +248,7 @@ int cxl_start_context(struct cxl_context *ctx, u64 wed, | |||
| 240 | 248 | ||
| 241 | if ((rc = cxl_ops->attach_process(ctx, kernel, wed, 0))) { | 249 | if ((rc = cxl_ops->attach_process(ctx, kernel, wed, 0))) { |
| 242 | put_pid(ctx->pid); | 250 | put_pid(ctx->pid); |
| 251 | cxl_adapter_context_put(ctx->afu->adapter); | ||
| 243 | cxl_ctx_put(); | 252 | cxl_ctx_put(); |
| 244 | goto out; | 253 | goto out; |
| 245 | } | 254 | } |
diff --git a/drivers/misc/cxl/context.c b/drivers/misc/cxl/context.c index c466ee2b0c97..5e506c19108a 100644 --- a/drivers/misc/cxl/context.c +++ b/drivers/misc/cxl/context.c | |||
| @@ -238,6 +238,9 @@ int __detach_context(struct cxl_context *ctx) | |||
| 238 | put_pid(ctx->glpid); | 238 | put_pid(ctx->glpid); |
| 239 | 239 | ||
| 240 | cxl_ctx_put(); | 240 | cxl_ctx_put(); |
| 241 | |||
| 242 | /* Decrease the attached context count on the adapter */ | ||
| 243 | cxl_adapter_context_put(ctx->afu->adapter); | ||
| 241 | return 0; | 244 | return 0; |
| 242 | } | 245 | } |
| 243 | 246 | ||
diff --git a/drivers/misc/cxl/cxl.h b/drivers/misc/cxl/cxl.h index 01d372aba131..a144073593fa 100644 --- a/drivers/misc/cxl/cxl.h +++ b/drivers/misc/cxl/cxl.h | |||
| @@ -618,6 +618,14 @@ struct cxl { | |||
| 618 | bool perst_select_user; | 618 | bool perst_select_user; |
| 619 | bool perst_same_image; | 619 | bool perst_same_image; |
| 620 | bool psl_timebase_synced; | 620 | bool psl_timebase_synced; |
| 621 | |||
| 622 | /* | ||
| 623 | * number of contexts mapped on to this card. Possible values are: | ||
| 624 | * >0: Number of contexts mapped and new one can be mapped. | ||
| 625 | * 0: No active contexts and new ones can be mapped. | ||
| 626 | * -1: No contexts mapped and new ones cannot be mapped. | ||
| 627 | */ | ||
| 628 | atomic_t contexts_num; | ||
| 621 | }; | 629 | }; |
| 622 | 630 | ||
| 623 | int cxl_pci_alloc_one_irq(struct cxl *adapter); | 631 | int cxl_pci_alloc_one_irq(struct cxl *adapter); |
| @@ -944,4 +952,20 @@ bool cxl_pci_is_vphb_device(struct pci_dev *dev); | |||
| 944 | 952 | ||
| 945 | /* decode AFU error bits in the PSL register PSL_SERR_An */ | 953 | /* decode AFU error bits in the PSL register PSL_SERR_An */ |
| 946 | void cxl_afu_decode_psl_serr(struct cxl_afu *afu, u64 serr); | 954 | void cxl_afu_decode_psl_serr(struct cxl_afu *afu, u64 serr); |
| 955 | |||
| 956 | /* | ||
| 957 | * Increments the number of attached contexts on an adapter. | ||
| 958 | * In case an adapter_context_lock is taken the return -EBUSY. | ||
| 959 | */ | ||
| 960 | int cxl_adapter_context_get(struct cxl *adapter); | ||
| 961 | |||
| 962 | /* Decrements the number of attached contexts on an adapter */ | ||
| 963 | void cxl_adapter_context_put(struct cxl *adapter); | ||
| 964 | |||
| 965 | /* If no active contexts then prevents contexts from being attached */ | ||
| 966 | int cxl_adapter_context_lock(struct cxl *adapter); | ||
| 967 | |||
| 968 | /* Unlock the contexts-lock if taken. Warn and force unlock otherwise */ | ||
| 969 | void cxl_adapter_context_unlock(struct cxl *adapter); | ||
| 970 | |||
| 947 | #endif | 971 | #endif |
diff --git a/drivers/misc/cxl/file.c b/drivers/misc/cxl/file.c index 5fb9894b157f..d0b421f49b39 100644 --- a/drivers/misc/cxl/file.c +++ b/drivers/misc/cxl/file.c | |||
| @@ -205,11 +205,22 @@ static long afu_ioctl_start_work(struct cxl_context *ctx, | |||
| 205 | ctx->pid = get_task_pid(current, PIDTYPE_PID); | 205 | ctx->pid = get_task_pid(current, PIDTYPE_PID); |
| 206 | ctx->glpid = get_task_pid(current->group_leader, PIDTYPE_PID); | 206 | ctx->glpid = get_task_pid(current->group_leader, PIDTYPE_PID); |
| 207 | 207 | ||
| 208 | /* | ||
| 209 | * Increment the mapped context count for adapter. This also checks | ||
| 210 | * if adapter_context_lock is taken. | ||
| 211 | */ | ||
| 212 | rc = cxl_adapter_context_get(ctx->afu->adapter); | ||
| 213 | if (rc) { | ||
| 214 | afu_release_irqs(ctx, ctx); | ||
| 215 | goto out; | ||
| 216 | } | ||
| 217 | |||
| 208 | trace_cxl_attach(ctx, work.work_element_descriptor, work.num_interrupts, amr); | 218 | trace_cxl_attach(ctx, work.work_element_descriptor, work.num_interrupts, amr); |
| 209 | 219 | ||
| 210 | if ((rc = cxl_ops->attach_process(ctx, false, work.work_element_descriptor, | 220 | if ((rc = cxl_ops->attach_process(ctx, false, work.work_element_descriptor, |
| 211 | amr))) { | 221 | amr))) { |
| 212 | afu_release_irqs(ctx, ctx); | 222 | afu_release_irqs(ctx, ctx); |
| 223 | cxl_adapter_context_put(ctx->afu->adapter); | ||
| 213 | goto out; | 224 | goto out; |
| 214 | } | 225 | } |
| 215 | 226 | ||
diff --git a/drivers/misc/cxl/guest.c b/drivers/misc/cxl/guest.c index 9aa58a77a24d..3e102cd6ed91 100644 --- a/drivers/misc/cxl/guest.c +++ b/drivers/misc/cxl/guest.c | |||
| @@ -1152,6 +1152,9 @@ struct cxl *cxl_guest_init_adapter(struct device_node *np, struct platform_devic | |||
| 1152 | if ((rc = cxl_sysfs_adapter_add(adapter))) | 1152 | if ((rc = cxl_sysfs_adapter_add(adapter))) |
| 1153 | goto err_put1; | 1153 | goto err_put1; |
| 1154 | 1154 | ||
| 1155 | /* release the context lock as the adapter is configured */ | ||
| 1156 | cxl_adapter_context_unlock(adapter); | ||
| 1157 | |||
| 1155 | return adapter; | 1158 | return adapter; |
| 1156 | 1159 | ||
| 1157 | err_put1: | 1160 | err_put1: |
diff --git a/drivers/misc/cxl/main.c b/drivers/misc/cxl/main.c index d9be23b24aa3..62e0dfb5f15b 100644 --- a/drivers/misc/cxl/main.c +++ b/drivers/misc/cxl/main.c | |||
| @@ -243,8 +243,10 @@ struct cxl *cxl_alloc_adapter(void) | |||
| 243 | if (dev_set_name(&adapter->dev, "card%i", adapter->adapter_num)) | 243 | if (dev_set_name(&adapter->dev, "card%i", adapter->adapter_num)) |
| 244 | goto err2; | 244 | goto err2; |
| 245 | 245 | ||
| 246 | return adapter; | 246 | /* start with context lock taken */ |
| 247 | atomic_set(&adapter->contexts_num, -1); | ||
| 247 | 248 | ||
| 249 | return adapter; | ||
| 248 | err2: | 250 | err2: |
| 249 | cxl_remove_adapter_nr(adapter); | 251 | cxl_remove_adapter_nr(adapter); |
| 250 | err1: | 252 | err1: |
| @@ -286,6 +288,44 @@ int cxl_afu_select_best_mode(struct cxl_afu *afu) | |||
| 286 | return 0; | 288 | return 0; |
| 287 | } | 289 | } |
| 288 | 290 | ||
| 291 | int cxl_adapter_context_get(struct cxl *adapter) | ||
| 292 | { | ||
| 293 | int rc; | ||
| 294 | |||
| 295 | rc = atomic_inc_unless_negative(&adapter->contexts_num); | ||
| 296 | return rc >= 0 ? 0 : -EBUSY; | ||
| 297 | } | ||
| 298 | |||
| 299 | void cxl_adapter_context_put(struct cxl *adapter) | ||
| 300 | { | ||
| 301 | atomic_dec_if_positive(&adapter->contexts_num); | ||
| 302 | } | ||
| 303 | |||
| 304 | int cxl_adapter_context_lock(struct cxl *adapter) | ||
| 305 | { | ||
| 306 | int rc; | ||
| 307 | /* no active contexts -> contexts_num == 0 */ | ||
| 308 | rc = atomic_cmpxchg(&adapter->contexts_num, 0, -1); | ||
| 309 | return rc ? -EBUSY : 0; | ||
| 310 | } | ||
| 311 | |||
| 312 | void cxl_adapter_context_unlock(struct cxl *adapter) | ||
| 313 | { | ||
| 314 | int val = atomic_cmpxchg(&adapter->contexts_num, -1, 0); | ||
| 315 | |||
| 316 | /* | ||
| 317 | * contexts lock taken -> contexts_num == -1 | ||
| 318 | * If not true then show a warning and force reset the lock. | ||
| 319 | * This will happen when context_unlock was requested without | ||
| 320 | * doing a context_lock. | ||
| 321 | */ | ||
| 322 | if (val != -1) { | ||
| 323 | atomic_set(&adapter->contexts_num, 0); | ||
| 324 | WARN(1, "Adapter context unlocked with %d active contexts", | ||
| 325 | val); | ||
| 326 | } | ||
| 327 | } | ||
| 328 | |||
| 289 | static int __init init_cxl(void) | 329 | static int __init init_cxl(void) |
| 290 | { | 330 | { |
| 291 | int rc = 0; | 331 | int rc = 0; |
diff --git a/drivers/misc/cxl/pci.c b/drivers/misc/cxl/pci.c index 7afad8477ad5..e96be9ca4e60 100644 --- a/drivers/misc/cxl/pci.c +++ b/drivers/misc/cxl/pci.c | |||
| @@ -1487,6 +1487,8 @@ static int cxl_configure_adapter(struct cxl *adapter, struct pci_dev *dev) | |||
| 1487 | if ((rc = cxl_native_register_psl_err_irq(adapter))) | 1487 | if ((rc = cxl_native_register_psl_err_irq(adapter))) |
| 1488 | goto err; | 1488 | goto err; |
| 1489 | 1489 | ||
| 1490 | /* Release the context lock as adapter is configured */ | ||
| 1491 | cxl_adapter_context_unlock(adapter); | ||
| 1490 | return 0; | 1492 | return 0; |
| 1491 | 1493 | ||
| 1492 | err: | 1494 | err: |
diff --git a/drivers/misc/cxl/sysfs.c b/drivers/misc/cxl/sysfs.c index b043c20f158f..a8b6d6a635e9 100644 --- a/drivers/misc/cxl/sysfs.c +++ b/drivers/misc/cxl/sysfs.c | |||
| @@ -75,12 +75,31 @@ static ssize_t reset_adapter_store(struct device *device, | |||
| 75 | int val; | 75 | int val; |
| 76 | 76 | ||
| 77 | rc = sscanf(buf, "%i", &val); | 77 | rc = sscanf(buf, "%i", &val); |
| 78 | if ((rc != 1) || (val != 1)) | 78 | if ((rc != 1) || (val != 1 && val != -1)) |
| 79 | return -EINVAL; | 79 | return -EINVAL; |
| 80 | 80 | ||
| 81 | if ((rc = cxl_ops->adapter_reset(adapter))) | 81 | /* |
| 82 | return rc; | 82 | * See if we can lock the context mapping that's only allowed |
| 83 | return count; | 83 | * when there are no contexts attached to the adapter. Once |
| 84 | * taken this will also prevent any context from getting activated. | ||
| 85 | */ | ||
| 86 | if (val == 1) { | ||
| 87 | rc = cxl_adapter_context_lock(adapter); | ||
| 88 | if (rc) | ||
| 89 | goto out; | ||
| 90 | |||
| 91 | rc = cxl_ops->adapter_reset(adapter); | ||
| 92 | /* In case reset failed release context lock */ | ||
| 93 | if (rc) | ||
| 94 | cxl_adapter_context_unlock(adapter); | ||
| 95 | |||
| 96 | } else if (val == -1) { | ||
| 97 | /* Perform a forced adapter reset */ | ||
| 98 | rc = cxl_ops->adapter_reset(adapter); | ||
| 99 | } | ||
| 100 | |||
| 101 | out: | ||
| 102 | return rc ? rc : count; | ||
| 84 | } | 103 | } |
| 85 | 104 | ||
| 86 | static ssize_t load_image_on_perst_show(struct device *device, | 105 | static ssize_t load_image_on_perst_show(struct device *device, |
