Commit 91678f37 authored by Tim Sell's avatar Tim Sell Committed by Greg Kroah-Hartman

staging: unisys: visornic: prevent faults if driver re-loaded during stress

Prevent faults that would occur during this sequence of activity during
network stress:

    rmmod visornic
    modprobe visornic
    /etc/init.d/network restart

The problem fixed was that the back-end IO partition was holding onto
stale receive buffers after the "rmmod visornic", and erroneously
completing them after a subsequent "modprobe visornic".  This is fixed
in this patch as follows:

* Tell the back-end IO partition that we want it to employ its
  "incarnation mechanism" to ensure it does not complete stale receive
  buffers after the guest virtual device environment changes (e.g., by
  re-loading the driver), by setting the
  ULTRA_IO_DRIVER_SUPPORTS_ENHANCED_RCVBUF_CHECKING feature bit, and
  supplying a unique incarnation number in rcvpost.unique_num for each
  receive buffer posted.

* When visornic loads, make sure we drain and ignore any possible-stale
  data in the channel before beginning network operation.

Prior to this patch, faults like this would occur almost every time if
you attempted to rmmod + modprobe the visornic driver and restart the
network service during heavy network activity:

    BUG: spinlock bad magic on CPU#0, ksoftirqd/0/3
     lock: 0xffff88002d8a56d8, .magic: ffff8800, .owner: <none>/-1,
                               .owner_cpu: 2304
    CPU: 0 PID: 3 Comm: ksoftirqd/0 Tainted: G         C
           4.3.0-rc3-ARCH+ #74
Signed-off-by: default avatarTim Sell <timothy.sell@unisys.com>
Signed-off-by: default avatarBenjamin Romer <benjamin.romer@unisys.com>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent 5e757bc5
...@@ -120,7 +120,7 @@ struct visornic_devdata { ...@@ -120,7 +120,7 @@ struct visornic_devdata {
atomic_t interrupt_rcvd; atomic_t interrupt_rcvd;
wait_queue_head_t rsp_queue; wait_queue_head_t rsp_queue;
struct sk_buff **rcvbuf; struct sk_buff **rcvbuf;
u64 uniquenum; /* TODO figure out why not used */ u64 incarnation_id; /* lets IOPART know about re-birth */
unsigned short old_flags; /* flags as they were prior to unsigned short old_flags; /* flags as they were prior to
* set_multicast_list * set_multicast_list
*/ */
...@@ -431,7 +431,7 @@ post_skb(struct uiscmdrsp *cmdrsp, ...@@ -431,7 +431,7 @@ post_skb(struct uiscmdrsp *cmdrsp,
cmdrsp->net.rcvpost.frag.pi_off = cmdrsp->net.rcvpost.frag.pi_off =
(unsigned long)skb->data & PI_PAGE_MASK; (unsigned long)skb->data & PI_PAGE_MASK;
cmdrsp->net.rcvpost.frag.pi_len = skb->len; cmdrsp->net.rcvpost.frag.pi_len = skb->len;
cmdrsp->net.rcvpost.unique_num = devdata->uniquenum; cmdrsp->net.rcvpost.unique_num = devdata->incarnation_id;
if ((cmdrsp->net.rcvpost.frag.pi_off + skb->len) <= PI_PAGE_SIZE) { if ((cmdrsp->net.rcvpost.frag.pi_off + skb->len) <= PI_PAGE_SIZE) {
cmdrsp->net.type = NET_RCV_POST; cmdrsp->net.type = NET_RCV_POST;
...@@ -1365,6 +1365,7 @@ devdata_initialize(struct visornic_devdata *devdata, struct visor_device *dev) ...@@ -1365,6 +1365,7 @@ devdata_initialize(struct visornic_devdata *devdata, struct visor_device *dev)
return NULL; return NULL;
memset(devdata, '\0', sizeof(struct visornic_devdata)); memset(devdata, '\0', sizeof(struct visornic_devdata));
devdata->dev = dev; devdata->dev = dev;
devdata->incarnation_id = get_jiffies_64();
return devdata; return devdata;
} }
...@@ -1588,7 +1589,21 @@ send_rcv_posts_if_needed(struct visornic_devdata *devdata) ...@@ -1588,7 +1589,21 @@ send_rcv_posts_if_needed(struct visornic_devdata *devdata)
} }
/** /**
* draing_queue - drains the response queue * drain_resp_queue - drains and ignores all messages from the resp queue
* @cmdrsp: io channel command response message
* @devdata: visornic device to drain
*/
static void
drain_resp_queue(struct uiscmdrsp *cmdrsp, struct visornic_devdata *devdata)
{
while (visorchannel_signalremove(devdata->dev->visorchannel,
IOCHAN_FROM_IOPART,
cmdrsp))
;
}
/**
* service_resp_queue - drains the response queue
* @cmdrsp: io channel command response message * @cmdrsp: io channel command response message
* @devdata: visornic device to drain * @devdata: visornic device to drain
* *
...@@ -1777,6 +1792,8 @@ static int visornic_probe(struct visor_device *dev) ...@@ -1777,6 +1792,8 @@ static int visornic_probe(struct visor_device *dev)
err = -ENOMEM; err = -ENOMEM;
goto cleanup_netdev; goto cleanup_netdev;
} }
/* don't trust messages laying around in the channel */
drain_resp_queue(devdata->cmdrsp, devdata);
devdata->netdev = netdev; devdata->netdev = netdev;
dev_set_drvdata(&dev->device, devdata); dev_set_drvdata(&dev->device, devdata);
...@@ -1868,6 +1885,7 @@ static int visornic_probe(struct visor_device *dev) ...@@ -1868,6 +1885,7 @@ static int visornic_probe(struct visor_device *dev)
} }
features |= ULTRA_IO_CHANNEL_IS_POLLING; features |= ULTRA_IO_CHANNEL_IS_POLLING;
features |= ULTRA_IO_DRIVER_SUPPORTS_ENHANCED_RCVBUF_CHECKING;
err = visorbus_write_channel(dev, channel_offset, &features, 8); err = visorbus_write_channel(dev, channel_offset, &features, 8);
if (err) { if (err) {
dev_err(&dev->device, dev_err(&dev->device,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment