Skip to content

Commit 76631ff

Browse files
niklas88davem330
authored andcommitted
s390/ism: Fix and simplify add()/remove() callback handling
Previously the clients_lock was protecting the clients array against concurrent addition/removal of clients but was also accessed from IRQ context. This meant that it had to be a spinlock and that the add() and remove() callbacks in which clients need to do allocation and take mutexes can't be called under the clients_lock. To work around this these callbacks were moved to workqueues. This not only introduced significant complexity but is also subtly broken in at least one way. In ism_dev_init() and ism_dev_exit() clients[i]->tgt_ism is used to communicate the added/removed ISM device to the work function. While write access to client[i]->tgt_ism is protected by the clients_lock and the code waits that there is no pending add/remove work before and after setting clients[i]->tgt_ism this is not enough. The problem is that the wait happens based on per ISM device counters. Thus a concurrent ism_dev_init()/ism_dev_exit() for a different ISM device may overwrite a clients[i]->tgt_ism between unlocking the clients_lock and the subsequent wait for the work to finnish. Thankfully with the clients_lock no longer held in IRQ context it can be turned into a mutex which can be held during the calls to add()/remove() completely removing the need for the workqueues and the associated broken housekeeping including the per ISM device counters and the clients[i]->tgt_ism. Fixes: 89e7d2b ("net/ism: Add new API for client registration") Signed-off-by: Niklas Schnelle <schnelle@linux.ibm.com> Signed-off-by: David S. Miller <davem@davemloft.net>
1 parent 6b5c13b commit 76631ff

2 files changed

Lines changed: 24 additions & 68 deletions

File tree

drivers/s390/net/ism_drv.c

Lines changed: 24 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ static const struct smcd_ops ism_ops;
3636
static struct ism_client *clients[MAX_CLIENTS]; /* use an array rather than */
3737
/* a list for fast mapping */
3838
static u8 max_client;
39-
static DEFINE_SPINLOCK(clients_lock);
39+
static DEFINE_MUTEX(clients_lock);
4040
struct ism_dev_list {
4141
struct list_head list;
4242
struct mutex mutex; /* protects ism device list */
@@ -59,11 +59,10 @@ static void ism_setup_forwarding(struct ism_client *client, struct ism_dev *ism)
5959
int ism_register_client(struct ism_client *client)
6060
{
6161
struct ism_dev *ism;
62-
unsigned long flags;
6362
int i, rc = -ENOSPC;
6463

6564
mutex_lock(&ism_dev_list.mutex);
66-
spin_lock_irqsave(&clients_lock, flags);
65+
mutex_lock(&clients_lock);
6766
for (i = 0; i < MAX_CLIENTS; ++i) {
6867
if (!clients[i]) {
6968
clients[i] = client;
@@ -74,7 +73,8 @@ int ism_register_client(struct ism_client *client)
7473
break;
7574
}
7675
}
77-
spin_unlock_irqrestore(&clients_lock, flags);
76+
mutex_unlock(&clients_lock);
77+
7878
if (i < MAX_CLIENTS) {
7979
/* initialize with all devices that we got so far */
8080
list_for_each_entry(ism, &ism_dev_list.list, list) {
@@ -96,11 +96,11 @@ int ism_unregister_client(struct ism_client *client)
9696
int rc = 0;
9797

9898
mutex_lock(&ism_dev_list.mutex);
99-
spin_lock_irqsave(&clients_lock, flags);
99+
mutex_lock(&clients_lock);
100100
clients[client->id] = NULL;
101101
if (client->id + 1 == max_client)
102102
max_client--;
103-
spin_unlock_irqrestore(&clients_lock, flags);
103+
mutex_unlock(&clients_lock);
104104
list_for_each_entry(ism, &ism_dev_list.list, list) {
105105
spin_lock_irqsave(&ism->lock, flags);
106106
/* Stop forwarding IRQs and events */
@@ -571,21 +571,9 @@ static u64 ism_get_local_gid(struct ism_dev *ism)
571571
return ism->local_gid;
572572
}
573573

574-
static void ism_dev_add_work_func(struct work_struct *work)
575-
{
576-
struct ism_client *client = container_of(work, struct ism_client,
577-
add_work);
578-
579-
client->add(client->tgt_ism);
580-
ism_setup_forwarding(client, client->tgt_ism);
581-
atomic_dec(&client->tgt_ism->add_dev_cnt);
582-
wake_up(&client->tgt_ism->waitq);
583-
}
584-
585574
static int ism_dev_init(struct ism_dev *ism)
586575
{
587576
struct pci_dev *pdev = ism->pdev;
588-
unsigned long flags;
589577
int i, ret;
590578

591579
ret = pci_alloc_irq_vectors(pdev, 1, 1, PCI_IRQ_MSI);
@@ -618,25 +606,16 @@ static int ism_dev_init(struct ism_dev *ism)
618606
/* hardware is V2 capable */
619607
ism_create_system_eid();
620608

621-
init_waitqueue_head(&ism->waitq);
622-
atomic_set(&ism->free_clients_cnt, 0);
623-
atomic_set(&ism->add_dev_cnt, 0);
624-
625-
wait_event(ism->waitq, !atomic_read(&ism->add_dev_cnt));
626-
spin_lock_irqsave(&clients_lock, flags);
627-
for (i = 0; i < max_client; ++i)
609+
mutex_lock(&ism_dev_list.mutex);
610+
mutex_lock(&clients_lock);
611+
for (i = 0; i < max_client; ++i) {
628612
if (clients[i]) {
629-
INIT_WORK(&clients[i]->add_work,
630-
ism_dev_add_work_func);
631-
clients[i]->tgt_ism = ism;
632-
atomic_inc(&ism->add_dev_cnt);
633-
schedule_work(&clients[i]->add_work);
613+
clients[i]->add(ism);
614+
ism_setup_forwarding(clients[i], ism);
634615
}
635-
spin_unlock_irqrestore(&clients_lock, flags);
636-
637-
wait_event(ism->waitq, !atomic_read(&ism->add_dev_cnt));
616+
}
617+
mutex_unlock(&clients_lock);
638618

639-
mutex_lock(&ism_dev_list.mutex);
640619
list_add(&ism->list, &ism_dev_list.list);
641620
mutex_unlock(&ism_dev_list.mutex);
642621

@@ -711,40 +690,24 @@ static int ism_probe(struct pci_dev *pdev, const struct pci_device_id *id)
711690
return ret;
712691
}
713692

714-
static void ism_dev_remove_work_func(struct work_struct *work)
715-
{
716-
struct ism_client *client = container_of(work, struct ism_client,
717-
remove_work);
718-
unsigned long flags;
719-
720-
spin_lock_irqsave(&client->tgt_ism->lock, flags);
721-
client->tgt_ism->subs[client->id] = NULL;
722-
spin_unlock_irqrestore(&client->tgt_ism->lock, flags);
723-
client->remove(client->tgt_ism);
724-
atomic_dec(&client->tgt_ism->free_clients_cnt);
725-
wake_up(&client->tgt_ism->waitq);
726-
}
727-
728-
/* Callers must hold ism_dev_list.mutex */
729693
static void ism_dev_exit(struct ism_dev *ism)
730694
{
731695
struct pci_dev *pdev = ism->pdev;
732696
unsigned long flags;
733697
int i;
734698

735-
wait_event(ism->waitq, !atomic_read(&ism->free_clients_cnt));
736-
spin_lock_irqsave(&clients_lock, flags);
699+
spin_lock_irqsave(&ism->lock, flags);
737700
for (i = 0; i < max_client; ++i)
738-
if (clients[i]) {
739-
INIT_WORK(&clients[i]->remove_work,
740-
ism_dev_remove_work_func);
741-
clients[i]->tgt_ism = ism;
742-
atomic_inc(&ism->free_clients_cnt);
743-
schedule_work(&clients[i]->remove_work);
744-
}
745-
spin_unlock_irqrestore(&clients_lock, flags);
701+
ism->subs[i] = NULL;
702+
spin_unlock_irqrestore(&ism->lock, flags);
746703

747-
wait_event(ism->waitq, !atomic_read(&ism->free_clients_cnt));
704+
mutex_lock(&ism_dev_list.mutex);
705+
mutex_lock(&clients_lock);
706+
for (i = 0; i < max_client; ++i) {
707+
if (clients[i])
708+
clients[i]->remove(ism);
709+
}
710+
mutex_unlock(&clients_lock);
748711

749712
if (SYSTEM_EID.serial_number[0] != '0' ||
750713
SYSTEM_EID.type[0] != '0')
@@ -755,15 +718,14 @@ static void ism_dev_exit(struct ism_dev *ism)
755718
kfree(ism->sba_client_arr);
756719
pci_free_irq_vectors(pdev);
757720
list_del_init(&ism->list);
721+
mutex_unlock(&ism_dev_list.mutex);
758722
}
759723

760724
static void ism_remove(struct pci_dev *pdev)
761725
{
762726
struct ism_dev *ism = dev_get_drvdata(&pdev->dev);
763727

764-
mutex_lock(&ism_dev_list.mutex);
765728
ism_dev_exit(ism);
766-
mutex_unlock(&ism_dev_list.mutex);
767729

768730
pci_release_mem_regions(pdev);
769731
pci_disable_device(pdev);

include/linux/ism.h

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -45,9 +45,6 @@ struct ism_dev {
4545
int ieq_idx;
4646

4747
struct ism_client *subs[MAX_CLIENTS];
48-
atomic_t free_clients_cnt;
49-
atomic_t add_dev_cnt;
50-
wait_queue_head_t waitq;
5148
};
5249

5350
struct ism_event {
@@ -69,9 +66,6 @@ struct ism_client {
6966
*/
7067
void (*handle_irq)(struct ism_dev *dev, unsigned int bit, u16 dmbemask);
7168
/* Private area - don't touch! */
72-
struct work_struct remove_work;
73-
struct work_struct add_work;
74-
struct ism_dev *tgt_ism;
7569
u8 id;
7670
};
7771

0 commit comments

Comments
 (0)