From nobody Thu Nov 28 08:35:49 2024 Delivered-To: importer@patchew.org Received-SPF: pass (zohomail.com: domain of lists.xenproject.org designates 192.237.175.120 as permitted sender) client-ip=192.237.175.120; envelope-from=xen-devel-bounces@lists.xenproject.org; helo=lists.xenproject.org; Authentication-Results: mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of lists.xenproject.org designates 192.237.175.120 as permitted sender) smtp.mailfrom=xen-devel-bounces@lists.xenproject.org; dmarc=pass(p=quarantine dis=none) header.from=suse.com ARC-Seal: i=1; a=rsa-sha256; t=1676026540; cv=none; d=zohomail.com; s=zohoarc; b=WEJwxZjWL/YORagfGrJv6Y/+UD1J2OTibjz5Hcdjw31OZid79sCbGLYRolt7lp5EQpj/vE8dtAUmd9RtlII9qR9MNSmG9F/ovonFZ73mglig6xSmiLAdRCDARMFdfZFh+mcT3ih3uETKz1Bin5ZYEV4XpikIm+tMbabpgKzsA5E= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1676026540; h=Content-Transfer-Encoding:Cc:Date:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:To; bh=AjLtvshfjPJLxhGdXS+IgJVdKslWSTSlXSgC0tGAfD0=; b=auPHOqbCsYUkm+/gl2lYT5FZ753nfs3Rky5l70gPvCkmDEKtn/lfVU42TMan3YM6ktqOd3HmQq+BKGYik3kYpwMMsEdfYi/qooT+jlVZyB0U8JmDzwUPEv0eaIxSODgnvVVKdNbKNzFNVzIsfWFFpkhugdeEtKF2s8Exf81lVB8= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of lists.xenproject.org designates 192.237.175.120 as permitted sender) smtp.mailfrom=xen-devel-bounces@lists.xenproject.org; dmarc=pass header.from= (p=quarantine dis=none) Return-Path: Received: from lists.xenproject.org (lists.xenproject.org [192.237.175.120]) by mx.zohomail.com with SMTPS id 1676026540969693.6498608686227; Fri, 10 Feb 2023 02:55:40 -0800 (PST) Received: from list by lists.xenproject.org with outflank-mailman.493298.763193 (Exim 4.92) (envelope-from ) id 1pQR3i-0002lf-SD; Fri, 10 Feb 2023 10:55:14 +0000 Received: by outflank-mailman (output) from mailman id 493298.763193; Fri, 10 Feb 2023 10:55:14 +0000 Received: from localhost ([127.0.0.1] helo=lists.xenproject.org) by lists.xenproject.org with esmtp (Exim 4.92) (envelope-from ) id 1pQR3i-0002lY-O4; Fri, 10 Feb 2023 10:55:14 +0000 Received: by outflank-mailman (input) for mailman id 493298; Fri, 10 Feb 2023 10:55:13 +0000 Received: from se1-gles-flk1-in.inumbo.com ([94.247.172.50] helo=se1-gles-flk1.inumbo.com) by lists.xenproject.org with esmtp (Exim 4.92) (envelope-from ) id 1pQQvn-000617-VA for xen-devel@lists.xenproject.org; Fri, 10 Feb 2023 10:47:04 +0000 Received: from smtp-out1.suse.de (smtp-out1.suse.de [2001:67c:2178:6::1c]) by se1-gles-flk1.inumbo.com (Halon) with ESMTPS id 3e9e5fb6-a930-11ed-93b5-47a8fe42b414; Fri, 10 Feb 2023 11:46:59 +0100 (CET) Received: from imap2.suse-dmz.suse.de (imap2.suse-dmz.suse.de [192.168.254.74]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature ECDSA (P-521) server-digest SHA512) (No client certificate requested) by smtp-out1.suse.de (Postfix) with ESMTPS id 286413FEC5; Fri, 10 Feb 2023 10:46:59 +0000 (UTC) Received: from imap2.suse-dmz.suse.de (imap2.suse-dmz.suse.de [192.168.254.74]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature ECDSA (P-521) server-digest SHA512) (No client certificate requested) by imap2.suse-dmz.suse.de (Postfix) with ESMTPS id EC1231325E; Fri, 10 Feb 2023 10:46:58 +0000 (UTC) Received: from dovecot-director2.suse.de ([192.168.254.65]) by imap2.suse-dmz.suse.de with ESMTPSA id t2tDOKIg5mMAVAAAMHmgww (envelope-from ); Fri, 10 Feb 2023 10:46:58 +0000 X-Outflank-Mailman: Message body and most headers restored to incoming version X-BeenThere: xen-devel@lists.xenproject.org List-Id: Xen developer discussion List-Unsubscribe: , List-Post: List-Help: List-Subscribe: , Errors-To: xen-devel-bounces@lists.xenproject.org Precedence: list Sender: "Xen-devel" X-Inumbo-ID: 3e9e5fb6-a930-11ed-93b5-47a8fe42b414 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=suse.com; s=susede1; t=1676026019; h=from:from:reply-to:date:date:message-id:message-id:to:to:cc:cc: mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=AjLtvshfjPJLxhGdXS+IgJVdKslWSTSlXSgC0tGAfD0=; b=NOT/uNDL/q3Nh9a1C32Rlo3B2Dyw3tSWtTow1koZyC/xfTeUU+zab4gzRWdQhPxW9MhA9h JfGEdoK2tm+/YaKjFmY6K14mBmlYXzsL0TURz4/IZFuIN9MwZKHFno9uQvLBOGAmFeIgi1 Ar1vr/wksJI1o4ekhjZl3Nyo8Kk0hYk= From: Juergen Gross To: minios-devel@lists.xenproject.org, xen-devel@lists.xenproject.org Cc: samuel.thibault@ens-lyon.org, wl@xen.org, Juergen Gross Subject: [PATCH v2 5/7] Mini-OS: add 9pfs transport layer Date: Fri, 10 Feb 2023 11:46:26 +0100 Message-Id: <20230210104628.14374-6-jgross@suse.com> X-Mailer: git-send-email 2.35.3 In-Reply-To: <20230210104628.14374-1-jgross@suse.com> References: <20230210104628.14374-1-jgross@suse.com> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-ZohoMail-DKIM: pass (identity @suse.com) X-ZM-MESSAGEID: 1676026541950100001 Content-Type: text/plain; charset="utf-8" Add the transport layer of 9pfs. This is basically the infrastructure to send requests to the backend and to receive the related answers via the rings. As a first example add the version and attach requests of the 9pfs protocol when mounting a new 9pfs device. For the version use the "9P2000.u" variant, as it is the smallest subset supported by the qemu based backend. Signed-off-by: Juergen Gross Reviewed-by: Samuel Thibault --- V2: - add more comments (Samuel Thibault) - log short copy (Samuel Thibault) - send event after consuming response (Samuel Thibault) - reaturn EAGAIN in case no free request could be got (Samuel Thibault) --- 9pfront.c | 478 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 478 insertions(+) diff --git a/9pfront.c b/9pfront.c index 89ecb3a1..0b8d5461 100644 --- a/9pfront.c +++ b/9pfront.c @@ -7,6 +7,8 @@ #include #include #include +#include +#include #include #include #include @@ -14,6 +16,9 @@ #include =20 #ifdef HAVE_LIBC + +#define N_REQS 64 + struct dev_9pfs { int id; char nodename[20]; @@ -22,6 +27,7 @@ struct dev_9pfs { =20 char *tag; const char *mnt; + unsigned int msize_max; =20 struct xen_9pfs_data_intf *intf; struct xen_9pfs_data data; @@ -32,14 +38,470 @@ struct dev_9pfs { evtchn_port_t evtchn; unsigned int ring_order; xenbus_event_queue events; + + unsigned int free_reqs; + struct req { + unsigned int id; + unsigned int next_free; /* N_REQS =3D=3D end of list. */ + unsigned int cmd; + int result; + bool inflight; + unsigned char *data; /* Returned data. */ + } req[N_REQS]; + + struct wait_queue_head waitq; + struct semaphore ring_out_sem; + struct semaphore ring_in_sem; }; =20 #define DEFAULT_9PFS_RING_ORDER 4 =20 +#define P9_CMD_VERSION 100 +#define P9_CMD_ATTACH 104 +#define P9_CMD_ERROR 107 + +#define P9_QID_SIZE 13 + +struct p9_header { + uint32_t size; + uint8_t cmd; + uint16_t tag; +} __attribute__((packed)); + +#define P9_VERSION "9P2000.u" +#define P9_ROOT_FID ~0 + static unsigned int ftype_9pfs; =20 +static struct req *get_free_req(struct dev_9pfs *dev) +{ + struct req *req; + + if ( dev->free_reqs =3D=3D N_REQS ) + return NULL; + + req =3D dev->req + dev->free_reqs; + dev->free_reqs =3D req->next_free; + + return req; +} + +static void put_free_req(struct dev_9pfs *dev, struct req *req) +{ + req->next_free =3D dev->free_reqs; + req->inflight =3D false; + req->data =3D NULL; + dev->free_reqs =3D req->id; +} + +static unsigned int ring_out_free(struct dev_9pfs *dev) +{ + RING_IDX ring_size =3D XEN_FLEX_RING_SIZE(dev->ring_order); + unsigned int queued; + + queued =3D xen_9pfs_queued(dev->prod_pvt_out, dev->intf->out_cons, rin= g_size); + rmb(); + + return ring_size - queued; +} + +static unsigned int ring_in_data(struct dev_9pfs *dev) +{ + RING_IDX ring_size =3D XEN_FLEX_RING_SIZE(dev->ring_order); + unsigned int queued; + + queued =3D xen_9pfs_queued(dev->intf->in_prod, dev->cons_pvt_in, ring_= size); + rmb(); + + return queued; +} + +static void copy_to_ring(struct dev_9pfs *dev, void *data, unsigned int le= n) +{ + RING_IDX ring_size =3D XEN_FLEX_RING_SIZE(dev->ring_order); + RING_IDX prod =3D xen_9pfs_mask(dev->prod_pvt_out, ring_size); + RING_IDX cons =3D xen_9pfs_mask(dev->intf->out_cons, ring_size); + + xen_9pfs_write_packet(dev->data.out, data, len, &prod, cons, ring_size= ); + dev->prod_pvt_out +=3D len; +} + +static void copy_from_ring(struct dev_9pfs *dev, void *data, unsigned int = len) +{ + RING_IDX ring_size =3D XEN_FLEX_RING_SIZE(dev->ring_order); + RING_IDX prod =3D xen_9pfs_mask(dev->intf->in_prod, ring_size); + RING_IDX cons =3D xen_9pfs_mask(dev->cons_pvt_in, ring_size); + + xen_9pfs_read_packet(data, dev->data.in, len, prod, &cons, ring_size); + dev->cons_pvt_in +=3D len; +} + +/* + * send_9p() and rcv_9p() are using a special format string for specifying + * the kind of data sent/expected. Each data item is represented by a sing= le + * character: + * U: 4 byte unsigned integer (uint32_t) + * S: String (2 byte length + characters) + * in the rcv_9p() case the data for string is allocated (length omitte= d, + * string terminated by a NUL character) + * Q: A 13 byte "qid", consisting of 1 byte file type, 4 byte file version + * and 8 bytes unique file id. Only valid for receiving. + */ +static void send_9p(struct dev_9pfs *dev, struct req *req, const char *fmt= , ...) +{ + struct p9_header hdr; + va_list ap, aq; + const char *f; + uint32_t intval; + uint16_t len; + char *strval; + + hdr.size =3D sizeof(hdr); + hdr.cmd =3D req->cmd; + hdr.tag =3D req->id; + + va_start(ap, fmt); + + va_copy(aq, ap); + for ( f =3D fmt; *f; f++ ) + { + switch ( *f ) + { + case 'U': + hdr.size +=3D 4; + intval =3D va_arg(aq, unsigned int); + break; + case 'S': + hdr.size +=3D 2; + strval =3D va_arg(aq, char *); + hdr.size +=3D strlen(strval); + break; + default: + printk("send_9p: unknown format character %c\n", *f); + break; + } + } + va_end(aq); + + /* + * Waiting for free space must be done in the critical section! + * Otherwise we might get overtaken by other short requests. + */ + down(&dev->ring_out_sem); + + wait_event(dev->waitq, ring_out_free(dev) >=3D hdr.size); + + copy_to_ring(dev, &hdr, sizeof(hdr)); + for ( f =3D fmt; *f; f++ ) + { + switch ( *f ) + { + case 'U': + intval =3D va_arg(ap, unsigned int); + copy_to_ring(dev, &intval, sizeof(intval)); + break; + case 'S': + strval =3D va_arg(ap, char *); + len =3D strlen(strval); + copy_to_ring(dev, &len, sizeof(len)); + copy_to_ring(dev, strval, len); + break; + } + } + + wmb(); /* Data on ring must be seen before updating index. */ + dev->intf->out_prod =3D dev->prod_pvt_out; + req->inflight =3D true; + + up(&dev->ring_out_sem); + + va_end(ap); + + notify_remote_via_evtchn(dev->evtchn); +} + +/* + * Using an opportunistic approach for receiving data: in case multiple + * requests are outstanding (which is very unlikely), we nevertheless need + * to consume all data available until we reach the desired request. + * For requests other than the one we are waiting for, we link the complete + * data to the request via an intermediate buffer. For our own request we = can + * omit that buffer and directly fill the caller provided variables. + * + * Helper functions: + * + * copy_bufs(): copy raw data into a target buffer. There can be 2 source + * buffers involved (in case the copy is done from the ring and it is ac= ross + * the ring end). The buffer pointers and lengths are updated according = to + * the number of bytes copied. + * + * rcv_9p_copy(): copy the data (without the generic header) of a 9p respo= nse + * to the specified variables using the specified format string for + * deciphering the single item types. The decision whether to copy from = the + * ring or an allocated buffer is done via the "hdr" parameter, which is + * NULL in the buffer case (in that case the header is located at the st= art + * of the buffer). + * + * rcv_9p_one(): Checks for an already filled buffer with the correct tag = in + * it. If none is found, consumes one response. It checks the tag of the + * response in order to decide whether to allocate a buffer for putting = the + * data into, or to fill the user supplied variables. Return true, if the + * tag did match. Waits if no data is ready to be consumed. + */ +static void copy_bufs(unsigned char **buf1, unsigned char **buf2, + unsigned int *len1, unsigned int *len2, + void *target, unsigned int len) +{ + if ( len <=3D *len1 ) + { + memcpy(target, *buf1, len); + *buf1 +=3D len; + *len1 -=3D len; + } + else + { + memcpy(target, *buf1, *len1); + target =3D (char *)target + *len1; + len -=3D *len1; + *buf1 =3D *buf2; + *len1 =3D *len2; + *buf2 =3D NULL; + *len2 =3D 0; + if ( len > *len1 ) + { + printk("9pfs: short copy (dropping %u bytes)\n", len - *len1); + len =3D *len1; + } + memcpy(target, *buf1, *len1); + } +} + +static void rcv_9p_copy(struct dev_9pfs *dev, struct req *req, + struct p9_header *hdr, const char *fmt, va_list ap) +{ + struct p9_header *h =3D hdr ? hdr : (void *)req->data; + RING_IDX cons =3D dev->cons_pvt_in + h->size - sizeof(*h); + RING_IDX ring_size =3D XEN_FLEX_RING_SIZE(dev->ring_order); + unsigned char *buf1, *buf2; + unsigned int len1, len2; + const char *f; + char *str; + uint16_t len; + uint32_t err; + uint32_t *val; + char **strval; + uint8_t *qval; + + if ( hdr ) + { + buf1 =3D xen_9pfs_get_ring_ptr(dev->data.in, dev->cons_pvt_in, rin= g_size); + buf2 =3D xen_9pfs_get_ring_ptr(dev->data.in, 0, ring_size); + len1 =3D ring_size - xen_9pfs_mask(dev->cons_pvt_in, ring_size); + if ( len1 > h->size - sizeof(*h) ) + len1 =3D h->size - sizeof(*h); + len2 =3D h->size - sizeof(*h) - len1; + } + else + { + buf1 =3D req->data + sizeof(*h); + buf2 =3D NULL; + len1 =3D h->size - sizeof(*h); + len2 =3D 0; + } + + if ( h->cmd =3D=3D P9_CMD_ERROR ) + { + copy_bufs(&buf1, &buf2, &len1, &len2, &len, sizeof(len)); + str =3D malloc(len + 1); + copy_bufs(&buf1, &buf2, &len1, &len2, str, len); + str[len] =3D 0; + printk("9pfs: request %u resulted in \"%s\"\n", req->cmd, str); + free(str); + err =3D EIO; + copy_bufs(&buf1, &buf2, &len1, &len2, &err, sizeof(err)); + req->result =3D err; + + if ( hdr ) + dev->cons_pvt_in =3D cons; + + return; + } + + if ( h->cmd !=3D req->cmd + 1 ) + { + req->result =3D EDOM; + printk("9pfs: illegal response: wrong return type (%u instead of %= u)\n", + h->cmd, req->cmd + 1); + + if ( hdr ) + dev->cons_pvt_in =3D cons; + + return; + } + + req->result =3D 0; + + for ( f =3D fmt; *f; f++ ) + { + switch ( *f ) + { + case 'U': + val =3D va_arg(ap, uint32_t *); + copy_bufs(&buf1, &buf2, &len1, &len2, val, sizeof(*val)); + break; + case 'S': + strval =3D va_arg(ap, char **); + copy_bufs(&buf1, &buf2, &len1, &len2, &len, sizeof(len)); + *strval =3D malloc(len + 1); + copy_bufs(&buf1, &buf2, &len1, &len2, *strval, len); + (*strval)[len] =3D 0; + break; + case 'Q': + qval =3D va_arg(ap, uint8_t *); + copy_bufs(&buf1, &buf2, &len1, &len2, qval, P9_QID_SIZE); + break; + default: + printk("rcv_9p: unknown format character %c\n", *f); + break; + } + } + + if ( hdr ) + dev->cons_pvt_in =3D cons; +} + +static bool rcv_9p_one(struct dev_9pfs *dev, struct req *req, const char *= fmt, + va_list ap) +{ + struct p9_header hdr; + struct req *tmp; + + if ( req->data ) + { + rcv_9p_copy(dev, req, NULL, fmt, ap); + free(req->data); + req->data =3D NULL; + + return true; + } + + wait_event(dev->waitq, ring_in_data(dev) >=3D sizeof(hdr)); + + copy_from_ring(dev, &hdr, sizeof(hdr)); + + wait_event(dev->waitq, ring_in_data(dev) >=3D hdr.size - sizeof(hdr)); + + tmp =3D dev->req + hdr.tag; + if ( hdr.tag >=3D N_REQS || !tmp->inflight ) + { + printk("9pfs: illegal response: %s\n", + hdr.tag >=3D N_REQS ? "tag out of bounds" : "request not pe= nding"); + dev->cons_pvt_in +=3D hdr.size - sizeof(hdr); + + return false; + } + + tmp->inflight =3D false; + + if ( tmp !=3D req ) + { + tmp->data =3D malloc(hdr.size); + memcpy(tmp->data, &hdr, sizeof(hdr)); + copy_from_ring(dev, tmp->data + sizeof(hdr), hdr.size - sizeof(hdr= )); + + return false; + } + + rcv_9p_copy(dev, req, &hdr, fmt, ap); + + return true; +} + +static void rcv_9p(struct dev_9pfs *dev, struct req *req, const char *fmt,= ...) +{ + va_list ap; + + va_start(ap, fmt); + + down(&dev->ring_in_sem); + + while ( !rcv_9p_one(dev, req, fmt, ap) ); + + rmb(); /* Read all data before updating ring index. */ + dev->intf->in_cons =3D dev->cons_pvt_in; + + notify_remote_via_evtchn(dev->evtchn); + + up(&dev->ring_in_sem); + + va_end(ap); +} + +static int p9_version(struct dev_9pfs *dev) +{ + unsigned int msize =3D XEN_FLEX_RING_SIZE(dev->ring_order) / 2; + struct req *req =3D get_free_req(dev); + char *verret; + int ret; + + if ( !req ) + return EAGAIN; + + req->cmd =3D P9_CMD_VERSION; + send_9p(dev, req, "US", msize, P9_VERSION); + rcv_9p(dev, req, "US", &dev->msize_max, &verret); + ret =3D req->result; + + put_free_req(dev, req); + + if ( ret ) + return ret; + + if ( strcmp(verret, P9_VERSION) ) + ret =3D ENOMSG; + free(verret); + + return ret; +} + +static int p9_attach(struct dev_9pfs *dev) +{ + uint32_t fid =3D P9_ROOT_FID; + uint32_t afid =3D 0; + uint32_t uid =3D 0; + uint8_t qid[P9_QID_SIZE]; + struct req *req =3D get_free_req(dev); + int ret; + + if ( !req ) + return EAGAIN; + + req->cmd =3D P9_CMD_ATTACH; + send_9p(dev, req, "UUSSU", fid, afid, "root", "root", uid); + rcv_9p(dev, req, "Q", qid); + ret =3D req->result; + + put_free_req(dev, req); + + return ret; +} + +static int connect_9pfs(struct dev_9pfs *dev) +{ + int ret; + + ret =3D p9_version(dev); + if ( ret ) + return ret; + + return p9_attach(dev); +} + static void intr_9pfs(evtchn_port_t port, struct pt_regs *regs, void *data) { + struct dev_9pfs *dev =3D data; + + wake_up(&dev->waitq); } =20 static int open_9pfs(struct mount_point *mnt, const char *pathname, int fl= ags, @@ -87,6 +549,16 @@ void *init_9pfront(unsigned int id, const char *mnt) memset(dev, 0, sizeof(*dev)); snprintf(dev->nodename, sizeof(dev->nodename), "device/9pfs/%u", id); dev->id =3D id; + init_waitqueue_head(&dev->waitq); + init_SEMAPHORE(&dev->ring_out_sem, 1); + init_SEMAPHORE(&dev->ring_in_sem, 1); + + for ( i =3D 0; i < N_REQS; i++ ) + { + dev->req[i].id =3D i; + dev->req[i].next_free =3D i + 1; + } + dev->free_reqs =3D 0; =20 msg =3D xenbus_read_unsigned(XBT_NIL, dev->nodename, "backend-id", &de= v->dom); if ( msg ) @@ -205,6 +677,12 @@ void *init_9pfront(unsigned int id, const char *mnt) =20 unmask_evtchn(dev->evtchn); =20 + if ( connect_9pfs(dev) ) + { + reason =3D "9pfs connect failed"; + goto err; + } + dev->mnt =3D mnt; if ( mount(dev->mnt, dev, open_9pfs) ) { --=20 2.35.3