Commit d7dfb07d authored by Linus Torvalds's avatar Linus Torvalds
Browse files

Merge tag 'firewire-updates-6.12' of...

Merge tag 'firewire-updates-6.12' of git://git.kernel.org/pub/scm/linux/kernel/git/ieee1394/linux1394

Pull firewire updates from Takashi Sakamoto:
 "In the FireWire subsystem, tasklets have been used as the bottom half
  of 1394 OHCi hardIRQ. In recent kernel updates, BH workqueues have
  become available, and some developers have proposed replacing the
  tasklet with a BH workqueue.

  As a first step towards dropping tasklet use, the 1394 OHCI
  isochronous context can use regular workqueues. In this context, the
  batch of packets is processed in the specific queue, thus the timing
  jitter caused by task scheduling is not so critical.

  Additionally, DMA transmission can be scheduled per-packet basis,
  therefore the context can be sleep between the operation of
  transmissions. Furthermore, in-kernel protocol implementation involves
  some CPU-bound tasks, which can sometimes consumes CPU time so long.
  These characteristics suggest that normal workqueues are suitable,
  through BH workqueues are not.

  The replacement with a workqueue allows unit drivers to process the
  content of packets in non-atomic context. It brings some reliefs to
  some drivers in sound subsystem that spin-lock is not mandatory
  anymore during isochronous packet processing.

  Summary:

   - Replace tasklet with workqueue for isochronous context

   - Replace IDR with XArray

   - Utilize guard macro where possible

   - Print deprecation warning when enabling debug parameter of
     firewire-ohci module

   - Switch to nonatomic PCM operation"

* tag 'firewire-updates-6.12' of git://git.kernel.org/pub/scm/linux/kernel/git/ieee1394/linux1394: (55 commits)
  firewire: core: rename cause flag of tracepoints event
  firewire: core: update documentation of kernel APIs for flushing completions
  firewire: core: add helper function to retire descriptors
  Revert "firewire: core: move workqueue handler from 1394 OHCI driver to core function"
  Revert "firewire: core: use mutex to coordinate concurrent calls to flush completions"
  firewire: core: use mutex to coordinate concurrent calls to flush completions
  firewire: core: move workqueue handler from 1394 OHCI driver to core function
  firewire: core: fulfill documentation of fw_iso_context_flush_completions()
  firewire: core: expose kernel API to schedule work item to process isochronous context
  firewire: core: use WARN_ON_ONCE() to avoid superfluous dumps
  ALSA: firewire: use nonatomic PCM operation
  firewire: core: non-atomic memory allocation for isochronous event to user client
  firewire: ohci: operate IT/IR events in sleepable work process instead of tasklet softIRQ
  firewire: core: add local API to queue work item to workqueue specific to isochronous contexts
  firewire: core: allocate workqueue to handle isochronous contexts in card
  firewire: ohci: obsolete direct usage of printk_ratelimit()
  firewire: ohci: deprecate debug parameter
  firewire: core: update fw_device outside of device_find_child()
  firewire: ohci: fix error path to detect initiated reset in TI TSB41BA3D phy
  firewire: core/ohci: minor refactoring for computation of configuration ROM size
  ...
parents 3a378723 f1cba521
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -43,6 +43,8 @@ Firewire core transaction interfaces
Firewire Isochronous I/O interfaces
===================================

.. kernel-doc:: include/linux/firewire.h
   :functions: fw_iso_context_schedule_flush_completions
.. kernel-doc:: drivers/firewire/core-iso.c
   :export:
+54 −37
Original line number Diff line number Diff line
@@ -168,7 +168,6 @@ static size_t required_space(struct fw_descriptor *desc)
int fw_core_add_descriptor(struct fw_descriptor *desc)
{
	size_t i;
	int ret;

	/*
	 * Check descriptor is valid; the length of all blocks in the
@@ -182,29 +181,25 @@ int fw_core_add_descriptor(struct fw_descriptor *desc)
	if (i != desc->length)
		return -EINVAL;

	mutex_lock(&card_mutex);
	guard(mutex)(&card_mutex);

	if (config_rom_length + required_space(desc) > 256)
		return -EBUSY;

	if (config_rom_length + required_space(desc) > 256) {
		ret = -EBUSY;
	} else {
	list_add_tail(&desc->link, &descriptor_list);
	config_rom_length += required_space(desc);
	descriptor_count++;
	if (desc->immediate > 0)
		descriptor_count++;
	update_config_roms();
		ret = 0;
	}

	mutex_unlock(&card_mutex);

	return ret;
	return 0;
}
EXPORT_SYMBOL(fw_core_add_descriptor);

void fw_core_remove_descriptor(struct fw_descriptor *desc)
{
	mutex_lock(&card_mutex);
	guard(mutex)(&card_mutex);

	list_del(&desc->link);
	config_rom_length -= required_space(desc);
@@ -212,8 +207,6 @@ void fw_core_remove_descriptor(struct fw_descriptor *desc)
	if (desc->immediate > 0)
		descriptor_count--;
	update_config_roms();

	mutex_unlock(&card_mutex);
}
EXPORT_SYMBOL(fw_core_remove_descriptor);

@@ -381,11 +374,11 @@ static void bm_work(struct work_struct *work)

		bm_id = be32_to_cpu(transaction_data[0]);

		spin_lock_irq(&card->lock);
		scoped_guard(spinlock_irq, &card->lock) {
			if (rcode == RCODE_COMPLETE && generation == card->generation)
				card->bm_node_id =
				    bm_id == 0x3f ? local_id : 0xffc0 | bm_id;
		spin_unlock_irq(&card->lock);
		}

		if (rcode == RCODE_COMPLETE && bm_id != 0x3f) {
			/* Somebody else is BM.  Only act as IRM. */
@@ -578,25 +571,47 @@ void fw_card_initialize(struct fw_card *card,
}
EXPORT_SYMBOL(fw_card_initialize);

int fw_card_add(struct fw_card *card,
		u32 max_receive, u32 link_speed, u64 guid)
int fw_card_add(struct fw_card *card, u32 max_receive, u32 link_speed, u64 guid,
		unsigned int supported_isoc_contexts)
{
	struct workqueue_struct *isoc_wq;
	int ret;

	// This workqueue should be:
	//  * != WQ_BH			Sleepable.
	//  * == WQ_UNBOUND		Any core can process data for isoc context. The
	//				implementation of unit protocol could consumes the core
	//				longer somehow.
	//  * != WQ_MEM_RECLAIM		Not used for any backend of block device.
	//  * == WQ_FREEZABLE		Isochronous communication is at regular interval in real
	//				time, thus should be drained if possible at freeze phase.
	//  * == WQ_HIGHPRI		High priority to process semi-realtime timestamped data.
	//  * == WQ_SYSFS		Parameters are available via sysfs.
	//  * max_active == n_it + n_ir	A hardIRQ could notify events for multiple isochronous
	//				contexts if they are scheduled to the same cycle.
	isoc_wq = alloc_workqueue("firewire-isoc-card%u",
				  WQ_UNBOUND | WQ_FREEZABLE | WQ_HIGHPRI | WQ_SYSFS,
				  supported_isoc_contexts, card->index);
	if (!isoc_wq)
		return -ENOMEM;

	card->max_receive = max_receive;
	card->link_speed = link_speed;
	card->guid = guid;

	mutex_lock(&card_mutex);
	guard(mutex)(&card_mutex);

	generate_config_rom(card, tmp_config_rom);
	ret = card->driver->enable(card, tmp_config_rom, config_rom_length);
	if (ret == 0)
		list_add_tail(&card->link, &card_list);
	if (ret < 0) {
		destroy_workqueue(isoc_wq);
		return ret;
	}

	mutex_unlock(&card_mutex);
	card->isoc_wq = isoc_wq;
	list_add_tail(&card->link, &card_list);

	return ret;
	return 0;
}
EXPORT_SYMBOL(fw_card_add);

@@ -714,29 +729,31 @@ EXPORT_SYMBOL_GPL(fw_card_release);
void fw_core_remove_card(struct fw_card *card)
{
	struct fw_card_driver dummy_driver = dummy_driver_template;
	unsigned long flags;

	might_sleep();

	card->driver->update_phy_reg(card, 4,
				     PHY_LINK_ACTIVE | PHY_CONTENDER, 0);
	fw_schedule_bus_reset(card, false, true);

	mutex_lock(&card_mutex);
	scoped_guard(mutex, &card_mutex)
		list_del_init(&card->link);
	mutex_unlock(&card_mutex);

	/* Switch off most of the card driver interface. */
	dummy_driver.free_iso_context	= card->driver->free_iso_context;
	dummy_driver.stop_iso		= card->driver->stop_iso;
	card->driver = &dummy_driver;
	drain_workqueue(card->isoc_wq);

	spin_lock_irqsave(&card->lock, flags);
	scoped_guard(spinlock_irqsave, &card->lock)
		fw_destroy_nodes(card);
	spin_unlock_irqrestore(&card->lock, flags);

	/* Wait for all users, especially device workqueue jobs, to finish. */
	fw_card_put(card);
	wait_for_completion(&card->done);

	destroy_workqueue(card->isoc_wq);

	WARN_ON(!list_empty(&card->transaction_list));
}
EXPORT_SYMBOL(fw_core_remove_card);
+192 −208

File changed.

Preview size limit exceeded, changes collapsed.

+92 −110

File changed.

Preview size limit exceeded, changes collapsed.

+44 −5
Original line number Diff line number Diff line
@@ -209,23 +209,63 @@ void fw_iso_context_queue_flush(struct fw_iso_context *ctx)
}
EXPORT_SYMBOL(fw_iso_context_queue_flush);

/**
 * fw_iso_context_flush_completions() - process isochronous context in current process context.
 * @ctx: the isochronous context
 *
 * Process the isochronous context in the current process context. The registered callback function
 * is called when a queued packet buffer with the interrupt flag is completed, either after
 * transmission in the IT context or after being filled in the IR context. Additionally, the
 * callback function is also called for the packet buffer completed at last. Furthermore, the
 * callback function is called as well when the header buffer in the context becomes full. If it is
 * required to process the context asynchronously, fw_iso_context_schedule_flush_completions() is
 * available instead.
 *
 * Context: Process context. May sleep due to disable_work_sync().
 */
int fw_iso_context_flush_completions(struct fw_iso_context *ctx)
{
	int err;

	trace_isoc_outbound_flush_completions(ctx);
	trace_isoc_inbound_single_flush_completions(ctx);
	trace_isoc_inbound_multiple_flush_completions(ctx);

	return ctx->card->driver->flush_iso_completions(ctx);
	might_sleep();

	// Avoid dead lock due to programming mistake.
	if (WARN_ON_ONCE(current_work() == &ctx->work))
		return 0;

	disable_work_sync(&ctx->work);

	err = ctx->card->driver->flush_iso_completions(ctx);

	enable_work(&ctx->work);

	return err;
}
EXPORT_SYMBOL(fw_iso_context_flush_completions);

int fw_iso_context_stop(struct fw_iso_context *ctx)
{
	int err;

	trace_isoc_outbound_stop(ctx);
	trace_isoc_inbound_single_stop(ctx);
	trace_isoc_inbound_multiple_stop(ctx);

	return ctx->card->driver->stop_iso(ctx);
	might_sleep();

	// Avoid dead lock due to programming mistake.
	if (WARN_ON_ONCE(current_work() == &ctx->work))
		return 0;

	err = ctx->card->driver->stop_iso(ctx);

	cancel_work_sync(&ctx->work);

	return err;
}
EXPORT_SYMBOL(fw_iso_context_stop);

@@ -375,9 +415,8 @@ void fw_iso_resource_manage(struct fw_card *card, int generation,
	u32 channels_lo = channels_mask >> 32;	/* channels 63...32 */
	int irm_id, ret, c = -EINVAL;

	spin_lock_irq(&card->lock);
	scoped_guard(spinlock_irq, &card->lock)
		irm_id = card->irm_node->node_id;
	spin_unlock_irq(&card->lock);

	if (channels_hi)
		c = manage_channel(card, irm_id, generation, channels_hi,
Loading