Conversation

Takashi Sakamoto (坂本 貴史)

Edited 1 month ago
static void iso_resource_auto_work(struct work_struct *work)
{
        struct iso_resource_auto *r = from_work(r, work, work.work);
        struct client *client = r->client;
        unsigned long index = r->resource.handle;
        u64 reset_jiffies;
        struct iso_resource_event *e;
        int resource_generation, current_generation;
        int channel, bandwidth, todo;
        bool free = false;

        scoped_guard(spinlock_irq, &client->device->card->lock) {
                reset_jiffies = client->device->card->reset_jiffies;
                current_generation = client->device->generation;
        }

        scoped_guard(spinlock_irq, &client->lock) {
                resource_generation = r->generation;
                r->generation = current_generation;
                todo = r->todo;
        }

        switch (todo) {
        case ISO_RES_AUTO_ALLOC:
                // Allow 1000ms grace period for other reallocations.
                if (time_is_after_jiffies64(reset_jiffies + secs_to_jiffies(1))) {
                        scoped_guard(spinlock_irq, &client->lock)
                                schedule_iso_resource_auto(r, msecs_to_jiffies(333));
                        goto out;
                }
                break;
        case ISO_RES_AUTO_REALLOC:
                // We could be called twice within the same generation.
                if (resource_generation == current_generation)
                        goto out;
                break;
        case ISO_RES_AUTO_DEALLOC:
        default:
                break;
        }

        bandwidth = r->bandwidth;

        fw_iso_resource_manage(client->device->card, current_generation,
                        r->channels, &channel, &bandwidth,
                        todo == ISO_RES_AUTO_ALLOC ||
                        todo == ISO_RES_AUTO_REALLOC);

        if (todo == ISO_RES_AUTO_DEALLOC) {
                e = no_free_ptr(r->e_dealloc);
        } else {
                // Is this generation outdated already?  As long as this resource sticks in the
                // xarray, it will be scheduled again for a newer generation or at shutdown.
                if (channel == -EAGAIN)
                        goto out;

                // Finishes successfully.
                if (channel >= 0 || bandwidth > 0) {
                        // Generate no event at reallocation.
                        if (todo == ISO_RES_AUTO_REALLOC)
                                goto out;

                        scoped_guard(spinlock_irq, &client->lock) {
                                // Transit from allocation to reallocation, except if the client
                                // requested deallocation in the meantime.
                                r->todo = ISO_RES_AUTO_REALLOC;
                                r->channels = 1ULL << channel;
                        }
                } else {
                        // Allocation or reallocation failure?  Pull this resource out of the
                        // xarray and prepare for deletion, unless the client is shutting down.
                        scoped_guard(spinlock_irq, &client->lock) {
                                if (!client->in_shutdown && xa_erase(&client->resource_xa, index)) {
                                        client_put(client);
                                        free = true;
                                }
                        }
                }

                e = no_free_ptr(r->e_alloc);
        }

        e->iso_resource.handle    = r->resource.handle;
        e->iso_resource.channel   = channel;
        e->iso_resource.bandwidth = bandwidth;

        queue_event(client, &e->event,
                    &e->iso_resource, sizeof(e->iso_resource), NULL, 0);

        if (free) {
                cancel_delayed_work(&r->work);
                kfree(r->e_alloc);
                kfree(r->e_dealloc);
                kfree(r);
        }
 out:
        client_put(client);
}

static void iso_resource_once_work(struct work_struct *work)
{
        struct iso_resource_once *r = from_work(r, work, work.work);
        struct client *client = r->client;
        struct iso_resource_event *e;
        int generation, channel, bandwidth;

        generation = client->device->generation;

        bandwidth = r->bandwidth;
        r->generation = generation;

        fw_iso_resource_manage(client->device->card, generation, r->channels, &channel, &bandwidth,
                               r->todo == ISO_RES_ONCE_ALLOC);

        e = no_free_ptr(r->event);
        e->iso_resource.channel   = channel;
        e->iso_resource.bandwidth = bandwidth;

        queue_event(client, &e->event, &e->iso_resource, sizeof(e->iso_resource), NULL, 0);

        cancel_delayed_work(&r->work);
        client_put(r->client);
        kfree(r);
}
0
0
0