Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

PCI: Prepare to protect against concurrent isolated cpuset change

HK_TYPE_DOMAIN will soon integrate cpuset isolated partitions and
therefore be made modifiable at runtime. Synchronize against the cpumask
update using RCU.

The RCU locked section includes both the housekeeping CPU target
election for the PCI probe work and the work enqueue.

This way the housekeeping update side will simply need to flush the
pending related works after updating the housekeeping mask in order to
make sure that no PCI work ever executes on an isolated CPU. This part
will be handled in a subsequent patch.

Signed-off-by: Frederic Weisbecker <frederic@kernel.org>
Acked-by: Bjorn Helgaas <bhelgaas@google.com>
Cc: linux-pci@vger.kernel.org
Cc: Marco Crivellari <marco.crivellari@suse.com>
Cc: Michal Hocko <mhocko@suse.com>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Tejun Heo <tj@kernel.org>
Cc: Thomas Gleixner <tglx@linutronix.de>
Cc: Vlastimil Babka <vbabka@suse.cz>
Cc: Waiman Long <longman@redhat.com>

+39 -10
+39 -10
drivers/pci/pci-driver.c
··· 302 302 const struct pci_device_id *id; 303 303 }; 304 304 305 - static long local_pci_probe(void *_ddi) 305 + static int local_pci_probe(struct drv_dev_and_id *ddi) 306 306 { 307 - struct drv_dev_and_id *ddi = _ddi; 308 307 struct pci_dev *pci_dev = ddi->dev; 309 308 struct pci_driver *pci_drv = ddi->drv; 310 309 struct device *dev = &pci_dev->dev; ··· 337 338 return 0; 338 339 } 339 340 341 + struct pci_probe_arg { 342 + struct drv_dev_and_id *ddi; 343 + struct work_struct work; 344 + int ret; 345 + }; 346 + 347 + static void local_pci_probe_callback(struct work_struct *work) 348 + { 349 + struct pci_probe_arg *arg = container_of(work, struct pci_probe_arg, work); 350 + 351 + arg->ret = local_pci_probe(arg->ddi); 352 + } 353 + 340 354 static bool pci_physfn_is_probed(struct pci_dev *dev) 341 355 { 342 356 #ifdef CONFIG_PCI_IOV ··· 374 362 dev->is_probed = 1; 375 363 376 364 cpu_hotplug_disable(); 377 - 378 365 /* 379 366 * Prevent nesting work_on_cpu() for the case where a Virtual Function 380 367 * device is probed from work_on_cpu() of the Physical device. 381 368 */ 382 369 if (node < 0 || node >= MAX_NUMNODES || !node_online(node) || 383 370 pci_physfn_is_probed(dev)) { 384 - cpu = nr_cpu_ids; 371 + error = local_pci_probe(&ddi); 385 372 } else { 386 373 cpumask_var_t wq_domain_mask; 374 + struct pci_probe_arg arg = { .ddi = &ddi }; 387 375 388 376 if (!zalloc_cpumask_var(&wq_domain_mask, GFP_KERNEL)) { 389 377 error = -ENOMEM; 390 378 goto out; 391 379 } 380 + 381 + INIT_WORK_ONSTACK(&arg.work, local_pci_probe_callback); 382 + 383 + /* 384 + * The target election and the enqueue of the work must be within 385 + * the same RCU read side section so that when the workqueue pool 386 + * is flushed after a housekeeping cpumask update, further readers 387 + * are guaranteed to queue the probing work to the appropriate 388 + * targets. 389 + */ 390 + rcu_read_lock(); 392 391 cpumask_and(wq_domain_mask, 393 392 housekeeping_cpumask(HK_TYPE_WQ), 394 393 housekeeping_cpumask(HK_TYPE_DOMAIN)); 395 394 396 395 cpu = cpumask_any_and(cpumask_of_node(node), 397 396 wq_domain_mask); 398 - free_cpumask_var(wq_domain_mask); 399 - } 397 + if (cpu < nr_cpu_ids) { 398 + schedule_work_on(cpu, &arg.work); 399 + rcu_read_unlock(); 400 + flush_work(&arg.work); 401 + error = arg.ret; 402 + } else { 403 + rcu_read_unlock(); 404 + error = local_pci_probe(&ddi); 405 + } 400 406 401 - if (cpu < nr_cpu_ids) 402 - error = work_on_cpu(cpu, local_pci_probe, &ddi); 403 - else 404 - error = local_pci_probe(&ddi); 407 + free_cpumask_var(wq_domain_mask); 408 + destroy_work_on_stack(&arg.work); 409 + } 405 410 out: 406 411 dev->is_probed = 0; 407 412 cpu_hotplug_enable();