Skip to content

Commit 6b5c13b

Browse files
niklas88davem330
authored andcommitted
s390/ism: Fix locking for forwarding of IRQs and events to clients
The clients array references all registered clients and is protected by the clients_lock. Besides its use as general list of clients the clients array is accessed in ism_handle_irq() to forward ISM device events to clients. While the clients_lock is taken in the IRQ handler when calling handle_event() it is however incorrectly not held during the client->handle_irq() call and for the preceding clients[] access leaving it unprotected against concurrent client (un-)registration. Furthermore the accesses to ism->sba_client_arr[] in ism_register_dmb() and ism_unregister_dmb() are not protected by any lock. This is especially problematic as the client ID from the ism->sba_client_arr[] is not checked against NO_CLIENT and neither is the client pointer checked. Instead of expanding the use of the clients_lock further add a separate array in struct ism_dev which references clients subscribed to the device's events and IRQs. This array is protected by ism->lock which is already taken in ism_handle_irq() and can be taken outside the IRQ handler when adding/removing subscribers or the accessing ism->sba_client_arr[]. This also means that the clients_lock is no longer taken in IRQ context. Fixes: 89e7d2b ("net/ism: Add new API for client registration") Signed-off-by: Niklas Schnelle <schnelle@linux.ibm.com> Reviewed-by: Alexandra Winter <wintera@linux.ibm.com> Signed-off-by: David S. Miller <davem@davemloft.net>
1 parent c329b26 commit 6b5c13b

2 files changed

Lines changed: 37 additions & 8 deletions

File tree

drivers/s390/net/ism_drv.c

Lines changed: 36 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,15 @@ static struct ism_dev_list ism_dev_list = {
4747
.mutex = __MUTEX_INITIALIZER(ism_dev_list.mutex),
4848
};
4949

50+
static void ism_setup_forwarding(struct ism_client *client, struct ism_dev *ism)
51+
{
52+
unsigned long flags;
53+
54+
spin_lock_irqsave(&ism->lock, flags);
55+
ism->subs[client->id] = client;
56+
spin_unlock_irqrestore(&ism->lock, flags);
57+
}
58+
5059
int ism_register_client(struct ism_client *client)
5160
{
5261
struct ism_dev *ism;
@@ -71,6 +80,7 @@ int ism_register_client(struct ism_client *client)
7180
list_for_each_entry(ism, &ism_dev_list.list, list) {
7281
ism->priv[i] = NULL;
7382
client->add(ism);
83+
ism_setup_forwarding(client, ism);
7484
}
7585
}
7686
mutex_unlock(&ism_dev_list.mutex);
@@ -92,6 +102,9 @@ int ism_unregister_client(struct ism_client *client)
92102
max_client--;
93103
spin_unlock_irqrestore(&clients_lock, flags);
94104
list_for_each_entry(ism, &ism_dev_list.list, list) {
105+
spin_lock_irqsave(&ism->lock, flags);
106+
/* Stop forwarding IRQs and events */
107+
ism->subs[client->id] = NULL;
95108
for (int i = 0; i < ISM_NR_DMBS; ++i) {
96109
if (ism->sba_client_arr[i] == client->id) {
97110
pr_err("%s: attempt to unregister client '%s'"
@@ -101,6 +114,7 @@ int ism_unregister_client(struct ism_client *client)
101114
goto out;
102115
}
103116
}
117+
spin_unlock_irqrestore(&ism->lock, flags);
104118
}
105119
out:
106120
mutex_unlock(&ism_dev_list.mutex);
@@ -328,6 +342,7 @@ int ism_register_dmb(struct ism_dev *ism, struct ism_dmb *dmb,
328342
struct ism_client *client)
329343
{
330344
union ism_reg_dmb cmd;
345+
unsigned long flags;
331346
int ret;
332347

333348
ret = ism_alloc_dmb(ism, dmb);
@@ -351,7 +366,9 @@ int ism_register_dmb(struct ism_dev *ism, struct ism_dmb *dmb,
351366
goto out;
352367
}
353368
dmb->dmb_tok = cmd.response.dmb_tok;
369+
spin_lock_irqsave(&ism->lock, flags);
354370
ism->sba_client_arr[dmb->sba_idx - ISM_DMB_BIT_OFFSET] = client->id;
371+
spin_unlock_irqrestore(&ism->lock, flags);
355372
out:
356373
return ret;
357374
}
@@ -360,6 +377,7 @@ EXPORT_SYMBOL_GPL(ism_register_dmb);
360377
int ism_unregister_dmb(struct ism_dev *ism, struct ism_dmb *dmb)
361378
{
362379
union ism_unreg_dmb cmd;
380+
unsigned long flags;
363381
int ret;
364382

365383
memset(&cmd, 0, sizeof(cmd));
@@ -368,7 +386,9 @@ int ism_unregister_dmb(struct ism_dev *ism, struct ism_dmb *dmb)
368386

369387
cmd.request.dmb_tok = dmb->dmb_tok;
370388

389+
spin_lock_irqsave(&ism->lock, flags);
371390
ism->sba_client_arr[dmb->sba_idx - ISM_DMB_BIT_OFFSET] = NO_CLIENT;
391+
spin_unlock_irqrestore(&ism->lock, flags);
372392

373393
ret = ism_cmd(ism, &cmd);
374394
if (ret && ret != ISM_ERROR)
@@ -491,6 +511,7 @@ static u16 ism_get_chid(struct ism_dev *ism)
491511
static void ism_handle_event(struct ism_dev *ism)
492512
{
493513
struct ism_event *entry;
514+
struct ism_client *clt;
494515
int i;
495516

496517
while ((ism->ieq_idx + 1) != READ_ONCE(ism->ieq->header.idx)) {
@@ -499,21 +520,21 @@ static void ism_handle_event(struct ism_dev *ism)
499520

500521
entry = &ism->ieq->entry[ism->ieq_idx];
501522
debug_event(ism_debug_info, 2, entry, sizeof(*entry));
502-
spin_lock(&clients_lock);
503-
for (i = 0; i < max_client; ++i)
504-
if (clients[i])
505-
clients[i]->handle_event(ism, entry);
506-
spin_unlock(&clients_lock);
523+
for (i = 0; i < max_client; ++i) {
524+
clt = ism->subs[i];
525+
if (clt)
526+
clt->handle_event(ism, entry);
527+
}
507528
}
508529
}
509530

510531
static irqreturn_t ism_handle_irq(int irq, void *data)
511532
{
512533
struct ism_dev *ism = data;
513-
struct ism_client *clt;
514534
unsigned long bit, end;
515535
unsigned long *bv;
516536
u16 dmbemask;
537+
u8 client_id;
517538

518539
bv = (void *) &ism->sba->dmb_bits[ISM_DMB_WORD_OFFSET];
519540
end = sizeof(ism->sba->dmb_bits) * BITS_PER_BYTE - ISM_DMB_BIT_OFFSET;
@@ -530,8 +551,10 @@ static irqreturn_t ism_handle_irq(int irq, void *data)
530551
dmbemask = ism->sba->dmbe_mask[bit + ISM_DMB_BIT_OFFSET];
531552
ism->sba->dmbe_mask[bit + ISM_DMB_BIT_OFFSET] = 0;
532553
barrier();
533-
clt = clients[ism->sba_client_arr[bit]];
534-
clt->handle_irq(ism, bit + ISM_DMB_BIT_OFFSET, dmbemask);
554+
client_id = ism->sba_client_arr[bit];
555+
if (unlikely(client_id == NO_CLIENT || !ism->subs[client_id]))
556+
continue;
557+
ism->subs[client_id]->handle_irq(ism, bit + ISM_DMB_BIT_OFFSET, dmbemask);
535558
}
536559

537560
if (ism->sba->e) {
@@ -554,6 +577,7 @@ static void ism_dev_add_work_func(struct work_struct *work)
554577
add_work);
555578

556579
client->add(client->tgt_ism);
580+
ism_setup_forwarding(client, client->tgt_ism);
557581
atomic_dec(&client->tgt_ism->add_dev_cnt);
558582
wake_up(&client->tgt_ism->waitq);
559583
}
@@ -691,7 +715,11 @@ static void ism_dev_remove_work_func(struct work_struct *work)
691715
{
692716
struct ism_client *client = container_of(work, struct ism_client,
693717
remove_work);
718+
unsigned long flags;
694719

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);
695723
client->remove(client->tgt_ism);
696724
atomic_dec(&client->tgt_ism->free_clients_cnt);
697725
wake_up(&client->tgt_ism->waitq);

include/linux/ism.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ struct ism_dev {
4444
u64 local_gid;
4545
int ieq_idx;
4646

47+
struct ism_client *subs[MAX_CLIENTS];
4748
atomic_t free_clients_cnt;
4849
atomic_t add_dev_cnt;
4950
wait_queue_head_t waitq;

0 commit comments

Comments
 (0)