/** * fw_iso_resources_allocate - allocate isochronous channel and bandwidth * @r: the resource manager * @max_payload_bytes: the amount of data (including CIP headers) per packet * @speed: the speed (e.g., SCODE_400) at which the packets will be sent * * This function allocates one isochronous channel and enough bandwidth for the * specified packet size. * * Returns the channel number that the caller must use for streaming, or * a negative error code. Due to potentionally long delays, this function is * interruptible and can return -ERESTARTSYS. On success, the caller is * responsible for calling fw_iso_resources_update() on bus resets, and * fw_iso_resources_free() when the resources are not longer needed. */ int fw_iso_resources_allocate(struct fw_iso_resources *r, unsigned int max_payload_bytes, int speed) { struct fw_card *card = fw_parent_device(r->unit)->card; int bandwidth, channel, err; if (WARN_ON(r->allocated)) return -EBADFD; r->bandwidth = packet_bandwidth(max_payload_bytes, speed); retry_after_bus_reset: spin_lock_irq(&card->lock); r->generation = card->generation; r->bandwidth_overhead = current_bandwidth_overhead(card); spin_unlock_irq(&card->lock); err = wait_isoch_resource_delay_after_bus_reset(card); if (err < 0) return err; mutex_lock(&r->mutex); bandwidth = r->bandwidth + r->bandwidth_overhead; fw_iso_resource_manage(card, r->generation, r->channels_mask, &channel, &bandwidth, true); if (channel == -EAGAIN) { mutex_unlock(&r->mutex); goto retry_after_bus_reset; } if (channel >= 0) { r->channel = channel; r->allocated = true; } else { if (channel == -EBUSY) dev_err(&r->unit->device, "isochronous resources exhausted\n"); else dev_err(&r->unit->device, "isochronous resource allocation failed\n"); } mutex_unlock(&r->mutex); return channel; }
/** * fw_iso_resources_free - frees allocated resources * @r: the resource manager * * This function deallocates the channel and bandwidth, if allocated. */ void fw_iso_resources_free(struct fw_iso_resources *r) { struct fw_card *card = fw_parent_device(r->unit)->card; int bandwidth, channel; mutex_lock(&r->mutex); if (r->allocated) { bandwidth = r->bandwidth + r->bandwidth_overhead; fw_iso_resource_manage(card, r->generation, 1uLL << r->channel, &channel, &bandwidth, false); if (channel < 0) dev_err(&r->unit->device, "isochronous resource deallocation failed\n"); r->allocated = false; } mutex_unlock(&r->mutex); }
/** * fw_iso_resources_update - update resource allocations after a bus reset * @r: the resource manager * * This function must be called from the driver's .update handler to reallocate * any resources that were allocated before the bus reset. It is safe to call * this function if no resources are currently allocated. * * Returns a negative error code on failure. If this happens, the caller must * stop streaming. */ int fw_iso_resources_update(struct fw_iso_resources *r) { struct fw_card *card = fw_parent_device(r->unit)->card; int bandwidth, channel; mutex_lock(&r->mutex); if (!r->allocated) { mutex_unlock(&r->mutex); return 0; } spin_lock_irq(&card->lock); r->generation = card->generation; r->bandwidth_overhead = current_bandwidth_overhead(card); spin_unlock_irq(&card->lock); bandwidth = r->bandwidth + r->bandwidth_overhead; fw_iso_resource_manage(card, r->generation, 1uLL << r->channel, &channel, &bandwidth, true); /* * When another bus reset happens, pretend that the allocation * succeeded; we will try again for the new generation later. */ if (channel < 0 && channel != -EAGAIN) { r->allocated = false; if (channel == -EBUSY) dev_err(&r->unit->device, "isochronous resources exhausted\n"); else dev_err(&r->unit->device, "isochronous resource allocation failed\n"); } mutex_unlock(&r->mutex); return channel; }
static void iso_resource_work(struct work_struct *work) { struct iso_resource_event *e; struct iso_resource *r = container_of(work, struct iso_resource, work.work); struct client *client = r->client; int generation, channel, bandwidth, todo; bool skip, free, success; spin_lock_irq(&client->lock); generation = client->device->generation; todo = r->todo; /* Allow 1000ms grace period for other reallocations. */ if (todo == ISO_RES_ALLOC && time_is_after_jiffies(client->device->card->reset_jiffies + HZ)) { if (schedule_delayed_work(&r->work, DIV_ROUND_UP(HZ, 3))) client_get(client); skip = true; } else { /* We could be called twice within the same generation. */ skip = todo == ISO_RES_REALLOC && r->generation == generation; } free = todo == ISO_RES_DEALLOC || todo == ISO_RES_ALLOC_ONCE || todo == ISO_RES_DEALLOC_ONCE; r->generation = generation; spin_unlock_irq(&client->lock); if (skip) goto out; bandwidth = r->bandwidth; fw_iso_resource_manage(client->device->card, generation, r->channels, &channel, &bandwidth, todo == ISO_RES_ALLOC || todo == ISO_RES_REALLOC || todo == ISO_RES_ALLOC_ONCE, r->transaction_data); /* * Is this generation outdated already? As long as this resource sticks * in the idr, it will be scheduled again for a newer generation or at * shutdown. */ if (channel == -EAGAIN && (todo == ISO_RES_ALLOC || todo == ISO_RES_REALLOC)) goto out; success = channel >= 0 || bandwidth > 0; spin_lock_irq(&client->lock); /* * Transit from allocation to reallocation, except if the client * requested deallocation in the meantime. */ if (r->todo == ISO_RES_ALLOC) r->todo = ISO_RES_REALLOC; /* * Allocation or reallocation failure? Pull this resource out of the * idr and prepare for deletion, unless the client is shutting down. */ if (r->todo == ISO_RES_REALLOC && !success && !client->in_shutdown && idr_find(&client->resource_idr, r->resource.handle)) { idr_remove(&client->resource_idr, r->resource.handle); client_put(client); free = true; } spin_unlock_irq(&client->lock); if (todo == ISO_RES_ALLOC && channel >= 0) r->channels = 1ULL << channel; if (todo == ISO_RES_REALLOC && success) goto out; if (todo == ISO_RES_ALLOC || todo == ISO_RES_ALLOC_ONCE) { e = r->e_alloc; r->e_alloc = NULL; } else { e = r->e_dealloc; r->e_dealloc = NULL; } e->resource.handle = r->resource.handle; e->resource.channel = channel; e->resource.bandwidth = bandwidth; queue_event(client, &e->event, &e->resource, sizeof(e->resource), NULL, 0); if (free) { cancel_delayed_work(&r->work); kfree(r->e_alloc); kfree(r->e_dealloc); kfree(r); } out: client_put(client); }