Annotation of qemu/roms/ipxe/src/net/infiniband/ib_cm.c, revision 1.1

1.1     ! root        1: /*
        !             2:  * Copyright (C) 2009 Michael Brown <[email protected]>.
        !             3:  *
        !             4:  * This program is free software; you can redistribute it and/or
        !             5:  * modify it under the terms of the GNU General Public License as
        !             6:  * published by the Free Software Foundation; either version 2 of the
        !             7:  * License, or any later version.
        !             8:  *
        !             9:  * This program is distributed in the hope that it will be useful, but
        !            10:  * WITHOUT ANY WARRANTY; without even the implied warranty of
        !            11:  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
        !            12:  * General Public License for more details.
        !            13:  *
        !            14:  * You should have received a copy of the GNU General Public License
        !            15:  * along with this program; if not, write to the Free Software
        !            16:  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
        !            17:  */
        !            18: 
        !            19: FILE_LICENCE ( GPL2_OR_LATER );
        !            20: 
        !            21: #include <stdint.h>
        !            22: #include <stdlib.h>
        !            23: #include <string.h>
        !            24: #include <byteswap.h>
        !            25: #include <errno.h>
        !            26: #include <assert.h>
        !            27: #include <ipxe/infiniband.h>
        !            28: #include <ipxe/ib_mi.h>
        !            29: #include <ipxe/ib_pathrec.h>
        !            30: #include <ipxe/ib_cm.h>
        !            31: 
        !            32: /**
        !            33:  * @file
        !            34:  *
        !            35:  * Infiniband communication management
        !            36:  *
        !            37:  */
        !            38: 
        !            39: /** List of connections */
        !            40: static LIST_HEAD ( ib_cm_conns );
        !            41: 
        !            42: /**
        !            43:  * Find connection by local communication ID
        !            44:  *
        !            45:  * @v local_id         Local communication ID
        !            46:  * @ret conn           Connection, or NULL
        !            47:  */
        !            48: static struct ib_connection * ib_cm_find ( uint32_t local_id ) {
        !            49:        struct ib_connection *conn;
        !            50: 
        !            51:        list_for_each_entry ( conn, &ib_cm_conns, list ) {
        !            52:                if ( conn->local_id == local_id )
        !            53:                        return conn;
        !            54:        }
        !            55:        return NULL;
        !            56: }
        !            57: 
        !            58: /**
        !            59:  * Send "ready to use" response
        !            60:  *
        !            61:  * @v ibdev            Infiniband device
        !            62:  * @v mi               Management interface
        !            63:  * @v av               Address vector
        !            64:  * @v local_id         Local communication ID
        !            65:  * @v remote_id                Remote communication ID
        !            66:  * @ret rc             Return status code
        !            67:  */
        !            68: static int ib_cm_send_rtu ( struct ib_device *ibdev,
        !            69:                            struct ib_mad_interface *mi,
        !            70:                            struct ib_address_vector *av,
        !            71:                            uint32_t local_id, uint32_t remote_id ) {
        !            72:        union ib_mad mad;
        !            73:        struct ib_cm_ready_to_use *rtu = &mad.cm.cm_data.ready_to_use;
        !            74:        int rc;
        !            75: 
        !            76:        /* Construct "ready to use" response */
        !            77:        memset ( &mad, 0, sizeof ( mad ) );
        !            78:        mad.hdr.mgmt_class = IB_MGMT_CLASS_CM;
        !            79:        mad.hdr.class_version = IB_CM_CLASS_VERSION;
        !            80:        mad.hdr.method = IB_MGMT_METHOD_SEND;
        !            81:        mad.hdr.attr_id = htons ( IB_CM_ATTR_READY_TO_USE );
        !            82:        rtu->local_id = htonl ( local_id );
        !            83:        rtu->remote_id = htonl ( remote_id );
        !            84:        if ( ( rc = ib_mi_send ( ibdev, mi, &mad, av ) ) != 0 ){
        !            85:                DBG ( "CM could not send RTU: %s\n", strerror ( rc ) );
        !            86:                return rc;
        !            87:        }
        !            88: 
        !            89:        return 0;
        !            90: }
        !            91: 
        !            92: /**
        !            93:  * Handle duplicate connection replies
        !            94:  *
        !            95:  * @v ibdev            Infiniband device
        !            96:  * @v mi               Management interface
        !            97:  * @v mad              Received MAD
        !            98:  * @v av               Source address vector
        !            99:  * @ret rc             Return status code
        !           100:  *
        !           101:  * If a "ready to use" MAD is lost, the peer may resend the connection
        !           102:  * reply.  We have to respond to these with duplicate "ready to use"
        !           103:  * MADs, otherwise the peer may time out and drop the connection.
        !           104:  */
        !           105: static void ib_cm_recv_rep ( struct ib_device *ibdev,
        !           106:                             struct ib_mad_interface *mi,
        !           107:                             union ib_mad *mad,
        !           108:                             struct ib_address_vector *av ) {
        !           109:        struct ib_cm_connect_reply *rep = &mad->cm.cm_data.connect_reply;
        !           110:        struct ib_connection *conn;
        !           111:        uint32_t local_id = ntohl ( rep->remote_id );
        !           112:        int rc;
        !           113: 
        !           114:        /* Identify connection */
        !           115:        conn = ib_cm_find ( local_id );
        !           116:        if ( conn ) {
        !           117:                /* Try to send "ready to use" reply */
        !           118:                if ( ( rc = ib_cm_send_rtu ( ibdev, mi, av, conn->local_id,
        !           119:                                             conn->remote_id ) ) != 0 ) {
        !           120:                        /* Ignore errors; the remote end will retry */
        !           121:                }
        !           122:        } else {
        !           123:                DBG ( "CM unidentified connection %08x\n", local_id );
        !           124:        }
        !           125: }
        !           126: 
        !           127: /**
        !           128:  * Send reply to disconnection request
        !           129:  *
        !           130:  * @v ibdev            Infiniband device
        !           131:  * @v mi               Management interface
        !           132:  * @v av               Address vector
        !           133:  * @v local_id         Local communication ID
        !           134:  * @v remote_id                Remote communication ID
        !           135:  * @ret rc             Return status code
        !           136:  */
        !           137: static int ib_cm_send_drep ( struct ib_device *ibdev,
        !           138:                             struct ib_mad_interface *mi,
        !           139:                             struct ib_address_vector *av,
        !           140:                             uint32_t local_id, uint32_t remote_id ) {
        !           141:        union ib_mad mad;
        !           142:        struct ib_cm_disconnect_reply *drep = &mad.cm.cm_data.disconnect_reply;
        !           143:        int rc;
        !           144: 
        !           145:        /* Construct reply to disconnection request */
        !           146:        memset ( &mad, 0, sizeof ( mad ) );
        !           147:        mad.hdr.mgmt_class = IB_MGMT_CLASS_CM;
        !           148:        mad.hdr.class_version = IB_CM_CLASS_VERSION;
        !           149:        mad.hdr.method = IB_MGMT_METHOD_SEND;
        !           150:        mad.hdr.attr_id = htons ( IB_CM_ATTR_DISCONNECT_REPLY );
        !           151:        drep->local_id = htonl ( local_id );
        !           152:        drep->remote_id = htonl ( remote_id );
        !           153:        if ( ( rc = ib_mi_send ( ibdev, mi, &mad, av ) ) != 0 ){
        !           154:                DBG ( "CM could not send DREP: %s\n", strerror ( rc ) );
        !           155:                return rc;
        !           156:        }
        !           157: 
        !           158:        return 0;
        !           159: }
        !           160: 
        !           161: /**
        !           162:  * Handle disconnection requests
        !           163:  *
        !           164:  * @v ibdev            Infiniband device
        !           165:  * @v mi               Management interface
        !           166:  * @v mad              Received MAD
        !           167:  * @v av               Source address vector
        !           168:  * @ret rc             Return status code
        !           169:  */
        !           170: static void ib_cm_recv_dreq ( struct ib_device *ibdev,
        !           171:                              struct ib_mad_interface *mi,
        !           172:                              union ib_mad *mad,
        !           173:                              struct ib_address_vector *av ) {
        !           174:        struct ib_cm_disconnect_request *dreq =
        !           175:                &mad->cm.cm_data.disconnect_request;
        !           176:        struct ib_connection *conn;
        !           177:        uint32_t local_id = ntohl ( dreq->remote_id );
        !           178:        uint32_t remote_id = ntohl ( dreq->local_id );
        !           179:        int rc;
        !           180: 
        !           181:        /* Identify connection */
        !           182:        conn = ib_cm_find ( local_id );
        !           183:        if ( conn ) {
        !           184:                /* Notify upper layer */
        !           185:                conn->op->changed ( ibdev, conn->qp, conn, -ENOTCONN,
        !           186:                                    &dreq->private_data,
        !           187:                                    sizeof ( dreq->private_data ) );
        !           188:        } else {
        !           189:                DBG ( "CM unidentified connection %08x\n", local_id );
        !           190:        }
        !           191: 
        !           192:        /* Send reply */
        !           193:        if ( ( rc = ib_cm_send_drep ( ibdev, mi, av, local_id,
        !           194:                                      remote_id ) ) != 0 ) {
        !           195:                /* Ignore errors; the remote end will retry */
        !           196:        }
        !           197: };
        !           198: 
        !           199: /** Communication management agents */
        !           200: struct ib_mad_agent ib_cm_agent[] __ib_mad_agent = {
        !           201:        {
        !           202:                .mgmt_class = IB_MGMT_CLASS_CM,
        !           203:                .class_version = IB_CM_CLASS_VERSION,
        !           204:                .attr_id = htons ( IB_CM_ATTR_CONNECT_REPLY ),
        !           205:                .handle = ib_cm_recv_rep,
        !           206:        },
        !           207:        {
        !           208:                .mgmt_class = IB_MGMT_CLASS_CM,
        !           209:                .class_version = IB_CM_CLASS_VERSION,
        !           210:                .attr_id = htons ( IB_CM_ATTR_DISCONNECT_REQUEST ),
        !           211:                .handle = ib_cm_recv_dreq,
        !           212:        },
        !           213: };
        !           214: 
        !           215: /**
        !           216:  * Convert connection rejection reason to return status code
        !           217:  *
        !           218:  * @v reason           Rejection reason (in network byte order)
        !           219:  * @ret rc             Return status code
        !           220:  */
        !           221: static int ib_cm_rejection_reason_to_rc ( uint16_t reason ) {
        !           222:        switch ( reason ) {
        !           223:        case htons ( IB_CM_REJECT_BAD_SERVICE_ID ) :
        !           224:                return -ENODEV;
        !           225:        case htons ( IB_CM_REJECT_STALE_CONN ) :
        !           226:                return -EALREADY;
        !           227:        case htons ( IB_CM_REJECT_CONSUMER ) :
        !           228:                return -ENOTTY;
        !           229:        default:
        !           230:                return -EPERM;
        !           231:        }
        !           232: }
        !           233: 
        !           234: /**
        !           235:  * Handle connection request transaction completion
        !           236:  *
        !           237:  * @v ibdev            Infiniband device
        !           238:  * @v mi               Management interface
        !           239:  * @v madx             Management transaction
        !           240:  * @v rc               Status code
        !           241:  * @v mad              Received MAD (or NULL on error)
        !           242:  * @v av               Source address vector (or NULL on error)
        !           243:  */
        !           244: static void ib_cm_req_complete ( struct ib_device *ibdev,
        !           245:                                 struct ib_mad_interface *mi,
        !           246:                                 struct ib_mad_transaction *madx,
        !           247:                                 int rc, union ib_mad *mad,
        !           248:                                 struct ib_address_vector *av ) {
        !           249:        struct ib_connection *conn = ib_madx_get_ownerdata ( madx );
        !           250:        struct ib_queue_pair *qp = conn->qp;
        !           251:        struct ib_cm_common *common = &mad->cm.cm_data.common;
        !           252:        struct ib_cm_connect_reply *rep = &mad->cm.cm_data.connect_reply;
        !           253:        struct ib_cm_connect_reject *rej = &mad->cm.cm_data.connect_reject;
        !           254:        void *private_data = NULL;
        !           255:        size_t private_data_len = 0;
        !           256: 
        !           257:        /* Report failures */
        !           258:        if ( ( rc == 0 ) && ( mad->hdr.status != htons ( IB_MGMT_STATUS_OK ) ))
        !           259:                rc = -EIO;
        !           260:        if ( rc != 0 ) {
        !           261:                DBGC ( conn, "CM %p connection request failed: %s\n",
        !           262:                       conn, strerror ( rc ) );
        !           263:                goto out;
        !           264:        }
        !           265: 
        !           266:        /* Record remote communication ID */
        !           267:        conn->remote_id = ntohl ( common->local_id );
        !           268: 
        !           269:        /* Handle response */
        !           270:        switch ( mad->hdr.attr_id ) {
        !           271: 
        !           272:        case htons ( IB_CM_ATTR_CONNECT_REPLY ) :
        !           273:                /* Extract fields */
        !           274:                qp->av.qpn = ( ntohl ( rep->local_qpn ) >> 8 );
        !           275:                qp->send.psn = ( ntohl ( rep->starting_psn ) >> 8 );
        !           276:                private_data = &rep->private_data;
        !           277:                private_data_len = sizeof ( rep->private_data );
        !           278:                DBGC ( conn, "CM %p connected to QPN %lx PSN %x\n",
        !           279:                       conn, qp->av.qpn, qp->send.psn );
        !           280: 
        !           281:                /* Modify queue pair */
        !           282:                if ( ( rc = ib_modify_qp ( ibdev, qp ) ) != 0 ) {
        !           283:                        DBGC ( conn, "CM %p could not modify queue pair: %s\n",
        !           284:                               conn, strerror ( rc ) );
        !           285:                        goto out;
        !           286:                }
        !           287: 
        !           288:                /* Send "ready to use" reply */
        !           289:                if ( ( rc = ib_cm_send_rtu ( ibdev, mi, av, conn->local_id,
        !           290:                                             conn->remote_id ) ) != 0 ) {
        !           291:                        /* Treat as non-fatal */
        !           292:                        rc = 0;
        !           293:                }
        !           294:                break;
        !           295: 
        !           296:        case htons ( IB_CM_ATTR_CONNECT_REJECT ) :
        !           297:                /* Extract fields */
        !           298:                DBGC ( conn, "CM %p connection rejected (reason %d)\n",
        !           299:                       conn, ntohs ( rej->reason ) );
        !           300:                /* Private data is valid only for a Consumer Reject */
        !           301:                if ( rej->reason == htons ( IB_CM_REJECT_CONSUMER ) ) {
        !           302:                        private_data = &rej->private_data;
        !           303:                        private_data_len = sizeof ( rej->private_data );
        !           304:                }
        !           305:                rc = ib_cm_rejection_reason_to_rc ( rej->reason );
        !           306:                break;
        !           307: 
        !           308:        default:
        !           309:                DBGC ( conn, "CM %p unexpected response (attribute %04x)\n",
        !           310:                       conn, ntohs ( mad->hdr.attr_id ) );
        !           311:                rc = -ENOTSUP;
        !           312:                break;
        !           313:        }
        !           314: 
        !           315:  out:
        !           316:        /* Destroy the completed transaction */
        !           317:        ib_destroy_madx ( ibdev, ibdev->gsi, madx );
        !           318:        conn->madx = NULL;
        !           319: 
        !           320:        /* Hand off to the upper completion handler */
        !           321:        conn->op->changed ( ibdev, qp, conn, rc, private_data,
        !           322:                            private_data_len );
        !           323: }
        !           324: 
        !           325: /** Connection request operations */
        !           326: static struct ib_mad_transaction_operations ib_cm_req_op = {
        !           327:        .complete = ib_cm_req_complete,
        !           328: };
        !           329: 
        !           330: /**
        !           331:  * Handle connection path transaction completion
        !           332:  *
        !           333:  * @v ibdev            Infiniband device
        !           334:  * @v path             Path
        !           335:  * @v rc               Status code
        !           336:  * @v av               Address vector, or NULL on error
        !           337:  */
        !           338: static void ib_cm_path_complete ( struct ib_device *ibdev,
        !           339:                                  struct ib_path *path, int rc,
        !           340:                                  struct ib_address_vector *av ) {
        !           341:        struct ib_connection *conn = ib_path_get_ownerdata ( path );
        !           342:        struct ib_queue_pair *qp = conn->qp;
        !           343:        union ib_mad mad;
        !           344:        struct ib_cm_connect_request *req = &mad.cm.cm_data.connect_request;
        !           345:        size_t private_data_len;
        !           346: 
        !           347:        /* Report failures */
        !           348:        if ( rc != 0 ) {
        !           349:                DBGC ( conn, "CM %p path lookup failed: %s\n",
        !           350:                       conn, strerror ( rc ) );
        !           351:                conn->op->changed ( ibdev, qp, conn, rc, NULL, 0 );
        !           352:                goto out;
        !           353:        }
        !           354: 
        !           355:        /* Update queue pair peer path */
        !           356:        memcpy ( &qp->av, av, sizeof ( qp->av ) );
        !           357: 
        !           358:        /* Construct connection request */
        !           359:        memset ( &mad, 0, sizeof ( mad ) );
        !           360:        mad.hdr.mgmt_class = IB_MGMT_CLASS_CM;
        !           361:        mad.hdr.class_version = IB_CM_CLASS_VERSION;
        !           362:        mad.hdr.method = IB_MGMT_METHOD_SEND;
        !           363:        mad.hdr.attr_id = htons ( IB_CM_ATTR_CONNECT_REQUEST );
        !           364:        req->local_id = htonl ( conn->local_id );
        !           365:        memcpy ( &req->service_id, &conn->service_id,
        !           366:                 sizeof ( req->service_id ) );
        !           367:        memcpy ( &req->local_ca, &ibdev->node_guid, sizeof ( req->local_ca ) );
        !           368:        req->local_qpn__responder_resources = htonl ( ( qp->qpn << 8 ) | 1 );
        !           369:        req->local_eecn__initiator_depth = htonl ( ( 0 << 8 ) | 1 );
        !           370:        req->remote_eecn__remote_timeout__service_type__ee_flow_ctrl =
        !           371:                htonl ( ( 0x14 << 3 ) | ( IB_CM_TRANSPORT_RC << 1 ) |
        !           372:                        ( 0 << 0 ) );
        !           373:        req->starting_psn__local_timeout__retry_count =
        !           374:                htonl ( ( qp->recv.psn << 8 ) | ( 0x14 << 3 ) |
        !           375:                        ( 0x07 << 0 ) );
        !           376:        req->pkey = htons ( ibdev->pkey );
        !           377:        req->payload_mtu__rdc_exists__rnr_retry =
        !           378:                ( ( IB_MTU_2048 << 4 ) | ( 1 << 3 ) | ( 0x07 << 0 ) );
        !           379:        req->max_cm_retries__srq = ( ( 0x0f << 4 ) | ( 0 << 3 ) );
        !           380:        req->primary.local_lid = htons ( ibdev->lid );
        !           381:        req->primary.remote_lid = htons ( conn->qp->av.lid );
        !           382:        memcpy ( &req->primary.local_gid, &ibdev->gid,
        !           383:                 sizeof ( req->primary.local_gid ) );
        !           384:        memcpy ( &req->primary.remote_gid, &conn->qp->av.gid,
        !           385:                 sizeof ( req->primary.remote_gid ) );
        !           386:        req->primary.flow_label__rate =
        !           387:                htonl ( ( 0 << 12 ) | ( conn->qp->av.rate << 0 ) );
        !           388:        req->primary.hop_limit = 0;
        !           389:        req->primary.sl__subnet_local =
        !           390:                ( ( conn->qp->av.sl << 4 ) | ( 1 << 3 ) );
        !           391:        req->primary.local_ack_timeout = ( 0x13 << 3 );
        !           392:        private_data_len = conn->private_data_len;
        !           393:        if ( private_data_len > sizeof ( req->private_data ) )
        !           394:                private_data_len = sizeof ( req->private_data );
        !           395:        memcpy ( &req->private_data, &conn->private_data, private_data_len );
        !           396: 
        !           397:        /* Create connection request */
        !           398:        av->qpn = IB_QPN_GSI;
        !           399:        av->qkey = IB_QKEY_GSI;
        !           400:        conn->madx = ib_create_madx ( ibdev, ibdev->gsi, &mad, av,
        !           401:                                      &ib_cm_req_op );
        !           402:        if ( ! conn->madx ) {
        !           403:                DBGC ( conn, "CM %p could not create connection request\n",
        !           404:                       conn );
        !           405:                conn->op->changed ( ibdev, qp, conn, rc, NULL, 0 );
        !           406:                goto out;
        !           407:        }
        !           408:        ib_madx_set_ownerdata ( conn->madx, conn );
        !           409: 
        !           410:  out:
        !           411:        /* Destroy the completed transaction */
        !           412:        ib_destroy_path ( ibdev, path );
        !           413:        conn->path = NULL;
        !           414: }
        !           415: 
        !           416: /** Connection path operations */
        !           417: static struct ib_path_operations ib_cm_path_op = {
        !           418:        .complete = ib_cm_path_complete,
        !           419: };
        !           420: 
        !           421: /**
        !           422:  * Create connection to remote QP
        !           423:  *
        !           424:  * @v ibdev            Infiniband device
        !           425:  * @v qp               Queue pair
        !           426:  * @v dgid             Target GID
        !           427:  * @v service_id       Target service ID
        !           428:  * @v private_data     Connection request private data
        !           429:  * @v private_data_len Length of connection request private data
        !           430:  * @v op               Connection operations
        !           431:  * @ret conn           Connection
        !           432:  */
        !           433: struct ib_connection *
        !           434: ib_create_conn ( struct ib_device *ibdev, struct ib_queue_pair *qp,
        !           435:                 union ib_gid *dgid, union ib_guid *service_id,
        !           436:                 void *private_data, size_t private_data_len,
        !           437:                 struct ib_connection_operations *op ) {
        !           438:        struct ib_connection *conn;
        !           439: 
        !           440:        /* Allocate and initialise request */
        !           441:        conn = zalloc ( sizeof ( *conn ) + private_data_len );
        !           442:        if ( ! conn )
        !           443:                goto err_alloc_conn;
        !           444:        conn->ibdev = ibdev;
        !           445:        conn->qp = qp;
        !           446:        memset ( &qp->av, 0, sizeof ( qp->av ) );
        !           447:        qp->av.gid_present = 1;
        !           448:        memcpy ( &qp->av.gid, dgid, sizeof ( qp->av.gid ) );
        !           449:        conn->local_id = random();
        !           450:        memcpy ( &conn->service_id, service_id, sizeof ( conn->service_id ) );
        !           451:        conn->op = op;
        !           452:        conn->private_data_len = private_data_len;
        !           453:        memcpy ( &conn->private_data, private_data, private_data_len );
        !           454: 
        !           455:        /* Create path */
        !           456:        conn->path = ib_create_path ( ibdev, &qp->av, &ib_cm_path_op );
        !           457:        if ( ! conn->path )
        !           458:                goto err_create_path;
        !           459:        ib_path_set_ownerdata ( conn->path, conn );
        !           460: 
        !           461:        /* Add to list of connections */
        !           462:        list_add ( &conn->list, &ib_cm_conns );
        !           463: 
        !           464:        DBGC ( conn, "CM %p created for IBDEV %p QPN %lx\n",
        !           465:               conn, ibdev, qp->qpn );
        !           466:        DBGC ( conn, "CM %p connecting to " IB_GID_FMT " " IB_GUID_FMT "\n",
        !           467:               conn, IB_GID_ARGS ( dgid ), IB_GUID_ARGS ( service_id ) );
        !           468: 
        !           469:        return conn;
        !           470: 
        !           471:        ib_destroy_path ( ibdev, conn->path );
        !           472:  err_create_path:
        !           473:        free ( conn );
        !           474:  err_alloc_conn:
        !           475:        return NULL;
        !           476: }
        !           477: 
        !           478: /**
        !           479:  * Destroy connection to remote QP
        !           480:  *
        !           481:  * @v ibdev            Infiniband device
        !           482:  * @v qp               Queue pair
        !           483:  * @v conn             Connection
        !           484:  */
        !           485: void ib_destroy_conn ( struct ib_device *ibdev,
        !           486:                       struct ib_queue_pair *qp __unused,
        !           487:                       struct ib_connection *conn ) {
        !           488: 
        !           489:        list_del ( &conn->list );
        !           490:        if ( conn->madx )
        !           491:                ib_destroy_madx ( ibdev, ibdev->gsi, conn->madx );
        !           492:        if ( conn->path )
        !           493:                ib_destroy_path ( ibdev, conn->path );
        !           494:        free ( conn );
        !           495: }

unix.superglobalmegacorp.com

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