diff options
| -rw-r--r-- | drivers/edac/edac_device.c | 56 | ||||
| -rw-r--r-- | drivers/edac/edac_mc.c | 72 |
2 files changed, 100 insertions, 28 deletions
diff --git a/drivers/edac/edac_device.c b/drivers/edac/edac_device.c index 173f4ba0f7c8..7e3723768aca 100644 --- a/drivers/edac/edac_device.c +++ b/drivers/edac/edac_device.c | |||
| @@ -32,7 +32,9 @@ | |||
| 32 | #include "edac_core.h" | 32 | #include "edac_core.h" |
| 33 | #include "edac_module.h" | 33 | #include "edac_module.h" |
| 34 | 34 | ||
| 35 | /* lock to memory controller's control array 'edac_device_list' */ | 35 | /* lock for the list: 'edac_device_list', manipulation of this list |
| 36 | * is protected by the 'device_ctls_mutex' lock | ||
| 37 | */ | ||
| 36 | static DEFINE_MUTEX(device_ctls_mutex); | 38 | static DEFINE_MUTEX(device_ctls_mutex); |
| 37 | static struct list_head edac_device_list = LIST_HEAD_INIT(edac_device_list); | 39 | static struct list_head edac_device_list = LIST_HEAD_INIT(edac_device_list); |
| 38 | 40 | ||
| @@ -386,6 +388,14 @@ EXPORT_SYMBOL_GPL(edac_device_find); | |||
| 386 | /* | 388 | /* |
| 387 | * edac_device_workq_function | 389 | * edac_device_workq_function |
| 388 | * performs the operation scheduled by a workq request | 390 | * performs the operation scheduled by a workq request |
| 391 | * | ||
| 392 | * this workq is embedded within an edac_device_ctl_info | ||
| 393 | * structure, that needs to be polled for possible error events. | ||
| 394 | * | ||
| 395 | * This operation is to acquire the list mutex lock | ||
| 396 | * (thus preventing insertation or deletion) | ||
| 397 | * and then call the device's poll function IFF this device is | ||
| 398 | * running polled and there is a poll function defined. | ||
| 389 | */ | 399 | */ |
| 390 | static void edac_device_workq_function(struct work_struct *work_req) | 400 | static void edac_device_workq_function(struct work_struct *work_req) |
| 391 | { | 401 | { |
| @@ -403,8 +413,17 @@ static void edac_device_workq_function(struct work_struct *work_req) | |||
| 403 | 413 | ||
| 404 | mutex_unlock(&device_ctls_mutex); | 414 | mutex_unlock(&device_ctls_mutex); |
| 405 | 415 | ||
| 406 | /* Reschedule */ | 416 | /* Reschedule the workq for the next time period to start again |
| 407 | queue_delayed_work(edac_workqueue, &edac_dev->work, edac_dev->delay); | 417 | * if the number of msec is for 1 sec, then adjust to the next |
| 418 | * whole one second to save timers fireing all over the period | ||
| 419 | * between integral seconds | ||
| 420 | */ | ||
| 421 | if (edac_dev->poll_msec == 1000) | ||
| 422 | queue_delayed_work(edac_workqueue, &edac_dev->work, | ||
| 423 | round_jiffies(edac_dev->delay)); | ||
| 424 | else | ||
| 425 | queue_delayed_work(edac_workqueue, &edac_dev->work, | ||
| 426 | edac_dev->delay); | ||
| 408 | } | 427 | } |
| 409 | 428 | ||
| 410 | /* | 429 | /* |
| @@ -417,11 +436,26 @@ void edac_device_workq_setup(struct edac_device_ctl_info *edac_dev, | |||
| 417 | { | 436 | { |
| 418 | debugf0("%s()\n", __func__); | 437 | debugf0("%s()\n", __func__); |
| 419 | 438 | ||
| 439 | /* take the arg 'msec' and set it into the control structure | ||
| 440 | * to used in the time period calculation | ||
| 441 | * then calc the number of jiffies that represents | ||
| 442 | */ | ||
| 420 | edac_dev->poll_msec = msec; | 443 | edac_dev->poll_msec = msec; |
| 421 | edac_dev->delay = msecs_to_jiffies(msec); /* Calc delay jiffies */ | 444 | edac_dev->delay = msecs_to_jiffies(msec); |
| 422 | 445 | ||
| 423 | INIT_DELAYED_WORK(&edac_dev->work, edac_device_workq_function); | 446 | INIT_DELAYED_WORK(&edac_dev->work, edac_device_workq_function); |
| 424 | queue_delayed_work(edac_workqueue, &edac_dev->work, edac_dev->delay); | 447 | |
| 448 | /* optimize here for the 1 second case, which will be normal value, to | ||
| 449 | * fire ON the 1 second time event. This helps reduce all sorts of | ||
| 450 | * timers firing on sub-second basis, while they are happy | ||
| 451 | * to fire together on the 1 second exactly | ||
| 452 | */ | ||
| 453 | if (edac_dev->poll_msec == 1000) | ||
| 454 | queue_delayed_work(edac_workqueue, &edac_dev->work, | ||
| 455 | round_jiffies(edac_dev->delay)); | ||
| 456 | else | ||
| 457 | queue_delayed_work(edac_workqueue, &edac_dev->work, | ||
| 458 | edac_dev->delay); | ||
| 425 | } | 459 | } |
| 426 | 460 | ||
| 427 | /* | 461 | /* |
| @@ -441,16 +475,20 @@ void edac_device_workq_teardown(struct edac_device_ctl_info *edac_dev) | |||
| 441 | 475 | ||
| 442 | /* | 476 | /* |
| 443 | * edac_device_reset_delay_period | 477 | * edac_device_reset_delay_period |
| 478 | * | ||
| 479 | * need to stop any outstanding workq queued up at this time | ||
| 480 | * because we will be resetting the sleep time. | ||
| 481 | * Then restart the workq on the new delay | ||
| 444 | */ | 482 | */ |
| 445 | |||
| 446 | void edac_device_reset_delay_period(struct edac_device_ctl_info *edac_dev, | 483 | void edac_device_reset_delay_period(struct edac_device_ctl_info *edac_dev, |
| 447 | unsigned long value) | 484 | unsigned long value) |
| 448 | { | 485 | { |
| 449 | mutex_lock(&device_ctls_mutex); | 486 | /* cancel the current workq request, without the mutex lock */ |
| 450 | |||
| 451 | /* cancel the current workq request */ | ||
| 452 | edac_device_workq_teardown(edac_dev); | 487 | edac_device_workq_teardown(edac_dev); |
| 453 | 488 | ||
| 489 | /* acquire the mutex before doing the workq setup */ | ||
| 490 | mutex_lock(&device_ctls_mutex); | ||
| 491 | |||
| 454 | /* restart the workq request, with new delay value */ | 492 | /* restart the workq request, with new delay value */ |
| 455 | edac_device_workq_setup(edac_dev, value); | 493 | edac_device_workq_setup(edac_dev, value); |
| 456 | 494 | ||
diff --git a/drivers/edac/edac_mc.c b/drivers/edac/edac_mc.c index 2d53cb38868a..4471be362599 100644 --- a/drivers/edac/edac_mc.c +++ b/drivers/edac/edac_mc.c | |||
| @@ -258,6 +258,12 @@ static void edac_mc_workq_function(struct work_struct *work_req) | |||
| 258 | 258 | ||
| 259 | mutex_lock(&mem_ctls_mutex); | 259 | mutex_lock(&mem_ctls_mutex); |
| 260 | 260 | ||
| 261 | /* if this control struct has movd to offline state, we are done */ | ||
| 262 | if (mci->op_state == OP_OFFLINE) { | ||
| 263 | mutex_unlock(&mem_ctls_mutex); | ||
| 264 | return; | ||
| 265 | } | ||
| 266 | |||
| 261 | /* Only poll controllers that are running polled and have a check */ | 267 | /* Only poll controllers that are running polled and have a check */ |
| 262 | if (edac_mc_assert_error_check_and_clear() && (mci->edac_check != NULL)) | 268 | if (edac_mc_assert_error_check_and_clear() && (mci->edac_check != NULL)) |
| 263 | mci->edac_check(mci); | 269 | mci->edac_check(mci); |
| @@ -279,11 +285,19 @@ static void edac_mc_workq_function(struct work_struct *work_req) | |||
| 279 | * edac_mc_workq_setup | 285 | * edac_mc_workq_setup |
| 280 | * initialize a workq item for this mci | 286 | * initialize a workq item for this mci |
| 281 | * passing in the new delay period in msec | 287 | * passing in the new delay period in msec |
| 288 | * | ||
| 289 | * locking model: | ||
| 290 | * | ||
| 291 | * called with the mem_ctls_mutex held | ||
| 282 | */ | 292 | */ |
| 283 | void edac_mc_workq_setup(struct mem_ctl_info *mci, unsigned msec) | 293 | static void edac_mc_workq_setup(struct mem_ctl_info *mci, unsigned msec) |
| 284 | { | 294 | { |
| 285 | debugf0("%s()\n", __func__); | 295 | debugf0("%s()\n", __func__); |
| 286 | 296 | ||
| 297 | /* if this instance is not in the POLL state, then simply return */ | ||
| 298 | if (mci->op_state != OP_RUNNING_POLL) | ||
| 299 | return; | ||
| 300 | |||
| 287 | INIT_DELAYED_WORK(&mci->work, edac_mc_workq_function); | 301 | INIT_DELAYED_WORK(&mci->work, edac_mc_workq_function); |
| 288 | queue_delayed_work(edac_workqueue, &mci->work, msecs_to_jiffies(msec)); | 302 | queue_delayed_work(edac_workqueue, &mci->work, msecs_to_jiffies(msec)); |
| 289 | } | 303 | } |
| @@ -291,29 +305,39 @@ void edac_mc_workq_setup(struct mem_ctl_info *mci, unsigned msec) | |||
| 291 | /* | 305 | /* |
| 292 | * edac_mc_workq_teardown | 306 | * edac_mc_workq_teardown |
| 293 | * stop the workq processing on this mci | 307 | * stop the workq processing on this mci |
| 308 | * | ||
| 309 | * locking model: | ||
| 310 | * | ||
| 311 | * called WITHOUT lock held | ||
| 294 | */ | 312 | */ |
| 295 | void edac_mc_workq_teardown(struct mem_ctl_info *mci) | 313 | static void edac_mc_workq_teardown(struct mem_ctl_info *mci) |
| 296 | { | 314 | { |
| 297 | int status; | 315 | int status; |
| 298 | 316 | ||
| 299 | status = cancel_delayed_work(&mci->work); | 317 | /* if not running POLL, leave now */ |
| 300 | if (status == 0) { | 318 | if (mci->op_state == OP_RUNNING_POLL) { |
| 301 | /* workq instance might be running, wait for it */ | 319 | status = cancel_delayed_work(&mci->work); |
| 302 | flush_workqueue(edac_workqueue); | 320 | if (status == 0) { |
| 321 | debugf0("%s() not canceled, flush the queue\n", | ||
| 322 | __func__); | ||
| 323 | |||
| 324 | /* workq instance might be running, wait for it */ | ||
| 325 | flush_workqueue(edac_workqueue); | ||
| 326 | } | ||
| 303 | } | 327 | } |
| 304 | } | 328 | } |
| 305 | 329 | ||
| 306 | /* | 330 | /* |
| 307 | * edac_reset_delay_period | 331 | * edac_reset_delay_period |
| 308 | */ | 332 | */ |
| 309 | 333 | static void edac_reset_delay_period(struct mem_ctl_info *mci, unsigned long value) | |
| 310 | void edac_reset_delay_period(struct mem_ctl_info *mci, unsigned long value) | ||
| 311 | { | 334 | { |
| 312 | mutex_lock(&mem_ctls_mutex); | ||
| 313 | |||
| 314 | /* cancel the current workq request */ | 335 | /* cancel the current workq request */ |
| 315 | edac_mc_workq_teardown(mci); | 336 | edac_mc_workq_teardown(mci); |
| 316 | 337 | ||
| 338 | /* lock the list of devices for the new setup */ | ||
| 339 | mutex_lock(&mem_ctls_mutex); | ||
| 340 | |||
| 317 | /* restart the workq request, with new delay value */ | 341 | /* restart the workq request, with new delay value */ |
| 318 | edac_mc_workq_setup(mci, value); | 342 | edac_mc_workq_setup(mci, value); |
| 319 | 343 | ||
| @@ -323,6 +347,10 @@ void edac_reset_delay_period(struct mem_ctl_info *mci, unsigned long value) | |||
| 323 | /* Return 0 on success, 1 on failure. | 347 | /* Return 0 on success, 1 on failure. |
| 324 | * Before calling this function, caller must | 348 | * Before calling this function, caller must |
| 325 | * assign a unique value to mci->mc_idx. | 349 | * assign a unique value to mci->mc_idx. |
| 350 | * | ||
| 351 | * locking model: | ||
| 352 | * | ||
| 353 | * called with the mem_ctls_mutex lock held | ||
| 326 | */ | 354 | */ |
| 327 | static int add_mc_to_global_list(struct mem_ctl_info *mci) | 355 | static int add_mc_to_global_list(struct mem_ctl_info *mci) |
| 328 | { | 356 | { |
| @@ -331,7 +359,8 @@ static int add_mc_to_global_list(struct mem_ctl_info *mci) | |||
| 331 | 359 | ||
| 332 | insert_before = &mc_devices; | 360 | insert_before = &mc_devices; |
| 333 | 361 | ||
| 334 | if (unlikely((p = find_mci_by_dev(mci->dev)) != NULL)) | 362 | p = find_mci_by_dev(mci->dev); |
| 363 | if (unlikely(p != NULL)) | ||
| 335 | goto fail0; | 364 | goto fail0; |
| 336 | 365 | ||
| 337 | list_for_each(item, &mc_devices) { | 366 | list_for_each(item, &mc_devices) { |
| @@ -467,8 +496,8 @@ int edac_mc_add_mc(struct mem_ctl_info *mci) | |||
| 467 | } | 496 | } |
| 468 | 497 | ||
| 469 | /* Report action taken */ | 498 | /* Report action taken */ |
| 470 | edac_mc_printk(mci, KERN_INFO, "Giving out device to %s %s: DEV %s\n", | 499 | edac_mc_printk(mci, KERN_INFO, "Giving out device to '%s' '%s':" |
| 471 | mci->mod_name, mci->ctl_name, dev_name(mci)); | 500 | " DEV %s\n", mci->mod_name, mci->ctl_name, dev_name(mci)); |
| 472 | 501 | ||
| 473 | mutex_unlock(&mem_ctls_mutex); | 502 | mutex_unlock(&mem_ctls_mutex); |
| 474 | return 0; | 503 | return 0; |
| @@ -493,10 +522,13 @@ struct mem_ctl_info *edac_mc_del_mc(struct device *dev) | |||
| 493 | { | 522 | { |
| 494 | struct mem_ctl_info *mci; | 523 | struct mem_ctl_info *mci; |
| 495 | 524 | ||
| 496 | debugf0("MC: %s()\n", __func__); | 525 | debugf0("%s()\n", __func__); |
| 526 | |||
| 497 | mutex_lock(&mem_ctls_mutex); | 527 | mutex_lock(&mem_ctls_mutex); |
| 498 | 528 | ||
| 499 | if ((mci = find_mci_by_dev(dev)) == NULL) { | 529 | /* find the requested mci struct in the global list */ |
| 530 | mci = find_mci_by_dev(dev); | ||
| 531 | if (mci == NULL) { | ||
| 500 | mutex_unlock(&mem_ctls_mutex); | 532 | mutex_unlock(&mem_ctls_mutex); |
| 501 | return NULL; | 533 | return NULL; |
| 502 | } | 534 | } |
| @@ -504,15 +536,17 @@ struct mem_ctl_info *edac_mc_del_mc(struct device *dev) | |||
| 504 | /* marking MCI offline */ | 536 | /* marking MCI offline */ |
| 505 | mci->op_state = OP_OFFLINE; | 537 | mci->op_state = OP_OFFLINE; |
| 506 | 538 | ||
| 507 | /* flush workq processes */ | ||
| 508 | edac_mc_workq_teardown(mci); | ||
| 509 | |||
| 510 | edac_remove_sysfs_mci_device(mci); | ||
| 511 | del_mc_from_global_list(mci); | 539 | del_mc_from_global_list(mci); |
| 512 | mutex_unlock(&mem_ctls_mutex); | 540 | mutex_unlock(&mem_ctls_mutex); |
| 541 | |||
| 542 | /* flush workq processes and remove sysfs */ | ||
| 543 | edac_mc_workq_teardown(mci); | ||
| 544 | edac_remove_sysfs_mci_device(mci); | ||
| 545 | |||
| 513 | edac_printk(KERN_INFO, EDAC_MC, | 546 | edac_printk(KERN_INFO, EDAC_MC, |
| 514 | "Removed device %d for %s %s: DEV %s\n", mci->mc_idx, | 547 | "Removed device %d for %s %s: DEV %s\n", mci->mc_idx, |
| 515 | mci->mod_name, mci->ctl_name, dev_name(mci)); | 548 | mci->mod_name, mci->ctl_name, dev_name(mci)); |
| 549 | |||
| 516 | return mci; | 550 | return mci; |
| 517 | } | 551 | } |
| 518 | EXPORT_SYMBOL_GPL(edac_mc_del_mc); | 552 | EXPORT_SYMBOL_GPL(edac_mc_del_mc); |
