summaryrefslogtreecommitdiff
path: root/usr/src/uts/common/inet/arp/arp.c
diff options
context:
space:
mode:
Diffstat (limited to 'usr/src/uts/common/inet/arp/arp.c')
-rw-r--r--usr/src/uts/common/inet/arp/arp.c214
1 files changed, 147 insertions, 67 deletions
diff --git a/usr/src/uts/common/inet/arp/arp.c b/usr/src/uts/common/inet/arp/arp.c
index e52655dd47..abdbc39a47 100644
--- a/usr/src/uts/common/inet/arp/arp.c
+++ b/usr/src/uts/common/inet/arp/arp.c
@@ -208,6 +208,7 @@ static void ar_ce_walk(arp_stack_t *as, void (*pfi)(ace_t *, void *),
static void ar_client_notify(const arl_t *arl, mblk_t *mp, int code);
static int ar_close(queue_t *q);
static int ar_cmd_dispatch(queue_t *q, mblk_t *mp, boolean_t from_wput);
+static void ar_cmd_drain(arl_t *arl);
static void ar_cmd_done(arl_t *arl);
static mblk_t *ar_dlpi_comm(t_uscalar_t prim, size_t size);
static void ar_dlpi_send(arl_t *, mblk_t *);
@@ -1331,6 +1332,53 @@ ar_dlpi_comm(t_uscalar_t prim, size_t size)
return (mp);
}
+static void
+ar_dlpi_dispatch(arl_t *arl)
+{
+ mblk_t *mp;
+ t_uscalar_t primitive = DL_PRIM_INVAL;
+
+ while (((mp = arl->arl_dlpi_deferred) != NULL) &&
+ (arl->arl_dlpi_pending == DL_PRIM_INVAL)) {
+ union DL_primitives *dlp = (union DL_primitives *)mp->b_rptr;
+
+ DTRACE_PROBE2(dlpi_dispatch, arl_t *, arl, mblk_t *, mp);
+
+ ASSERT(DB_TYPE(mp) == M_PROTO || DB_TYPE(mp) == M_PCPROTO);
+ arl->arl_dlpi_deferred = mp->b_next;
+ mp->b_next = NULL;
+
+ /*
+ * If this is a DL_NOTIFY_CONF, no ack is expected.
+ */
+ if ((primitive = dlp->dl_primitive) != DL_NOTIFY_CONF)
+ arl->arl_dlpi_pending = dlp->dl_primitive;
+ putnext(arl->arl_wq, mp);
+ }
+
+ if (arl->arl_dlpi_pending == DL_PRIM_INVAL) {
+ /*
+ * No pending DLPI operation.
+ */
+ ASSERT(mp == NULL);
+ DTRACE_PROBE1(dlpi_idle, arl_t *, arl);
+
+ /*
+ * If the last DLPI message dispatched is DL_NOTIFY_CONF,
+ * it is not assoicated with any pending cmd request, drain
+ * the rest of pending cmd requests, otherwise call
+ * ar_cmd_done() to finish up the current pending cmd
+ * operation.
+ */
+ if (primitive == DL_NOTIFY_CONF)
+ ar_cmd_drain(arl);
+ else
+ ar_cmd_done(arl);
+ } else if (mp != NULL) {
+ DTRACE_PROBE2(dlpi_defer, arl_t *, arl, mblk_t *, mp);
+ }
+}
+
/*
* The following two functions serialize DLPI messages to the driver, much
* along the lines of ill_dlpi_send and ill_dlpi_done in IP. Basically,
@@ -1341,26 +1389,18 @@ ar_dlpi_comm(t_uscalar_t prim, size_t size)
static void
ar_dlpi_send(arl_t *arl, mblk_t *mp)
{
+ mblk_t **mpp;
+
ASSERT(arl != NULL);
ASSERT(DB_TYPE(mp) == M_PROTO || DB_TYPE(mp) == M_PCPROTO);
- if (arl->arl_dlpi_pending != DL_PRIM_INVAL) {
- mblk_t **mpp;
-
- /* Must queue message. Tail insertion */
- mpp = &arl->arl_dlpi_deferred;
- while (*mpp != NULL)
- mpp = &((*mpp)->b_next);
- *mpp = mp;
+ /* Always queue the message. Tail insertion */
+ mpp = &arl->arl_dlpi_deferred;
+ while (*mpp != NULL)
+ mpp = &((*mpp)->b_next);
+ *mpp = mp;
- DTRACE_PROBE2(dlpi_defer, arl_t *, arl, mblk_t *, mp);
- return;
- }
-
- arl->arl_dlpi_pending =
- ((union DL_primitives *)mp->b_rptr)->dl_primitive;
- DTRACE_PROBE2(dlpi_send, arl_t *, arl, mblk_t *, mp);
- putnext(arl->arl_wq, mp);
+ ar_dlpi_dispatch(arl);
}
/*
@@ -1372,30 +1412,71 @@ ar_dlpi_send(arl_t *arl, mblk_t *mp)
static void
ar_dlpi_done(arl_t *arl, t_uscalar_t prim)
{
- mblk_t *mp;
-
if (arl->arl_dlpi_pending != prim) {
DTRACE_PROBE2(dlpi_done_unexpected, arl_t *, arl,
t_uscalar_t, prim);
return;
}
- if ((mp = arl->arl_dlpi_deferred) == NULL) {
- DTRACE_PROBE2(dlpi_done_idle, arl_t *, arl, t_uscalar_t, prim);
- arl->arl_dlpi_pending = DL_PRIM_INVAL;
- ar_cmd_done(arl);
- return;
- }
+ DTRACE_PROBE2(dlpi_done, arl_t *, arl, t_uscalar_t, prim);
+ arl->arl_dlpi_pending = DL_PRIM_INVAL;
+ ar_dlpi_dispatch(arl);
+}
- arl->arl_dlpi_deferred = mp->b_next;
- mp->b_next = NULL;
+/*
+ * Send a DL_NOTE_REPLUMB_DONE message down to the driver to indicate
+ * the replumb process has already been done. Note that mp is either a
+ * DL_NOTIFY_IND message or an AR_INTERFACE_DOWN message (comes from IP).
+ */
+static void
+arp_replumb_done(arl_t *arl, mblk_t *mp)
+{
+ ASSERT(arl->arl_state == ARL_S_DOWN && arl->arl_replumbing);
- ASSERT(DB_TYPE(mp) == M_PROTO || DB_TYPE(mp) == M_PCPROTO);
+ mp = mexchange(NULL, mp, sizeof (dl_notify_conf_t), M_PROTO,
+ DL_NOTIFY_CONF);
+ ((dl_notify_conf_t *)(mp->b_rptr))->dl_notification =
+ DL_NOTE_REPLUMB_DONE;
+ arl->arl_replumbing = B_FALSE;
+ ar_dlpi_send(arl, mp);
+}
+
+static void
+ar_cmd_drain(arl_t *arl)
+{
+ mblk_t *mp;
+ queue_t *q;
+
+ /*
+ * Run the commands that have been enqueued while we were waiting
+ * for the last command (AR_INTERFACE_UP or AR_INTERFACE_DOWN)
+ * to complete.
+ */
+ while ((mp = arl->arl_queue) != NULL) {
+ if (((uintptr_t)mp->b_prev & CMD_IN_PROGRESS) != 0) {
+ /*
+ * The current command is an AR_INTERFACE_UP or
+ * AR_INTERFACE_DOWN and is waiting for a DLPI ack
+ * from the driver. Return. We can't make progress now.
+ */
+ break;
+ }
+
+ mp = ar_cmd_dequeue(arl);
+ mp->b_prev = AR_DRAINING;
+ q = mp->b_queue;
+ mp->b_queue = NULL;
- arl->arl_dlpi_pending =
- ((union DL_primitives *)mp->b_rptr)->dl_primitive;
- DTRACE_PROBE2(dlpi_done_next, arl_t *, arl, mblk_t *, mp);
- putnext(arl->arl_wq, mp);
+ /*
+ * Don't call put(q, mp) since it can lead to reorder of
+ * messages by sending the current messages to the end of
+ * arp's syncq
+ */
+ if (q->q_flag & QREADR)
+ ar_rput(q, mp);
+ else
+ ar_wput(q, mp);
+ }
}
static void
@@ -1409,7 +1490,6 @@ ar_cmd_done(arl_t *arl)
queue_t *dlpi_op_done_q;
ar_t *ar_arl;
ar_t *ar_ip;
- queue_t *q;
ASSERT(arl->arl_state == ARL_S_UP || arl->arl_state == ARL_S_DOWN);
@@ -1458,44 +1538,24 @@ ar_cmd_done(arl_t *arl)
ar_arl->ar_arl_ip_assoc = ar_ip;
ar_ip->ar_arl_ip_assoc = ar_arl;
}
- }
- inet_freemsg(mp);
- }
- /*
- * Run the commands that have been enqueued while we were waiting
- * for the last command (AR_INTERFACE_UP or AR_INTERFACE_DOWN)
- * to complete.
- */
- while ((mp = ar_cmd_dequeue(arl)) != NULL) {
- mp->b_prev = AR_DRAINING;
- q = mp->b_queue;
- mp->b_queue = NULL;
-
- /*
- * Don't call put(q, mp) since it can lead to reorder of
- * messages by sending the current messages to the end of
- * arp's syncq
- */
- if (q->q_flag & QREADR)
- ar_rput(q, mp);
- else
- ar_wput(q, mp);
-
- if ((mp = arl->arl_queue) == NULL)
- goto done; /* no work to do */
-
- if ((cmd = (uintptr_t)mp->b_prev) & CMD_IN_PROGRESS) {
+ inet_freemsg(mp);
+ } else if (cmd == AR_INTERFACE_DOWN && arl->arl_replumbing) {
/*
- * The current command is an AR_INTERFACE_UP or
- * AR_INTERFACE_DOWN and is waiting for a DLPI ack
- * from the driver. Return. We can't make progress now.
+ * The arl is successfully brought down and this is
+ * a result of the DL_NOTE_REPLUMB process. Reset
+ * mp->b_prev first (it keeps the 'cmd' information
+ * at this point).
*/
- goto done;
+ mp->b_prev = NULL;
+ arp_replumb_done(arl, mp);
+ } else {
+ inet_freemsg(mp);
}
}
-done:
+ ar_cmd_drain(arl);
+
if (dlpi_op_done_mp != NULL) {
DTRACE_PROBE3(cmd_done_next, arl_t *, arl,
queue_t *, dlpi_op_done_q, mblk_t *, dlpi_op_done_mp);
@@ -2136,8 +2196,18 @@ ar_interface_down(queue_t *q, mblk_t *mp)
* The arl is already down, no work to do.
*/
if (arl->arl_state == ARL_S_DOWN) {
- /* ar_rput frees the mp */
- return (0);
+ if (arl->arl_replumbing) {
+ /*
+ * The arl is already down and this is a result of
+ * the DL_NOTE_REPLUMB process. Return EINPROGRESS
+ * so this mp won't be freed by ar_rput().
+ */
+ arp_replumb_done(arl, mp);
+ return (EINPROGRESS);
+ } else {
+ /* ar_rput frees the mp */
+ return (0);
+ }
}
/*
@@ -2672,7 +2742,7 @@ ar_ll_up(arl_t *arl)
if (notify_mp == NULL)
goto bad;
((dl_notify_req_t *)notify_mp->b_rptr)->dl_notifications =
- DL_NOTE_LINK_UP | DL_NOTE_LINK_DOWN;
+ DL_NOTE_LINK_UP | DL_NOTE_LINK_DOWN | DL_NOTE_REPLUMB;
arl->arl_state = ARL_S_PENDING;
if (arl->arl_provider_style == DL_STYLE2) {
@@ -3852,6 +3922,16 @@ ar_rput_dlpi(queue_t *q, mblk_t *mp)
case DL_NOTIFY_IND:
DTRACE_PROBE2(rput_dl_notify_ind, arl_t *, arl,
dl_notify_ind_t *, &dlp->notify_ind);
+
+ if (dlp->notify_ind.dl_notification == DL_NOTE_REPLUMB) {
+ arl->arl_replumbing = B_TRUE;
+ if (arl->arl_state == ARL_S_DOWN) {
+ arp_replumb_done(arl, mp);
+ return;
+ }
+ break;
+ }
+
if (ap != NULL) {
switch (dlp->notify_ind.dl_notification) {
case DL_NOTE_LINK_UP: