summaryrefslogtreecommitdiff
path: root/usr/src/uts/common/inet/ip/ip_if.c
diff options
context:
space:
mode:
Diffstat (limited to 'usr/src/uts/common/inet/ip/ip_if.c')
-rw-r--r--usr/src/uts/common/inet/ip/ip_if.c386
1 files changed, 305 insertions, 81 deletions
diff --git a/usr/src/uts/common/inet/ip/ip_if.c b/usr/src/uts/common/inet/ip/ip_if.c
index 09ed4c8421..32367adb79 100644
--- a/usr/src/uts/common/inet/ip/ip_if.c
+++ b/usr/src/uts/common/inet/ip/ip_if.c
@@ -193,6 +193,7 @@ static void ill_nominate_bcast_rcv(ill_group_t *illgrp);
static void ill_phyint_free(ill_t *ill);
static void ill_phyint_reinit(ill_t *ill);
static void ill_set_nce_router_flags(ill_t *, boolean_t);
+static void ill_set_phys_addr_tail(ipsq_t *, queue_t *, mblk_t *, void *);
static void ill_signal_ipsq_ills(ipsq_t *, boolean_t);
static boolean_t ill_split_ipsq(ipsq_t *cur_sq);
static void ill_stq_cache_delete(ire_t *, char *);
@@ -991,8 +992,8 @@ ill_delete_tail(ill_t *ill)
ill->ill_frag_ptr = NULL;
ill->ill_frag_hash_tbl = NULL;
}
- if (ill->ill_nd_lla_mp != NULL)
- freemsg(ill->ill_nd_lla_mp);
+
+ freemsg(ill->ill_nd_lla_mp);
/* Free all retained control messages. */
mpp = &ill->ill_first_mp_to_free;
do {
@@ -1213,19 +1214,28 @@ boolean_t
ipsq_pending_mp_add(conn_t *connp, ipif_t *ipif, queue_t *q, mblk_t *add_mp,
int waitfor)
{
- ipsq_t *ipsq;
+ ipsq_t *ipsq = ipif->ipif_ill->ill_phyint->phyint_ipsq;
ASSERT(IAM_WRITER_IPIF(ipif));
ASSERT(MUTEX_HELD(&ipif->ipif_ill->ill_lock));
ASSERT((add_mp->b_next == NULL) && (add_mp->b_prev == NULL));
+ ASSERT(ipsq->ipsq_pending_mp == NULL);
+ /*
+ * The caller may be using a different ipif than the one passed into
+ * ipsq_current_start() (e.g., suppose an ioctl that came in on the V4
+ * ill needs to wait for the V6 ill to quiesce). So we can't ASSERT
+ * that `ipsq_current_ipif == ipif'.
+ */
+ ASSERT(ipsq->ipsq_current_ipif != NULL);
+
/*
* M_IOCDATA from ioctls, M_IOCTL from tunnel ioctls,
- * M_ERROR/M_HANGUP from driver
+ * M_ERROR/M_HANGUP/M_PROTO/M_PCPROTO from the driver.
*/
ASSERT((DB_TYPE(add_mp) == M_IOCDATA) || (DB_TYPE(add_mp) == M_IOCTL) ||
- (DB_TYPE(add_mp) == M_ERROR) || (DB_TYPE(add_mp) == M_HANGUP));
+ (DB_TYPE(add_mp) == M_ERROR) || (DB_TYPE(add_mp) == M_HANGUP) ||
+ (DB_TYPE(add_mp) == M_PROTO) || (DB_TYPE(add_mp) == M_PCPROTO));
- ipsq = ipif->ipif_ill->ill_phyint->phyint_ipsq;
if (connp != NULL) {
ASSERT(MUTEX_HELD(&connp->conn_lock));
/*
@@ -1248,17 +1258,7 @@ ipsq_pending_mp_add(conn_t *connp, ipif_t *ipif, queue_t *q, mblk_t *add_mp,
add_mp->b_queue = q;
ipsq->ipsq_pending_mp = add_mp;
ipsq->ipsq_waitfor = waitfor;
- /*
- * ipsq_current_ipif is needed to restart the operation from
- * ipif_ill_refrele_tail when the last reference to the ipi/ill
- * is gone. Since this is not an ioctl ipsq_current_ipif has not
- * been set until now.
- */
- if (DB_TYPE(add_mp) == M_ERROR || DB_TYPE(add_mp) == M_HANGUP) {
- ASSERT(ipsq->ipsq_current_ipif == NULL);
- ipsq->ipsq_current_ipif = ipif;
- ipsq->ipsq_last_cmd = DB_TYPE(add_mp);
- }
+
if (connp != NULL)
connp->conn_oper_pending_ill = ipif->ipif_ill;
mutex_exit(&ipsq->ipsq_lock);
@@ -1352,11 +1352,18 @@ ipsq_pending_mp_cleanup(ill_t *ill, conn_t *connp)
ipsq->ipsq_pending_ipif = NULL;
ipsq->ipsq_waitfor = 0;
ipsq->ipsq_current_ipif = NULL;
+ ipsq->ipsq_current_ioctl = 0;
mutex_exit(&ipsq->ipsq_lock);
if (DB_TYPE(mp) == M_IOCTL || DB_TYPE(mp) == M_IOCDATA) {
- ip_ioctl_finish(q, mp, ENXIO, connp != NULL ? CONN_CLOSE :
- NO_COPYOUT, connp != NULL ? ipif : NULL, NULL);
+ if (connp == NULL) {
+ ip_ioctl_finish(q, mp, ENXIO, NO_COPYOUT, NULL);
+ } else {
+ ip_ioctl_finish(q, mp, ENXIO, CONN_CLOSE, NULL);
+ mutex_enter(&ipif->ipif_ill->ill_lock);
+ ipif->ipif_state_flags &= ~IPIF_CHANGING;
+ mutex_exit(&ipif->ipif_ill->ill_lock);
+ }
} else {
/*
* IP-MT XXX In the case of TLI/XTI bind / optmgmt this can't
@@ -1397,7 +1404,7 @@ ill_pending_mp_cleanup(ill_t *ill)
mp->b_next = NULL;
mp->b_prev = NULL;
mp->b_queue = NULL;
- ip_ioctl_finish(q, mp, ENXIO, NO_COPYOUT, NULL, NULL);
+ ip_ioctl_finish(q, mp, ENXIO, NO_COPYOUT, NULL);
mutex_enter(&ill->ill_lock);
}
ill->ill_pending_ipif = NULL;
@@ -1468,7 +1475,7 @@ ipsq_xopq_mp_cleanup(ill_t *ill, conn_t *connp)
curr->b_queue = NULL;
if (DB_TYPE(curr) == M_IOCTL || DB_TYPE(curr) == M_IOCDATA) {
ip_ioctl_finish(q, curr, ENXIO, connp != NULL ?
- CONN_CLOSE : NO_COPYOUT, NULL, NULL);
+ CONN_CLOSE : NO_COPYOUT, NULL);
} else {
/*
* IP-MT XXX In the case of TLI/XTI bind / optmgmt
@@ -1613,7 +1620,7 @@ ipif_all_down_tail(ipsq_t *ipsq, queue_t *q, mblk_t *mp, void *dummy_arg)
}
ill_down_tail(ill);
freemsg(mp);
- ipsq->ipsq_current_ipif = NULL;
+ ipsq_current_finish(ipsq);
}
/*
@@ -1624,11 +1631,9 @@ ipif_all_down_tail(ipsq_t *ipsq, queue_t *q, mblk_t *mp, void *dummy_arg)
boolean_t
ill_down_start(queue_t *q, mblk_t *mp)
{
- ill_t *ill;
+ ill_t *ill = q->q_ptr;
ipif_t *ipif;
- ill = q->q_ptr;
-
ASSERT(IAM_WRITER_ILL(ill));
for (ipif = ill->ill_ipif; ipif != NULL; ipif = ipif->ipif_next)
@@ -1637,16 +1642,15 @@ ill_down_start(queue_t *q, mblk_t *mp)
ill_down(ill);
(void) ipsq_pending_mp_cleanup(ill, NULL);
- mutex_enter(&ill->ill_lock);
+
+ ipsq_current_start(ill->ill_phyint->phyint_ipsq, ill->ill_ipif, 0);
+
/*
- * Atomically test and add the pending mp if references are
- * still active.
+ * Atomically test and add the pending mp if references are active.
*/
+ mutex_enter(&ill->ill_lock);
if (!ill_is_quiescent(ill)) {
- /*
- * Get rid of any pending mps and cleanup. Call will
- * not fail since we are passing a null connp.
- */
+ /* call cannot fail since `conn_t *' argument is NULL */
(void) ipsq_pending_mp_add(NULL, ill->ill_ipif, ill->ill_rq,
mp, ILL_DOWN);
mutex_exit(&ill->ill_lock);
@@ -4936,24 +4940,21 @@ ill_init(queue_t *q, ill_t *ill)
int
ill_dls_info(struct sockaddr_dl *sdl, const ipif_t *ipif)
{
- size_t length;
+ size_t len;
ill_t *ill = ipif->ipif_ill;
sdl->sdl_family = AF_LINK;
sdl->sdl_index = ill->ill_phyint->phyint_ifindex;
- sdl->sdl_type = ipif->ipif_type;
+ sdl->sdl_type = ill->ill_type;
(void) ipif_get_name(ipif, sdl->sdl_data, sizeof (sdl->sdl_data));
- length = mi_strlen(sdl->sdl_data);
- ASSERT(length < 256);
- sdl->sdl_nlen = (uchar_t)length;
+ len = strlen(sdl->sdl_data);
+ ASSERT(len < 256);
+ sdl->sdl_nlen = (uchar_t)len;
sdl->sdl_alen = ill->ill_phys_addr_length;
- mutex_enter(&ill->ill_lock);
- if (ill->ill_phys_addr_length != 0 && ill->ill_phys_addr != NULL) {
- bcopy(ill->ill_phys_addr, &sdl->sdl_data[length],
- ill->ill_phys_addr_length);
- }
- mutex_exit(&ill->ill_lock);
sdl->sdl_slen = 0;
+ if (ill->ill_phys_addr_length != 0 && ill->ill_phys_addr != NULL)
+ bcopy(ill->ill_phys_addr, &sdl->sdl_data[len], sdl->sdl_alen);
+
return (sizeof (struct sockaddr_dl));
}
@@ -6219,6 +6220,7 @@ ipif_ill_refrele_tail(ill_t *ill)
conn_t *connp;
ipsq_t *ipsq;
ipif_t *ipif;
+ dl_notify_ind_t *dlindp;
ASSERT(MUTEX_HELD(&ill->ill_lock));
@@ -6295,17 +6297,34 @@ ipif_ill_refrele_tail(ill_t *ill)
ASSERT(mp != NULL);
switch (mp->b_datap->db_type) {
+ case M_PCPROTO:
+ case M_PROTO:
+ /*
+ * For now, only DL_NOTIFY_IND messages can use this facility.
+ */
+ dlindp = (dl_notify_ind_t *)mp->b_rptr;
+ ASSERT(dlindp->dl_primitive == DL_NOTIFY_IND);
+
+ switch (dlindp->dl_notification) {
+ case DL_NOTE_PHYS_ADDR:
+ qwriter_ip(NULL, ill, ill->ill_rq, mp,
+ ill_set_phys_addr_tail, CUR_OP, B_TRUE);
+ return;
+ default:
+ ASSERT(0);
+ }
+ break;
+
case M_ERROR:
case M_HANGUP:
- (void) qwriter_ip(NULL, ill, ill->ill_rq, mp,
- ipif_all_down_tail, CUR_OP, B_TRUE);
+ qwriter_ip(NULL, ill, ill->ill_rq, mp, ipif_all_down_tail,
+ CUR_OP, B_TRUE);
return;
case M_IOCTL:
case M_IOCDATA:
- (void) qwriter_ip(NULL, ill,
- (connp != NULL ? CONNP_TO_WQ(connp) : ill->ill_wq), mp,
- ip_reprocess_ioctl, CUR_OP, B_TRUE);
+ qwriter_ip(NULL, ill, (connp != NULL ? CONNP_TO_WQ(connp) :
+ ill->ill_wq), mp, ip_reprocess_ioctl, CUR_OP, B_TRUE);
return;
default:
@@ -7978,6 +7997,68 @@ again:
}
/*
+ * Start the current exclusive operation on `ipsq'; associate it with `ipif'
+ * and `ioccmd'.
+ */
+void
+ipsq_current_start(ipsq_t *ipsq, ipif_t *ipif, int ioccmd)
+{
+ ASSERT(IAM_WRITER_IPSQ(ipsq));
+
+ mutex_enter(&ipsq->ipsq_lock);
+ ASSERT(ipsq->ipsq_current_ipif == NULL);
+ ASSERT(ipsq->ipsq_current_ioctl == 0);
+ ipsq->ipsq_current_ipif = ipif;
+ ipsq->ipsq_current_ioctl = ioccmd;
+ mutex_exit(&ipsq->ipsq_lock);
+}
+
+/*
+ * Finish the current exclusive operation on `ipsq'. Note that other
+ * operations will not be able to proceed until an ipsq_exit() is done.
+ */
+void
+ipsq_current_finish(ipsq_t *ipsq)
+{
+ ipif_t *ipif = ipsq->ipsq_current_ipif;
+ hook_nic_event_t *info;
+
+ ASSERT(IAM_WRITER_IPSQ(ipsq));
+
+ /*
+ * For SIOCSLIFREMOVEIF, the ipif has been already been blown away
+ * (but we're careful to never set IPIF_CHANGING in that case).
+ */
+ if (ipsq->ipsq_current_ioctl != SIOCLIFREMOVEIF) {
+ mutex_enter(&ipif->ipif_ill->ill_lock);
+ ipif->ipif_state_flags &= ~IPIF_CHANGING;
+ /*
+ * Unhook the nic event message from the ill and enqueue it
+ * into the nic event taskq.
+ */
+ if ((info = ipif->ipif_ill->ill_nic_event_info) != NULL) {
+ if (ddi_taskq_dispatch(eventq_queue_nic,
+ ip_ne_queue_func, info, DDI_SLEEP) == DDI_FAILURE) {
+ ip2dbg(("ipsq_current_finish: "
+ "ddi_taskq_dispatch failed\n"));
+ if (info->hne_data != NULL)
+ kmem_free(info->hne_data,
+ info->hne_datalen);
+ kmem_free(info, sizeof (hook_nic_event_t));
+ }
+ ipif->ipif_ill->ill_nic_event_info = NULL;
+ }
+ mutex_exit(&ipif->ipif_ill->ill_lock);
+ }
+
+ mutex_enter(&ipsq->ipsq_lock);
+ ASSERT(ipsq->ipsq_current_ipif != NULL);
+ ipsq->ipsq_current_ipif = NULL;
+ ipsq->ipsq_current_ioctl = 0;
+ mutex_exit(&ipsq->ipsq_lock);
+}
+
+/*
* The ill is closing. Flush all messages on the ipsq that originated
* from this ill. Usually there wont' be any messages on the ipsq_xopq_mphead
* for this ill since ipsq_enter could not have entered until then.
@@ -10556,7 +10637,7 @@ ip_sioctl_iocack(queue_t *q, mblk_t *mp)
ire_refrele(ire);
freemsg(mp);
ip_ioctl_finish(q, orig_ioc_mp,
- EINVAL, NO_COPYOUT, NULL, NULL);
+ EINVAL, NO_COPYOUT, NULL);
return;
}
*flagsp |= ATF_COM;
@@ -10582,7 +10663,7 @@ ip_sioctl_iocack(queue_t *q, mblk_t *mp)
/* Ditch the internal IOCTL. */
freemsg(mp);
ire_refrele(ire);
- ip_ioctl_finish(q, orig_ioc_mp, 0, COPYOUT, NULL, NULL);
+ ip_ioctl_finish(q, orig_ioc_mp, 0, COPYOUT, NULL);
return;
}
}
@@ -10626,7 +10707,7 @@ errack:
if (iocp->ioc_error || iocp->ioc_cmd != AR_ENTRY_SQUERY) {
err = iocp->ioc_error;
freemsg(mp);
- ip_ioctl_finish(q, orig_ioc_mp, err, NO_COPYOUT, NULL, NULL);
+ ip_ioctl_finish(q, orig_ioc_mp, err, NO_COPYOUT, NULL);
return;
}
@@ -10639,8 +10720,8 @@ errack:
if ((ill->ill_phys_addr_length + ill->ill_name_length) >
sizeof (xar->xarp_ha.sdl_data)) {
freemsg(mp);
- ip_ioctl_finish(q, orig_ioc_mp, EINVAL,
- NO_COPYOUT, NULL, NULL);
+ ip_ioctl_finish(q, orig_ioc_mp, EINVAL, NO_COPYOUT,
+ NULL);
return;
}
}
@@ -10667,7 +10748,7 @@ errack:
/* Ditch the internal IOCTL. */
freemsg(mp);
/* Complete the original. */
- ip_ioctl_finish(q, orig_ioc_mp, 0, COPYOUT, NULL, NULL);
+ ip_ioctl_finish(q, orig_ioc_mp, 0, COPYOUT, NULL);
}
/*
@@ -13853,9 +13934,8 @@ ipif_resolver_up(ipif_t *ipif, enum ip_resolver_action res_act)
* Copy the new hardware address and length into
* arp_add_mp to be sent to ARP.
*/
- area->area_hw_addr_length =
- ill->ill_phys_addr_length;
- bcopy((char *)ill->ill_phys_addr,
+ area->area_hw_addr_length = ill->ill_phys_addr_length;
+ bcopy(ill->ill_phys_addr,
((char *)area + area->area_hw_addr_offset),
area->area_hw_addr_length);
}
@@ -14950,6 +15030,7 @@ ill_merge_groups(ill_t *from_ill, ill_t *to_ill, char *groupname, mblk_t *mp,
*/
mutex_enter(&old_ipsq->ipsq_lock);
old_ipsq->ipsq_current_ipif = NULL;
+ old_ipsq->ipsq_current_ioctl = 0;
mutex_exit(&old_ipsq->ipsq_lock);
return (EINPROGRESS);
}
@@ -19757,7 +19838,7 @@ ipif_up(ipif_t *ipif, queue_t *q, mblk_t *mp)
/* Skip arp/ndp for any loopback interface. */
if (ill->ill_wq != NULL) {
- conn_t *connp = Q_TO_CONN(q);
+ conn_t *connp = CONN_Q(q) ? Q_TO_CONN(q) : NULL;
ipsq_t *ipsq = ill->ill_phyint->phyint_ipsq;
if (!ill->ill_dl_up) {
@@ -19783,13 +19864,15 @@ ipif_up(ipif_t *ipif, queue_t *q, mblk_t *mp)
* EINPROGRESS and we will complete in ip_arp_done.
*/
- ASSERT(connp != NULL);
+ ASSERT(connp != NULL || !CONN_Q(q));
ASSERT(ipsq->ipsq_pending_mp == NULL);
- mutex_enter(&connp->conn_lock);
+ if (connp != NULL)
+ mutex_enter(&connp->conn_lock);
mutex_enter(&ill->ill_lock);
success = ipsq_pending_mp_add(connp, ipif, q, mp, 0);
mutex_exit(&ill->ill_lock);
- mutex_exit(&connp->conn_lock);
+ if (connp != NULL)
+ mutex_exit(&connp->conn_lock);
if (!success)
return (EINTR);
@@ -19802,8 +19885,7 @@ ipif_up(ipif_t *ipif, queue_t *q, mblk_t *mp)
* That ioctl will complete in ip_rput.
*/
if (isv6) {
- err = ipif_ndp_up(ipif, &ipif->ipif_v6lcl_addr,
- B_FALSE);
+ err = ipif_ndp_up(ipif, &ipif->ipif_v6lcl_addr);
if (err != 0) {
if (err != EINPROGRESS)
mp = ipsq_pending_mp_get(ipsq, &connp);
@@ -19887,17 +19969,14 @@ ill_dl_up(ill_t *ill, ipif_t *ipif, mblk_t *mp, queue_t *q)
* Record state needed to complete this operation when the
* DL_BIND_ACK shows up. Also remember the pre-allocated mblks.
*/
- if (WR(q)->q_next == NULL) {
- connp = Q_TO_CONN(q);
- mutex_enter(&connp->conn_lock);
- } else {
- connp = NULL;
- }
+ ASSERT(WR(q)->q_next == NULL);
+ connp = Q_TO_CONN(q);
+
+ mutex_enter(&connp->conn_lock);
mutex_enter(&ipif->ipif_ill->ill_lock);
success = ipsq_pending_mp_add(connp, ipif, q, mp, 0);
mutex_exit(&ipif->ipif_ill->ill_lock);
- if (connp != NULL)
- mutex_exit(&connp->conn_lock);
+ mutex_exit(&connp->conn_lock);
if (!success)
goto bad;
@@ -19943,10 +20022,8 @@ bad:
* groups.
*/
- if (bind_mp != NULL)
- freemsg(bind_mp);
- if (unbind_mp != NULL)
- freemsg(unbind_mp);
+ freemsg(bind_mp);
+ freemsg(unbind_mp);
return (ENOMEM);
}
@@ -22981,13 +23058,13 @@ ipif_set_values(queue_t *q, mblk_t *mp, char *interf_name, uint_t *new_ppa_ptr)
return (EINPROGRESS);
/*
- * Need to set the ipsq_current_ipif now, if we have changed ipsq
- * due to the phyint merge in ill_phyint_reinit.
+ * If ill_phyint_reinit() changed our ipsq, then start on the new ipsq.
*/
- ASSERT(ipsq->ipsq_current_ipif == NULL ||
- ipsq->ipsq_current_ipif == ipif);
- ipsq->ipsq_current_ipif = ipif;
- ipsq->ipsq_last_cmd = SIOCSLIFNAME;
+ if (ipsq->ipsq_current_ipif == NULL)
+ ipsq_current_start(ipsq, ipif, SIOCSLIFNAME);
+ else
+ ASSERT(ipsq->ipsq_current_ipif == ipif);
+
error = ipif_set_values_tail(ill, ipif, mp, q);
ipsq_exit(ipsq, B_TRUE, B_TRUE);
if (error != 0 && error != EINPROGRESS) {
@@ -24188,3 +24265,150 @@ ipif_getby_indexes(uint_t ifindex, uint_t lifidx, boolean_t isv6)
ill_refrele(ill);
return (ipif);
}
+
+/*
+ * Flush the fastpath by deleting any IRE's that are waiting for the fastpath,
+ * and any IRE's that are using the fastpath. There are two exceptions:
+ * IRE_MIPRTUN and IRE_BROADCAST are difficult to recreate, so instead we just
+ * nuke their nce_fp_mp's; see ire_fastpath_flush() for details.
+ */
+void
+ill_fastpath_flush(ill_t *ill)
+{
+ if (ill->ill_isv6) {
+ nce_fastpath_list_dispatch(ill, NULL, NULL);
+ ndp_walk(ill, (pfi_t)ndp_fastpath_flush, NULL);
+ } else {
+ ire_fastpath_list_dispatch(ill, NULL, NULL);
+ ire_walk_ill_v4(MATCH_IRE_WQ | MATCH_IRE_TYPE,
+ IRE_CACHE | IRE_BROADCAST, ire_fastpath_flush, NULL, ill);
+ mutex_enter(&ire_mrtun_lock);
+ if (ire_mrtun_count != 0) {
+ mutex_exit(&ire_mrtun_lock);
+ ire_walk_ill_mrtun(MATCH_IRE_WQ, IRE_MIPRTUN,
+ ire_fastpath_flush, NULL, ill);
+ } else {
+ mutex_exit(&ire_mrtun_lock);
+ }
+ }
+}
+
+/*
+ * Set the physical address information for `ill' to the contents of the
+ * dl_notify_ind_t pointed to by `mp'. Must be called as writer, and will be
+ * asynchronous if `ill' cannot immediately be quiesced -- in which case
+ * EINPROGRESS will be returned.
+ */
+int
+ill_set_phys_addr(ill_t *ill, mblk_t *mp)
+{
+ ipsq_t *ipsq = ill->ill_phyint->phyint_ipsq;
+ dl_notify_ind_t *dlindp = (dl_notify_ind_t *)mp->b_rptr;
+
+ ASSERT(IAM_WRITER_IPSQ(ipsq));
+
+ if (dlindp->dl_data != DL_IPV6_LINK_LAYER_ADDR &&
+ dlindp->dl_data != DL_CURR_PHYS_ADDR) {
+ /* Changing DL_IPV6_TOKEN is not yet supported */
+ return (0);
+ }
+
+ /*
+ * We need to store up to two copies of `mp' in `ill'. Due to the
+ * design of ipsq_pending_mp_add(), we can't pass them as separate
+ * arguments to ill_set_phys_addr_tail(). Instead, chain them
+ * together here, then pull 'em apart in ill_set_phys_addr_tail().
+ */
+ if ((mp = copyb(mp)) == NULL || (mp->b_cont = copyb(mp)) == NULL) {
+ freemsg(mp);
+ return (ENOMEM);
+ }
+
+ ipsq_current_start(ipsq, ill->ill_ipif, 0);
+
+ /*
+ * If we can quiesce the ill, then set the address. If not, then
+ * ill_set_phys_addr_tail() will be called from ipif_ill_refrele_tail().
+ */
+ ill_down_ipifs(ill, NULL, 0, B_FALSE);
+ mutex_enter(&ill->ill_lock);
+ if (!ill_is_quiescent(ill)) {
+ /* call cannot fail since `conn_t *' argument is NULL */
+ (void) ipsq_pending_mp_add(NULL, ill->ill_ipif, ill->ill_rq,
+ mp, ILL_DOWN);
+ mutex_exit(&ill->ill_lock);
+ return (EINPROGRESS);
+ }
+ mutex_exit(&ill->ill_lock);
+
+ ill_set_phys_addr_tail(ipsq, ill->ill_rq, mp, NULL);
+ return (0);
+}
+
+/*
+ * Once the ill associated with `q' has quiesced, set its physical address
+ * information to the values in `addrmp'. Note that two copies of `addrmp'
+ * are passed (linked by b_cont), since we sometimes need to save two distinct
+ * copies in the ill_t, and our context doesn't permit sleeping or allocation
+ * failure (we'll free the other copy if it's not needed). Since the ill_t
+ * is quiesced, we know any stale IREs with the old address information have
+ * already been removed, so we don't need to call ill_fastpath_flush().
+ */
+/* ARGSUSED */
+static void
+ill_set_phys_addr_tail(ipsq_t *ipsq, queue_t *q, mblk_t *addrmp, void *dummy)
+{
+ ill_t *ill = q->q_ptr;
+ mblk_t *addrmp2 = unlinkb(addrmp);
+ dl_notify_ind_t *dlindp = (dl_notify_ind_t *)addrmp->b_rptr;
+ uint_t addrlen, addroff;
+
+ ASSERT(IAM_WRITER_IPSQ(ipsq));
+ mutex_enter(&ill->ill_lock);
+ ASSERT(ill_is_quiescent(ill));
+ mutex_exit(&ill->ill_lock);
+
+ addroff = dlindp->dl_addr_offset;
+ addrlen = dlindp->dl_addr_length - ABS(ill->ill_sap_length);
+
+ switch (dlindp->dl_data) {
+ case DL_IPV6_LINK_LAYER_ADDR:
+ ill_set_ndmp(ill, addrmp, addroff, addrlen);
+ freemsg(addrmp2);
+ break;
+
+ case DL_CURR_PHYS_ADDR:
+ freemsg(ill->ill_phys_addr_mp);
+ ill->ill_phys_addr = addrmp->b_rptr + addroff;
+ ill->ill_phys_addr_mp = addrmp;
+ ill->ill_phys_addr_length = addrlen;
+
+ if (ill->ill_isv6 && !(ill->ill_flags & ILLF_XRESOLV))
+ ill_set_ndmp(ill, addrmp2, addroff, addrlen);
+ else
+ freemsg(addrmp2);
+ break;
+ default:
+ ASSERT(0);
+ }
+
+ /*
+ * If there are ipifs to bring up, ill_up_ipifs() will return nonzero,
+ * and ipsq_current_finish() will be called by ip_rput_dlpi_writer()
+ * or ip_arp_done() when the last ipif is brought up.
+ */
+ if (ill_up_ipifs(ill, q, addrmp) == 0)
+ ipsq_current_finish(ipsq);
+}
+
+/*
+ * Helper routine for setting the ill_nd_lla fields.
+ */
+void
+ill_set_ndmp(ill_t *ill, mblk_t *ndmp, uint_t addroff, uint_t addrlen)
+{
+ freemsg(ill->ill_nd_lla_mp);
+ ill->ill_nd_lla = ndmp->b_rptr + addroff;
+ ill->ill_nd_lla_mp = ndmp;
+ ill->ill_nd_lla_len = addrlen;
+}