Annotation of Gnu-Mach/ipc/ipc_port.c, revision 1.1.1.4

1.1       root        1: /*
                      2:  * Mach Operating System
                      3:  * Copyright (c) 1991,1990,1989 Carnegie Mellon University.
                      4:  * Copyright (c) 1993,1994 The University of Utah and
                      5:  * the Computer Systems Laboratory (CSL).
                      6:  * All rights reserved.
                      7:  *
                      8:  * Permission to use, copy, modify and distribute this software and its
                      9:  * documentation is hereby granted, provided that both the copyright
                     10:  * notice and this permission notice appear in all copies of the
                     11:  * software, derivative works or modified versions, and any portions
                     12:  * thereof, and that both notices appear in supporting documentation.
                     13:  *
                     14:  * CARNEGIE MELLON, THE UNIVERSITY OF UTAH AND CSL ALLOW FREE USE OF
                     15:  * THIS SOFTWARE IN ITS "AS IS" CONDITION, AND DISCLAIM ANY LIABILITY
                     16:  * OF ANY KIND FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF
                     17:  * THIS SOFTWARE.
                     18:  *
                     19:  * Carnegie Mellon requests users of this software to return to
                     20:  *
                     21:  *  Software Distribution Coordinator  or  [email protected]
                     22:  *  School of Computer Science
                     23:  *  Carnegie Mellon University
                     24:  *  Pittsburgh PA 15213-3890
                     25:  *
                     26:  * any improvements or extensions that they make and grant Carnegie Mellon
                     27:  * the rights to redistribute these changes.
                     28:  */
                     29: /*
                     30:  *     File:   ipc/ipc_port.c
                     31:  *     Author: Rich Draves
                     32:  *     Date:   1989
                     33:  *
                     34:  *     Functions to manipulate IPC ports.
                     35:  */
                     36: 
1.1.1.3   root       37: #include <kern/printf.h>
                     38: #include <string.h>
1.1       root       39: 
                     40: #include <mach/port.h>
                     41: #include <mach/kern_return.h>
                     42: #include <kern/lock.h>
                     43: #include <kern/ipc_sched.h>
                     44: #include <kern/ipc_kobject.h>
                     45: #include <ipc/ipc_entry.h>
                     46: #include <ipc/ipc_space.h>
                     47: #include <ipc/ipc_object.h>
                     48: #include <ipc/ipc_port.h>
                     49: #include <ipc/ipc_pset.h>
                     50: #include <ipc/ipc_thread.h>
                     51: #include <ipc/ipc_mqueue.h>
                     52: #include <ipc/ipc_notify.h>
                     53: 
1.1.1.3   root       54: #if    MACH_KDB
                     55: #include <ddb/db_output.h>
                     56: #include <ipc/ipc_print.h>
                     57: #endif /* MACH_KDB */
1.1       root       58: 
                     59: 
                     60: decl_simple_lock_data(, ipc_port_multiple_lock_data)
                     61: 
                     62: decl_simple_lock_data(, ipc_port_timestamp_lock_data)
                     63: ipc_port_timestamp_t ipc_port_timestamp_data;
                     64: 
                     65: /*
                     66:  *     Routine:        ipc_port_timestamp
                     67:  *     Purpose:
                     68:  *             Retrieve a timestamp value.
                     69:  */
                     70: 
                     71: ipc_port_timestamp_t
                     72: ipc_port_timestamp(void)
                     73: {
                     74:        ipc_port_timestamp_t timestamp;
                     75: 
                     76:        ipc_port_timestamp_lock();
                     77:        timestamp = ipc_port_timestamp_data++;
                     78:        ipc_port_timestamp_unlock();
                     79: 
                     80:        return timestamp;
                     81: }
                     82: 
                     83: /*
                     84:  *     Routine:        ipc_port_dnrequest
                     85:  *     Purpose:
                     86:  *             Try to allocate a dead-name request slot.
                     87:  *             If successful, returns the request index.
                     88:  *             Otherwise returns zero.
                     89:  *     Conditions:
                     90:  *             The port is locked and active.
                     91:  *     Returns:
                     92:  *             KERN_SUCCESS            A request index was found.
                     93:  *             KERN_NO_SPACE           No index allocated.
                     94:  */
                     95: 
                     96: kern_return_t
1.1.1.4 ! root       97: ipc_port_dnrequest(
        !            98:        ipc_port_t                      port,
        !            99:        mach_port_t                     name,
        !           100:        ipc_port_t                      soright,
        !           101:        ipc_port_request_index_t        *indexp)
1.1       root      102: {
                    103:        ipc_port_request_t ipr, table;
                    104:        ipc_port_request_index_t index;
                    105: 
                    106:        assert(ip_active(port));
                    107:        assert(name != MACH_PORT_NULL);
                    108:        assert(soright != IP_NULL);
                    109: 
                    110:        table = port->ip_dnrequests;
                    111:        if (table == IPR_NULL)
                    112:                return KERN_NO_SPACE;
                    113: 
                    114:        index = table->ipr_next;
                    115:        if (index == 0)
                    116:                return KERN_NO_SPACE;
                    117: 
                    118:        ipr = &table[index];
                    119:        assert(ipr->ipr_name == MACH_PORT_NULL);
                    120: 
                    121:        table->ipr_next = ipr->ipr_next;
                    122:        ipr->ipr_name = name;
                    123:        ipr->ipr_soright = soright;
                    124: 
                    125:        *indexp = index;
                    126:        return KERN_SUCCESS;
                    127: }
                    128: 
                    129: /*
                    130:  *     Routine:        ipc_port_dngrow
                    131:  *     Purpose:
                    132:  *             Grow a port's table of dead-name requests.
                    133:  *     Conditions:
                    134:  *             The port must be locked and active.
                    135:  *             Nothing else locked; will allocate memory.
                    136:  *             Upon return the port is unlocked.
                    137:  *     Returns:
                    138:  *             KERN_SUCCESS            Grew the table.
                    139:  *             KERN_SUCCESS            Somebody else grew the table.
                    140:  *             KERN_SUCCESS            The port died.
                    141:  *             KERN_RESOURCE_SHORTAGE  Couldn't allocate new table.
                    142:  */
                    143: 
                    144: kern_return_t
1.1.1.4 ! root      145: ipc_port_dngrow(ipc_port_t port)
1.1       root      146: {
                    147:        ipc_table_size_t its;
                    148:        ipc_port_request_t otable, ntable;
                    149: 
                    150:        assert(ip_active(port));
                    151: 
                    152:        otable = port->ip_dnrequests;
                    153:        if (otable == IPR_NULL)
                    154:                its = &ipc_table_dnrequests[0];
                    155:        else
                    156:                its = otable->ipr_size + 1;
                    157: 
                    158:        ip_reference(port);
                    159:        ip_unlock(port);
                    160: 
                    161:        if ((its->its_size == 0) ||
                    162:            ((ntable = it_dnrequests_alloc(its)) == IPR_NULL)) {
                    163:                ipc_port_release(port);
                    164:                return KERN_RESOURCE_SHORTAGE;
                    165:        }
                    166: 
                    167:        ip_lock(port);
                    168:        ip_release(port);
                    169: 
                    170:        /*
                    171:         *      Check that port is still active and that nobody else
                    172:         *      has slipped in and grown the table on us.  Note that
                    173:         *      just checking port->ip_dnrequests == otable isn't
                    174:         *      sufficient; must check ipr_size.
                    175:         */
                    176: 
                    177:        if (ip_active(port) &&
                    178:            (port->ip_dnrequests == otable) &&
                    179:            ((otable == IPR_NULL) || (otable->ipr_size+1 == its))) {
                    180:                ipc_table_size_t oits = 0; /* '=0' to shut up lint */
                    181:                ipc_table_elems_t osize, nsize;
                    182:                ipc_port_request_index_t free, i;
                    183: 
                    184:                /* copy old table to new table */
                    185: 
                    186:                if (otable != IPR_NULL) {
                    187:                        oits = otable->ipr_size;
                    188:                        osize = oits->its_size;
                    189:                        free = otable->ipr_next;
                    190: 
1.1.1.3   root      191:                        memcpy((ntable + 1), (otable + 1), 
1.1       root      192:                              (osize - 1) * sizeof(struct ipc_port_request));
                    193:                } else {
                    194:                        osize = 1;
                    195:                        free = 0;
                    196:                }
                    197: 
                    198:                nsize = its->its_size;
                    199:                assert(nsize > osize);
                    200: 
                    201:                /* add new elements to the new table's free list */
                    202: 
                    203:                for (i = osize; i < nsize; i++) {
                    204:                        ipc_port_request_t ipr = &ntable[i];
                    205: 
                    206:                        ipr->ipr_name = MACH_PORT_NULL;
                    207:                        ipr->ipr_next = free;
                    208:                        free = i;
                    209:                }
                    210: 
                    211:                ntable->ipr_next = free;
                    212:                ntable->ipr_size = its;
                    213:                port->ip_dnrequests = ntable;
                    214:                ip_unlock(port);
                    215: 
                    216:                if (otable != IPR_NULL)
                    217:                        it_dnrequests_free(oits, otable);
                    218:        } else {
                    219:                ip_check_unlock(port);
                    220:                it_dnrequests_free(its, ntable);
                    221:        }
                    222: 
                    223:        return KERN_SUCCESS;
                    224: }
1.1.1.2   root      225: 
1.1       root      226: /*
                    227:  *     Routine:        ipc_port_dncancel
                    228:  *     Purpose:
                    229:  *             Cancel a dead-name request and return the send-once right.
                    230:  *     Conditions:
                    231:  *             The port must locked and active.
                    232:  */
                    233: 
                    234: ipc_port_t
                    235: ipc_port_dncancel(
                    236:        ipc_port_t                      port,
                    237:        mach_port_t                     name,
                    238:        ipc_port_request_index_t        index)
                    239: {
                    240:        ipc_port_request_t ipr, table;
                    241:        ipc_port_t dnrequest;
                    242: 
                    243:        assert(ip_active(port));
                    244:        assert(name != MACH_PORT_NULL);
                    245:        assert(index != 0);
                    246: 
                    247:        table = port->ip_dnrequests;
                    248:        assert(table != IPR_NULL);
                    249: 
                    250:        ipr = &table[index];
                    251:        dnrequest = ipr->ipr_soright;
                    252:        assert(ipr->ipr_name == name);
                    253: 
                    254:        /* return ipr to the free list inside the table */
                    255: 
                    256:        ipr->ipr_name = MACH_PORT_NULL;
                    257:        ipr->ipr_next = table->ipr_next;
                    258:        table->ipr_next = index;
                    259: 
                    260:        return dnrequest;
                    261: }
                    262: 
                    263: /*
                    264:  *     Routine:        ipc_port_pdrequest
                    265:  *     Purpose:
                    266:  *             Make a port-deleted request, returning the
                    267:  *             previously registered send-once right.
                    268:  *             Just cancels the previous request if notify is IP_NULL.
                    269:  *     Conditions:
                    270:  *             The port is locked and active.  It is unlocked.
                    271:  *             Consumes a ref for notify (if non-null), and
                    272:  *             returns previous with a ref (if non-null).
                    273:  */
                    274: 
                    275: void
                    276: ipc_port_pdrequest(
1.1.1.4 ! root      277:        ipc_port_t              port,
        !           278:        const ipc_port_t        notify,
        !           279:        ipc_port_t              *previousp)
1.1       root      280: {
                    281:        ipc_port_t previous;
                    282: 
                    283:        assert(ip_active(port));
                    284: 
                    285:        previous = port->ip_pdrequest;
                    286:        port->ip_pdrequest = notify;
                    287:        ip_unlock(port);
                    288: 
                    289:        *previousp = previous;
                    290: }
                    291: 
                    292: /*
                    293:  *     Routine:        ipc_port_nsrequest
                    294:  *     Purpose:
                    295:  *             Make a no-senders request, returning the
                    296:  *             previously registered send-once right.
                    297:  *             Just cancels the previous request if notify is IP_NULL.
                    298:  *     Conditions:
                    299:  *             The port is locked and active.  It is unlocked.
                    300:  *             Consumes a ref for notify (if non-null), and
                    301:  *             returns previous with a ref (if non-null).
                    302:  */
                    303: 
                    304: void
                    305: ipc_port_nsrequest(
                    306:        ipc_port_t              port,
                    307:        mach_port_mscount_t     sync,
                    308:        ipc_port_t              notify,
                    309:        ipc_port_t              *previousp)
                    310: {
                    311:        ipc_port_t previous;
                    312:        mach_port_mscount_t mscount;
                    313: 
                    314:        assert(ip_active(port));
                    315: 
                    316:        previous = port->ip_nsrequest;
                    317:        mscount = port->ip_mscount;
                    318: 
                    319:        if ((port->ip_srights == 0) &&
                    320:            (sync <= mscount) &&
                    321:            (notify != IP_NULL)) {
                    322:                port->ip_nsrequest = IP_NULL;
                    323:                ip_unlock(port);
                    324:                ipc_notify_no_senders(notify, mscount);
                    325:        } else {
                    326:                port->ip_nsrequest = notify;
                    327:                ip_unlock(port);
                    328:        }
                    329: 
                    330:        *previousp = previous;
                    331: }
                    332: 
                    333: /*
                    334:  *     Routine:        ipc_port_set_qlimit
                    335:  *     Purpose:
                    336:  *             Changes a port's queue limit; the maximum number
                    337:  *             of messages which may be queued to the port.
                    338:  *     Conditions:
                    339:  *             The port is locked and active.
                    340:  */
                    341: 
                    342: void
                    343: ipc_port_set_qlimit(
                    344:        ipc_port_t              port,
                    345:        mach_port_msgcount_t    qlimit)
                    346: {
                    347:        assert(ip_active(port));
                    348: 
                    349:        /* wake up senders allowed by the new qlimit */
                    350: 
                    351:        if (qlimit > port->ip_qlimit) {
                    352:                mach_port_msgcount_t i, wakeup;
                    353: 
                    354:                /* caution: wakeup, qlimit are unsigned */
                    355: 
                    356:                wakeup = qlimit - port->ip_qlimit;
                    357: 
                    358:                for (i = 0; i < wakeup; i++) {
                    359:                        ipc_thread_t th;
                    360: 
                    361:                        th = ipc_thread_dequeue(&port->ip_blocked);
                    362:                        if (th == ITH_NULL)
                    363:                                break;
                    364: 
                    365:                        th->ith_state = MACH_MSG_SUCCESS;
                    366:                        thread_go(th);
                    367:                }
                    368:        }
                    369: 
                    370:        port->ip_qlimit = qlimit;
                    371: }
                    372: 
                    373: /*
                    374:  *     Routine:        ipc_port_lock_mqueue
                    375:  *     Purpose:
                    376:  *             Locks and returns the message queue that the port is using.
                    377:  *             The message queue may be in the port or in its port set.
                    378:  *     Conditions:
                    379:  *             The port is locked and active.
                    380:  *             Port set, message queue locks may be taken.
                    381:  */
                    382: 
                    383: ipc_mqueue_t
1.1.1.4 ! root      384: ipc_port_lock_mqueue(ipc_port_t port)
1.1       root      385: {
                    386:        if (port->ip_pset != IPS_NULL) {
                    387:                ipc_pset_t pset = port->ip_pset;
                    388: 
                    389:                ips_lock(pset);
                    390:                if (ips_active(pset)) {
                    391:                        imq_lock(&pset->ips_messages);
                    392:                        ips_unlock(pset);
                    393:                        return &pset->ips_messages;
                    394:                }
                    395: 
                    396:                ipc_pset_remove(pset, port);
                    397:                ips_check_unlock(pset);
                    398:        }
                    399: 
                    400:        imq_lock(&port->ip_messages);
                    401:        return &port->ip_messages;
                    402: }
                    403: 
                    404: /*
                    405:  *     Routine:        ipc_port_set_seqno
                    406:  *     Purpose:
                    407:  *             Changes a port's sequence number.
                    408:  *     Conditions:
                    409:  *             The port is locked and active.
                    410:  *             Port set, message queue locks may be taken.
                    411:  */
                    412: 
                    413: void
1.1.1.4 ! root      414: ipc_port_set_seqno(
        !           415:        ipc_port_t              port,
        !           416:        mach_port_seqno_t       seqno)
1.1       root      417: {
                    418:        ipc_mqueue_t mqueue;
                    419: 
                    420:        mqueue = ipc_port_lock_mqueue(port);
                    421:        port->ip_seqno = seqno;
                    422:        imq_unlock(mqueue);
                    423: }
                    424: 
                    425: /*
1.1.1.4 ! root      426:  *     Routine:        ipc_port_set_protected_payload
        !           427:  *     Purpose:
        !           428:  *             Changes a port's protected payload.
        !           429:  *     Conditions:
        !           430:  *             The port is locked and active.
        !           431:  */
        !           432: 
        !           433: void
        !           434: ipc_port_set_protected_payload(ipc_port_t port, unsigned long payload)
        !           435: {
        !           436:        ipc_mqueue_t mqueue;
        !           437: 
        !           438:        mqueue = ipc_port_lock_mqueue(port);
        !           439:        port->ip_protected_payload = payload;
        !           440:        ipc_port_flag_protected_payload_set(port);
        !           441:        imq_unlock(mqueue);
        !           442: }
        !           443: 
        !           444: /*
        !           445:  *     Routine:        ipc_port_clear_protected_payload
        !           446:  *     Purpose:
        !           447:  *             Clear a port's protected payload.
        !           448:  *     Conditions:
        !           449:  *             The port is locked and active.
        !           450:  */
        !           451: 
        !           452: void
        !           453: ipc_port_clear_protected_payload(ipc_port_t port)
        !           454: {
        !           455:        ipc_mqueue_t mqueue;
        !           456: 
        !           457:        mqueue = ipc_port_lock_mqueue(port);
        !           458:        ipc_port_flag_protected_payload_clear(port);
        !           459:        imq_unlock(mqueue);
        !           460: }
        !           461: 
        !           462: 
        !           463: /*
1.1       root      464:  *     Routine:        ipc_port_clear_receiver
                    465:  *     Purpose:
                    466:  *             Prepares a receive right for transmission/destruction.
                    467:  *     Conditions:
                    468:  *             The port is locked and active.
                    469:  */
                    470: 
                    471: void
                    472: ipc_port_clear_receiver(
                    473:        ipc_port_t      port)
                    474: {
                    475:        ipc_pset_t pset;
                    476: 
                    477:        assert(ip_active(port));
                    478: 
                    479:        pset = port->ip_pset;
                    480:        if (pset != IPS_NULL) {
                    481:                /* No threads receiving from port, but must remove from set. */
                    482: 
                    483:                ips_lock(pset);
                    484:                ipc_pset_remove(pset, port);
                    485:                ips_check_unlock(pset);
                    486:        } else {
                    487:                /* Else, wake up all receivers, indicating why. */
                    488: 
                    489:                imq_lock(&port->ip_messages);
                    490:                ipc_mqueue_changed(&port->ip_messages, MACH_RCV_PORT_DIED);
                    491:                imq_unlock(&port->ip_messages);
                    492:        }
                    493: 
                    494:        ipc_port_set_mscount(port, 0);
                    495:        imq_lock(&port->ip_messages);
                    496:        port->ip_seqno = 0;
                    497:        imq_unlock(&port->ip_messages);
                    498: }
                    499: 
                    500: /*
                    501:  *     Routine:        ipc_port_init
                    502:  *     Purpose:
                    503:  *             Initializes a newly-allocated port.
                    504:  *             Doesn't touch the ip_object fields.
                    505:  */
                    506: 
                    507: void
                    508: ipc_port_init(
                    509:        ipc_port_t      port,
                    510:        ipc_space_t     space,
                    511:        mach_port_t     name)
                    512: {
                    513:        /* port->ip_kobject doesn't have to be initialized */
                    514: 
                    515:        ipc_target_init(&port->ip_target, name);
                    516: 
                    517:        port->ip_receiver = space;
                    518: 
                    519:        port->ip_mscount = 0;
                    520:        port->ip_srights = 0;
                    521:        port->ip_sorights = 0;
                    522: 
                    523:        port->ip_nsrequest = IP_NULL;
                    524:        port->ip_pdrequest = IP_NULL;
                    525:        port->ip_dnrequests = IPR_NULL;
                    526: 
                    527:        port->ip_pset = IPS_NULL;
                    528:        port->ip_cur_target = &port->ip_target;
                    529:        port->ip_seqno = 0;
                    530:        port->ip_msgcount = 0;
                    531:        port->ip_qlimit = MACH_PORT_QLIMIT_DEFAULT;
1.1.1.4 ! root      532:        ipc_port_flag_protected_payload_clear(port);
        !           533:        port->ip_protected_payload = 0;
1.1       root      534: 
                    535:        ipc_mqueue_init(&port->ip_messages);
                    536:        ipc_thread_queue_init(&port->ip_blocked);
                    537: }
                    538: 
                    539: /*
                    540:  *     Routine:        ipc_port_alloc
                    541:  *     Purpose:
                    542:  *             Allocate a port.
                    543:  *     Conditions:
                    544:  *             Nothing locked.  If successful, the port is returned
                    545:  *             locked.  (The caller doesn't have a reference.)
                    546:  *     Returns:
                    547:  *             KERN_SUCCESS            The port is allocated.
                    548:  *             KERN_INVALID_TASK       The space is dead.
                    549:  *             KERN_NO_SPACE           No room for an entry in the space.
                    550:  *             KERN_RESOURCE_SHORTAGE  Couldn't allocate memory.
                    551:  */
                    552: 
                    553: kern_return_t
                    554: ipc_port_alloc(
                    555:        ipc_space_t     space,
                    556:        mach_port_t     *namep,
                    557:        ipc_port_t      *portp)
                    558: {
                    559:        ipc_port_t port;
                    560:        mach_port_t name;
                    561:        kern_return_t kr;
                    562: 
                    563:        kr = ipc_object_alloc(space, IOT_PORT,
                    564:                              MACH_PORT_TYPE_RECEIVE, 0,
                    565:                              &name, (ipc_object_t *) &port);
                    566:        if (kr != KERN_SUCCESS)
                    567:                return kr;
                    568: 
                    569:        /* port is locked */
                    570: 
                    571:        ipc_port_init(port, space, name);
                    572: 
                    573:        *namep = name;
                    574:        *portp = port;
                    575: 
                    576:        return KERN_SUCCESS;
                    577: }
                    578: 
                    579: /*
                    580:  *     Routine:        ipc_port_alloc_name
                    581:  *     Purpose:
                    582:  *             Allocate a port, with a specific name.
                    583:  *     Conditions:
                    584:  *             Nothing locked.  If successful, the port is returned
                    585:  *             locked.  (The caller doesn't have a reference.)
                    586:  *     Returns:
                    587:  *             KERN_SUCCESS            The port is allocated.
                    588:  *             KERN_INVALID_TASK       The space is dead.
                    589:  *             KERN_NAME_EXISTS        The name already denotes a right.
                    590:  *             KERN_RESOURCE_SHORTAGE  Couldn't allocate memory.
                    591:  */
                    592: 
                    593: kern_return_t
                    594: ipc_port_alloc_name(
                    595:        ipc_space_t     space,
                    596:        mach_port_t     name,
                    597:        ipc_port_t      *portp)
                    598: {
                    599:        ipc_port_t port;
                    600:        kern_return_t kr;
                    601: 
                    602:        kr = ipc_object_alloc_name(space, IOT_PORT,
                    603:                                   MACH_PORT_TYPE_RECEIVE, 0,
                    604:                                   name, (ipc_object_t *) &port);
                    605:        if (kr != KERN_SUCCESS)
                    606:                return kr;
                    607:        /* port is locked */
                    608: 
                    609:        ipc_port_init(port, space, name);
                    610: 
                    611:        *portp = port;
                    612:        return KERN_SUCCESS;
                    613: }
                    614: 
                    615: /*
                    616:  *     Routine:        ipc_port_destroy
                    617:  *     Purpose:
                    618:  *             Destroys a port.  Cleans up queued messages.
                    619:  *
                    620:  *             If the port has a backup, it doesn't get destroyed,
                    621:  *             but is sent in a port-destroyed notification to the backup.
                    622:  *     Conditions:
                    623:  *             The port is locked and alive; nothing else locked.
                    624:  *             The caller has a reference, which is consumed.
                    625:  *             Afterwards, the port is unlocked and dead.
                    626:  */
                    627: 
                    628: void
                    629: ipc_port_destroy(
                    630:        ipc_port_t      port)
                    631: {
                    632:        ipc_port_t pdrequest, nsrequest;
                    633:        ipc_mqueue_t mqueue;
                    634:        ipc_kmsg_queue_t kmqueue;
                    635:        ipc_kmsg_t kmsg;
                    636:        ipc_thread_t sender;
                    637:        ipc_port_request_t dnrequests;
                    638: 
                    639:        assert(ip_active(port));
                    640:        /* port->ip_receiver_name is garbage */
                    641:        /* port->ip_receiver/port->ip_destination is garbage */
                    642:        assert(port->ip_pset == IPS_NULL);
                    643:        assert(port->ip_mscount == 0);
                    644:        assert(port->ip_seqno == 0);
                    645: 
                    646:        /* first check for a backup port */
                    647: 
                    648:        pdrequest = port->ip_pdrequest;
                    649:        if (pdrequest != IP_NULL) {
                    650:                /* we assume the ref for pdrequest */
                    651:                port->ip_pdrequest = IP_NULL;
                    652: 
                    653:                /* make port be in limbo */
                    654:                port->ip_receiver_name = MACH_PORT_NULL;
                    655:                port->ip_destination = IP_NULL;
1.1.1.4 ! root      656:                ipc_port_flag_protected_payload_clear(port);
1.1       root      657:                ip_unlock(port);
                    658: 
                    659:                if (!ipc_port_check_circularity(port, pdrequest)) {
                    660:                        /* consumes our refs for port and pdrequest */
                    661:                        ipc_notify_port_destroyed(pdrequest, port);
                    662:                        return;
                    663:                } else {
                    664:                        /* consume pdrequest and destroy port */
                    665:                        ipc_port_release_sonce(pdrequest);
                    666:                }
                    667: 
                    668:                ip_lock(port);
                    669:                assert(ip_active(port));
                    670:                assert(port->ip_pset == IPS_NULL);
                    671:                assert(port->ip_mscount == 0);
                    672:                assert(port->ip_seqno == 0);
                    673:                assert(port->ip_pdrequest == IP_NULL);
                    674:                assert(port->ip_receiver_name == MACH_PORT_NULL);
                    675:                assert(port->ip_destination == IP_NULL);
                    676: 
                    677:                /* fall through and destroy the port */
                    678:        }
                    679: 
                    680:        /*
                    681:         *      rouse all blocked senders
                    682:         *
                    683:         *      This must be done with the port locked, because
                    684:         *      ipc_mqueue_send can play with the ip_blocked queue
                    685:         *      of a dead port.
                    686:         */
                    687: 
                    688:        while ((sender = ipc_thread_dequeue(&port->ip_blocked)) != ITH_NULL) {
                    689:                sender->ith_state = MACH_MSG_SUCCESS;
                    690:                thread_go(sender);
                    691:        }
                    692: 
                    693:        /* once port is dead, we don't need to keep it locked */
                    694: 
                    695:        port->ip_object.io_bits &= ~IO_BITS_ACTIVE;
                    696:        port->ip_timestamp = ipc_port_timestamp();
                    697:        ip_unlock(port);
                    698: 
                    699:        /* throw away no-senders request */
                    700: 
                    701:        nsrequest = port->ip_nsrequest;
                    702:        if (nsrequest != IP_NULL)
                    703:                ipc_notify_send_once(nsrequest); /* consumes ref */
                    704: 
                    705:        /* destroy any queued messages */
                    706: 
                    707:        mqueue = &port->ip_messages;
                    708:        imq_lock(mqueue);
                    709:        assert(ipc_thread_queue_empty(&mqueue->imq_threads));
                    710:        kmqueue = &mqueue->imq_messages;
                    711: 
                    712:        while ((kmsg = ipc_kmsg_dequeue(kmqueue)) != IKM_NULL) {
                    713:                imq_unlock(mqueue);
                    714: 
                    715:                assert(kmsg->ikm_header.msgh_remote_port ==
                    716:                                                (mach_port_t) port);
                    717: 
                    718:                ipc_port_release(port);
                    719:                kmsg->ikm_header.msgh_remote_port = MACH_PORT_NULL;
                    720:                ipc_kmsg_destroy(kmsg);
                    721: 
                    722:                imq_lock(mqueue);
                    723:        }
                    724: 
                    725:        imq_unlock(mqueue);
                    726: 
                    727:        /* generate dead-name notifications */
                    728: 
                    729:        dnrequests = port->ip_dnrequests;
                    730:        if (dnrequests != IPR_NULL) {
                    731:                ipc_table_size_t its = dnrequests->ipr_size;
                    732:                ipc_table_elems_t size = its->its_size;
                    733:                ipc_port_request_index_t index;
                    734: 
                    735:                for (index = 1; index < size; index++) {
                    736:                        ipc_port_request_t ipr = &dnrequests[index];
                    737:                        mach_port_t name = ipr->ipr_name;
                    738:                        ipc_port_t soright;
                    739: 
                    740:                        if (name == MACH_PORT_NULL)
                    741:                                continue;
                    742: 
                    743:                        soright = ipr->ipr_soright;
                    744:                        assert(soright != IP_NULL);
                    745: 
                    746:                        ipc_notify_dead_name(soright, name);
                    747:                }
                    748: 
                    749:                it_dnrequests_free(its, dnrequests);
                    750:        }
                    751: 
                    752:        if (ip_kotype(port) != IKOT_NONE)
                    753:                ipc_kobject_destroy(port);
                    754: 
                    755:        /* Common destruction for the IPC target.  */
                    756:        ipc_target_terminate(&port->ip_target);
                    757: 
                    758:        ipc_port_release(port); /* consume caller's ref */
                    759: }
                    760: 
                    761: /*
                    762:  *     Routine:        ipc_port_check_circularity
                    763:  *     Purpose:
                    764:  *             Check if queueing "port" in a message for "dest"
                    765:  *             would create a circular group of ports and messages.
                    766:  *
                    767:  *             If no circularity (FALSE returned), then "port"
                    768:  *             is changed from "in limbo" to "in transit".
                    769:  *
                    770:  *             That is, we want to set port->ip_destination == dest,
                    771:  *             but guaranteeing that this doesn't create a circle
                    772:  *             port->ip_destination->ip_destination->... == port
                    773:  *     Conditions:
                    774:  *             No ports locked.  References held for "port" and "dest".
                    775:  */
                    776: 
                    777: boolean_t
                    778: ipc_port_check_circularity(
                    779:        ipc_port_t      port,
                    780:        ipc_port_t      dest)
                    781: {
                    782:        ipc_port_t base;
                    783: 
                    784:        assert(port != IP_NULL);
                    785:        assert(dest != IP_NULL);
                    786: 
                    787:        if (port == dest)
                    788:                return TRUE;
                    789:        base = dest;
                    790: 
                    791:        /*
                    792:         *      First try a quick check that can run in parallel.
                    793:         *      No circularity if dest is not in transit.
                    794:         */
                    795: 
                    796:        ip_lock(port);
                    797:        if (ip_lock_try(dest)) {
                    798:                if (!ip_active(dest) ||
                    799:                    (dest->ip_receiver_name != MACH_PORT_NULL) ||
                    800:                    (dest->ip_destination == IP_NULL))
                    801:                        goto not_circular;
                    802: 
                    803:                /* dest is in transit; further checking necessary */
                    804: 
                    805:                ip_unlock(dest);
                    806:        }
                    807:        ip_unlock(port);
                    808: 
                    809:        ipc_port_multiple_lock(); /* massive serialization */
                    810: 
                    811:        /*
                    812:         *      Search for the end of the chain (a port not in transit),
                    813:         *      acquiring locks along the way.
                    814:         */
                    815: 
                    816:        for (;;) {
                    817:                ip_lock(base);
                    818: 
                    819:                if (!ip_active(base) ||
                    820:                    (base->ip_receiver_name != MACH_PORT_NULL) ||
                    821:                    (base->ip_destination == IP_NULL))
                    822:                        break;
                    823: 
                    824:                base = base->ip_destination;
                    825:        }
                    826: 
                    827:        /* all ports in chain from dest to base, inclusive, are locked */
                    828: 
                    829:        if (port == base) {
                    830:                /* circularity detected! */
                    831: 
                    832:                ipc_port_multiple_unlock();
                    833: 
                    834:                /* port (== base) is in limbo */
                    835: 
                    836:                assert(ip_active(port));
                    837:                assert(port->ip_receiver_name == MACH_PORT_NULL);
                    838:                assert(port->ip_destination == IP_NULL);
                    839: 
                    840:                while (dest != IP_NULL) {
                    841:                        ipc_port_t next;
                    842: 
                    843:                        /* dest is in transit or in limbo */
                    844: 
                    845:                        assert(ip_active(dest));
                    846:                        assert(dest->ip_receiver_name == MACH_PORT_NULL);
                    847: 
                    848:                        next = dest->ip_destination;
                    849:                        ip_unlock(dest);
                    850:                        dest = next;
                    851:                }
                    852: 
                    853:                return TRUE;
                    854:        }
                    855: 
                    856:        /*
                    857:         *      The guarantee:  lock port while the entire chain is locked.
                    858:         *      Once port is locked, we can take a reference to dest,
                    859:         *      add port to the chain, and unlock everything.
                    860:         */
                    861: 
                    862:        ip_lock(port);
                    863:        ipc_port_multiple_unlock();
                    864: 
                    865:     not_circular:
                    866: 
                    867:        /* port is in limbo */
                    868: 
                    869:        assert(ip_active(port));
                    870:        assert(port->ip_receiver_name == MACH_PORT_NULL);
                    871:        assert(port->ip_destination == IP_NULL);
                    872: 
                    873:        ip_reference(dest);
                    874:        port->ip_destination = dest;
                    875: 
                    876:        /* now unlock chain */
                    877: 
                    878:        while (port != base) {
                    879:                ipc_port_t next;
                    880: 
                    881:                /* port is in transit */
                    882: 
                    883:                assert(ip_active(port));
                    884:                assert(port->ip_receiver_name == MACH_PORT_NULL);
                    885:                assert(port->ip_destination != IP_NULL);
                    886: 
                    887:                next = port->ip_destination;
                    888:                ip_unlock(port);
                    889:                port = next;
                    890:        }
                    891: 
                    892:        /* base is not in transit */
                    893: 
                    894:        assert(!ip_active(base) ||
                    895:               (base->ip_receiver_name != MACH_PORT_NULL) ||
                    896:               (base->ip_destination == IP_NULL));
                    897:        ip_unlock(base);
                    898: 
                    899:        return FALSE;
                    900: }
                    901: 
                    902: /*
                    903:  *     Routine:        ipc_port_lookup_notify
                    904:  *     Purpose:
                    905:  *             Make a send-once notify port from a receive right.
                    906:  *             Returns IP_NULL if name doesn't denote a receive right.
                    907:  *     Conditions:
                    908:  *             The space must be locked (read or write) and active.
                    909:  */
                    910: 
                    911: ipc_port_t
                    912: ipc_port_lookup_notify(
                    913:        ipc_space_t     space,
                    914:        mach_port_t     name)
                    915: {
                    916:        ipc_port_t port;
                    917:        ipc_entry_t entry;
                    918: 
                    919:        assert(space->is_active);
                    920: 
                    921:        entry = ipc_entry_lookup(space, name);
                    922:        if (entry == IE_NULL)
                    923:                return IP_NULL;
                    924: 
                    925:        if ((entry->ie_bits & MACH_PORT_TYPE_RECEIVE) == 0)
                    926:                return IP_NULL;
                    927: 
                    928:        port = (ipc_port_t) entry->ie_object;
                    929:        assert(port != IP_NULL);
                    930: 
                    931:        ip_lock(port);
                    932:        assert(ip_active(port));
                    933:        assert(port->ip_receiver_name == name);
                    934:        assert(port->ip_receiver == space);
                    935: 
                    936:        ip_reference(port);
                    937:        port->ip_sorights++;
                    938:        ip_unlock(port);
                    939: 
                    940:        return port;
                    941: }
                    942: 
                    943: /*
                    944:  *     Routine:        ipc_port_make_send
                    945:  *     Purpose:
                    946:  *             Make a naked send right from a receive right.
                    947:  *     Conditions:
                    948:  *             The port is not locked but it is active.
                    949:  */
                    950: 
                    951: ipc_port_t
                    952: ipc_port_make_send(
                    953:        ipc_port_t      port)
                    954: {
                    955:        assert(IP_VALID(port));
                    956: 
                    957:        ip_lock(port);
                    958:        assert(ip_active(port));
                    959:        port->ip_mscount++;
                    960:        port->ip_srights++;
                    961:        ip_reference(port);
                    962:        ip_unlock(port);
                    963: 
                    964:        return port;
                    965: }
                    966: 
                    967: /*
                    968:  *     Routine:        ipc_port_copy_send
                    969:  *     Purpose:
                    970:  *             Make a naked send right from another naked send right.
                    971:  *                     IP_NULL         -> IP_NULL
                    972:  *                     IP_DEAD         -> IP_DEAD
                    973:  *                     dead port       -> IP_DEAD
                    974:  *                     live port       -> port + ref
                    975:  *     Conditions:
                    976:  *             Nothing locked except possibly a space.
                    977:  */
                    978: 
                    979: ipc_port_t
                    980: ipc_port_copy_send(
                    981:        ipc_port_t      port)
                    982: {
                    983:        ipc_port_t sright;
                    984: 
                    985:        if (!IP_VALID(port))
                    986:                return port;
                    987: 
                    988:        ip_lock(port);
                    989:        if (ip_active(port)) {
                    990:                assert(port->ip_srights > 0);
                    991: 
                    992:                ip_reference(port);
                    993:                port->ip_srights++;
                    994:                sright = port;
                    995:        } else
                    996:                sright = IP_DEAD;
                    997:        ip_unlock(port);
                    998: 
                    999:        return sright;
                   1000: }
                   1001: 
                   1002: /*
                   1003:  *     Routine:        ipc_port_copyout_send
                   1004:  *     Purpose:
                   1005:  *             Copyout a naked send right (possibly null/dead),
                   1006:  *             or if that fails, destroy the right.
                   1007:  *     Conditions:
                   1008:  *             Nothing locked.
                   1009:  */
                   1010: 
                   1011: mach_port_t
                   1012: ipc_port_copyout_send(
                   1013:        ipc_port_t      sright,
                   1014:        ipc_space_t     space)
                   1015: {
                   1016:        mach_port_t name;
                   1017: 
                   1018:        if (IP_VALID(sright)) {
                   1019:                kern_return_t kr;
                   1020: 
                   1021:                kr = ipc_object_copyout(space, (ipc_object_t) sright,
                   1022:                                        MACH_MSG_TYPE_PORT_SEND, TRUE, &name);
                   1023:                if (kr != KERN_SUCCESS) {
                   1024:                        ipc_port_release_send(sright);
                   1025: 
                   1026:                        if (kr == KERN_INVALID_CAPABILITY)
                   1027:                                name = MACH_PORT_DEAD;
                   1028:                        else
                   1029:                                name = MACH_PORT_NULL;
                   1030:                }
                   1031:        } else
                   1032:                name = (mach_port_t) sright;
                   1033: 
                   1034:        return name;
                   1035: }
                   1036: 
                   1037: /*
                   1038:  *     Routine:        ipc_port_release_send
                   1039:  *     Purpose:
                   1040:  *             Release a (valid) naked send right.
                   1041:  *             Consumes a ref for the port.
                   1042:  *     Conditions:
                   1043:  *             Nothing locked.
                   1044:  */
                   1045: 
                   1046: void
                   1047: ipc_port_release_send(
                   1048:        ipc_port_t      port)
                   1049: {
                   1050:        ipc_port_t nsrequest = IP_NULL;
                   1051:        mach_port_mscount_t mscount;
                   1052: 
                   1053:        assert(IP_VALID(port));
                   1054: 
                   1055:        ip_lock(port);
                   1056:        ip_release(port);
                   1057: 
                   1058:        if (!ip_active(port)) {
                   1059:                ip_check_unlock(port);
                   1060:                return;
                   1061:        }
                   1062: 
                   1063:        assert(port->ip_srights > 0);
                   1064: 
                   1065:        if (--port->ip_srights == 0) {
                   1066:                nsrequest = port->ip_nsrequest;
                   1067:                if (nsrequest != IP_NULL) {
                   1068:                        port->ip_nsrequest = IP_NULL;
                   1069:                        mscount = port->ip_mscount;
                   1070:                }
                   1071:        }
                   1072: 
                   1073:        ip_unlock(port);
                   1074: 
                   1075:        if (nsrequest != IP_NULL)
                   1076:                ipc_notify_no_senders(nsrequest, mscount);
                   1077: }
                   1078: 
                   1079: /*
                   1080:  *     Routine:        ipc_port_make_sonce
                   1081:  *     Purpose:
                   1082:  *             Make a naked send-once right from a receive right.
                   1083:  *     Conditions:
                   1084:  *             The port is not locked but it is active.
                   1085:  */
                   1086: 
                   1087: ipc_port_t
                   1088: ipc_port_make_sonce(
                   1089:        ipc_port_t      port)
                   1090: {
                   1091:        assert(IP_VALID(port));
                   1092: 
                   1093:        ip_lock(port);
                   1094:        assert(ip_active(port));
                   1095:        port->ip_sorights++;
                   1096:        ip_reference(port);
                   1097:        ip_unlock(port);
                   1098: 
                   1099:        return port;
                   1100: }
                   1101: 
                   1102: /*
                   1103:  *     Routine:        ipc_port_release_sonce
                   1104:  *     Purpose:
                   1105:  *             Release a naked send-once right.
                   1106:  *             Consumes a ref for the port.
                   1107:  *
                   1108:  *             In normal situations, this is never used.
                   1109:  *             Send-once rights are only consumed when
                   1110:  *             a message (possibly a send-once notification)
                   1111:  *             is sent to them.
                   1112:  *     Conditions:
                   1113:  *             Nothing locked except possibly a space.
                   1114:  */
                   1115: 
                   1116: void
                   1117: ipc_port_release_sonce(
                   1118:        ipc_port_t      port)
                   1119: {
                   1120:        assert(IP_VALID(port));
                   1121: 
                   1122:        ip_lock(port);
                   1123: 
                   1124:        assert(port->ip_sorights > 0);
                   1125: 
                   1126:        port->ip_sorights--;
                   1127: 
                   1128:        ip_release(port);
                   1129: 
                   1130:        if (!ip_active(port)) {
                   1131:                ip_check_unlock(port);
                   1132:                return;
                   1133:        }
                   1134: 
                   1135:        ip_unlock(port);
                   1136: }
                   1137: 
                   1138: /*
                   1139:  *     Routine:        ipc_port_release_receive
                   1140:  *     Purpose:
                   1141:  *             Release a naked (in limbo or in transit) receive right.
                   1142:  *             Consumes a ref for the port; destroys the port.
                   1143:  *     Conditions:
                   1144:  *             Nothing locked.
                   1145:  */
                   1146: 
                   1147: void
                   1148: ipc_port_release_receive(
                   1149:        ipc_port_t      port)
                   1150: {
                   1151:        ipc_port_t dest;
                   1152: 
                   1153:        assert(IP_VALID(port));
                   1154: 
                   1155:        ip_lock(port);
                   1156:        assert(ip_active(port));
                   1157:        assert(port->ip_receiver_name == MACH_PORT_NULL);
                   1158:        dest = port->ip_destination;
                   1159: 
                   1160:        ipc_port_destroy(port); /* consumes ref, unlocks */
                   1161: 
                   1162:        if (dest != IP_NULL)
                   1163:                ipc_port_release(dest);
                   1164: }
                   1165: 
                   1166: /*
                   1167:  *     Routine:        ipc_port_alloc_special
                   1168:  *     Purpose:
                   1169:  *             Allocate a port in a special space.
                   1170:  *             The new port is returned with one ref.
                   1171:  *             If unsuccessful, IP_NULL is returned.
                   1172:  *     Conditions:
                   1173:  *             Nothing locked.
                   1174:  */
                   1175: 
                   1176: ipc_port_t
1.1.1.4 ! root     1177: ipc_port_alloc_special(ipc_space_t space)
1.1       root     1178: {
                   1179:        ipc_port_t port;
                   1180: 
                   1181:        port = (ipc_port_t) io_alloc(IOT_PORT);
                   1182:        if (port == IP_NULL)
                   1183:                return IP_NULL;
                   1184: 
                   1185:        io_lock_init(&port->ip_object);
                   1186:        port->ip_references = 1;
                   1187:        port->ip_object.io_bits = io_makebits(TRUE, IOT_PORT, 0);
                   1188: 
                   1189:        /*
                   1190:         *      The actual values of ip_receiver_name aren't important,
                   1191:         *      as long as they are valid (not null/dead).
                   1192:         *
                   1193:         *      Mach4: we set it to the internal port structure address
                   1194:         *      so we can always just pass on ip_receiver_name during
                   1195:         *      an rpc regardless of whether the destination is user or
                   1196:         *      kernel (i.e. no special-casing code for the kernel along
                   1197:         *      the fast rpc path).
                   1198:         */
                   1199: 
                   1200:        ipc_port_init(port, space, (mach_port_t)port);
                   1201: 
                   1202:        return port;
                   1203: }
                   1204: 
                   1205: /*
                   1206:  *     Routine:        ipc_port_dealloc_special
                   1207:  *     Purpose:
                   1208:  *             Deallocate a port in a special space.
                   1209:  *             Consumes one ref for the port.
                   1210:  *     Conditions:
                   1211:  *             Nothing locked.
                   1212:  */
                   1213: 
                   1214: void
                   1215: ipc_port_dealloc_special(
                   1216:        ipc_port_t      port,
                   1217:        ipc_space_t     space)
                   1218: {
                   1219:        ip_lock(port);
                   1220:        assert(ip_active(port));
                   1221:        assert(port->ip_receiver_name != MACH_PORT_NULL);
                   1222:        assert(port->ip_receiver == space);
                   1223: 
                   1224:        /*
                   1225:         *      We clear ip_receiver_name and ip_receiver to simplify
                   1226:         *      the ipc_space_kernel check in ipc_mqueue_send.
                   1227:         */
                   1228: 
                   1229:        port->ip_receiver_name = MACH_PORT_NULL;
                   1230:        port->ip_receiver = IS_NULL;
                   1231: 
                   1232:        /*
                   1233:         *      For ipc_space_kernel, all ipc_port_clear_receiver does
                   1234:         *      is clean things up for the assertions in ipc_port_destroy.
                   1235:         *      For ipc_space_reply, there might be a waiting receiver.
                   1236:         */
                   1237: 
                   1238:        ipc_port_clear_receiver(port);
                   1239:        ipc_port_destroy(port);
                   1240: }
                   1241: 
                   1242: #if    MACH_KDB
                   1243: #define        printf  kdbprintf
                   1244: 
                   1245: /*
                   1246:  *     Routine:        ipc_port_print
                   1247:  *     Purpose:
                   1248:  *             Pretty-print a port for kdb.
                   1249:  */
                   1250: 
                   1251: void
                   1252: ipc_port_print(port)
1.1.1.4 ! root     1253:        const ipc_port_t port;
1.1       root     1254: {
                   1255:        printf("port 0x%x\n", port);
                   1256: 
                   1257:        indent += 2;
                   1258: 
1.1.1.4 ! root     1259:        iprintf("flags ");
        !          1260:        printf("has_protected_payload=%d",
        !          1261:               ipc_port_flag_protected_payload(port));
        !          1262:        printf("\n");
        !          1263: 
1.1       root     1264:        ipc_object_print(&port->ip_object);
                   1265:        iprintf("receiver=0x%x", port->ip_receiver);
                   1266:        printf(", receiver_name=0x%x\n", port->ip_receiver_name);
                   1267: 
                   1268:        iprintf("mscount=%d", port->ip_mscount);
                   1269:        printf(", srights=%d", port->ip_srights);
                   1270:        printf(", sorights=%d\n", port->ip_sorights);
                   1271: 
                   1272:        iprintf("nsrequest=0x%x", port->ip_nsrequest);
                   1273:        printf(", pdrequest=0x%x", port->ip_pdrequest);
                   1274:        printf(", dnrequests=0x%x\n", port->ip_dnrequests);
                   1275: 
                   1276:        iprintf("pset=0x%x", port->ip_pset);
                   1277:        printf(", seqno=%d", port->ip_seqno);
                   1278:        printf(", msgcount=%d", port->ip_msgcount);
                   1279:        printf(", qlimit=%d\n", port->ip_qlimit);
                   1280: 
                   1281:        iprintf("kmsgs=0x%x", port->ip_messages.imq_messages.ikmq_base);
                   1282:        printf(", rcvrs=0x%x", port->ip_messages.imq_threads.ithq_base);
                   1283:        printf(", sndrs=0x%x", port->ip_blocked.ithq_base);
                   1284:        printf(", kobj=0x%x\n", port->ip_kobject);
                   1285: 
1.1.1.4 ! root     1286:        iprintf("protected_payload=%p\n", (void *) port->ip_protected_payload);
        !          1287: 
        !          1288:        indent -= 2;
1.1       root     1289: }
                   1290: 
1.1.1.2   root     1291: #endif /* MACH_KDB */

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.