diff options
-rw-r--r-- | sound/xen/Kconfig | 1 | ||||
-rw-r--r-- | sound/xen/Makefile | 1 | ||||
-rw-r--r-- | sound/xen/xen_snd_front.c | 7 | ||||
-rw-r--r-- | sound/xen/xen_snd_front.h | 4 | ||||
-rw-r--r-- | sound/xen/xen_snd_front_alsa.c | 102 | ||||
-rw-r--r-- | sound/xen/xen_snd_front_shbuf.c | 194 | ||||
-rw-r--r-- | sound/xen/xen_snd_front_shbuf.h | 36 |
7 files changed, 84 insertions, 261 deletions
diff --git a/sound/xen/Kconfig b/sound/xen/Kconfig index 4f1fceea82d2..e4d7beb4df1c 100644 --- a/sound/xen/Kconfig +++ b/sound/xen/Kconfig @@ -5,6 +5,7 @@ config SND_XEN_FRONTEND depends on XEN select SND_PCM select XEN_XENBUS_FRONTEND + select XEN_FRONT_PGDIR_SHBUF help Choose this option if you want to enable a para-virtualized frontend sound driver for Xen guest OSes. diff --git a/sound/xen/Makefile b/sound/xen/Makefile index 1e6470ecc2f2..24031775b715 100644 --- a/sound/xen/Makefile +++ b/sound/xen/Makefile @@ -3,7 +3,6 @@ snd_xen_front-objs := xen_snd_front.o \ xen_snd_front_cfg.o \ xen_snd_front_evtchnl.o \ - xen_snd_front_shbuf.o \ xen_snd_front_alsa.o obj-$(CONFIG_SND_XEN_FRONTEND) += snd_xen_front.o diff --git a/sound/xen/xen_snd_front.c b/sound/xen/xen_snd_front.c index b089b13b5160..a9e5c2cd7698 100644 --- a/sound/xen/xen_snd_front.c +++ b/sound/xen/xen_snd_front.c @@ -16,12 +16,12 @@ #include <xen/xen.h> #include <xen/xenbus.h> +#include <xen/xen-front-pgdir-shbuf.h> #include <xen/interface/io/sndif.h> #include "xen_snd_front.h" #include "xen_snd_front_alsa.h" #include "xen_snd_front_evtchnl.h" -#include "xen_snd_front_shbuf.h" static struct xensnd_req * be_stream_prepare_req(struct xen_snd_front_evtchnl *evtchnl, u8 operation) @@ -82,7 +82,7 @@ int xen_snd_front_stream_query_hw_param(struct xen_snd_front_evtchnl *evtchnl, } int xen_snd_front_stream_prepare(struct xen_snd_front_evtchnl *evtchnl, - struct xen_snd_front_shbuf *sh_buf, + struct xen_front_pgdir_shbuf *shbuf, u8 format, unsigned int channels, unsigned int rate, u32 buffer_sz, u32 period_sz) @@ -99,7 +99,8 @@ int xen_snd_front_stream_prepare(struct xen_snd_front_evtchnl *evtchnl, req->op.open.pcm_rate = rate; req->op.open.buffer_sz = buffer_sz; req->op.open.period_sz = period_sz; - req->op.open.gref_directory = xen_snd_front_shbuf_get_dir_start(sh_buf); + req->op.open.gref_directory = + xen_front_pgdir_shbuf_get_dir_start(shbuf); mutex_unlock(&evtchnl->ring_io_lock); ret = be_stream_do_io(evtchnl); diff --git a/sound/xen/xen_snd_front.h b/sound/xen/xen_snd_front.h index a2ea2463bcc5..05611f113b94 100644 --- a/sound/xen/xen_snd_front.h +++ b/sound/xen/xen_snd_front.h @@ -16,7 +16,7 @@ struct xen_snd_front_card_info; struct xen_snd_front_evtchnl; struct xen_snd_front_evtchnl_pair; -struct xen_snd_front_shbuf; +struct xen_front_pgdir_shbuf; struct xensnd_query_hw_param; struct xen_snd_front_info { @@ -35,7 +35,7 @@ int xen_snd_front_stream_query_hw_param(struct xen_snd_front_evtchnl *evtchnl, struct xensnd_query_hw_param *hw_param_resp); int xen_snd_front_stream_prepare(struct xen_snd_front_evtchnl *evtchnl, - struct xen_snd_front_shbuf *sh_buf, + struct xen_front_pgdir_shbuf *shbuf, u8 format, unsigned int channels, unsigned int rate, u32 buffer_sz, u32 period_sz); diff --git a/sound/xen/xen_snd_front_alsa.c b/sound/xen/xen_snd_front_alsa.c index 2cbd9679aca1..a7f413cb704d 100644 --- a/sound/xen/xen_snd_front_alsa.c +++ b/sound/xen/xen_snd_front_alsa.c @@ -15,17 +15,24 @@ #include <sound/pcm_params.h> #include <xen/xenbus.h> +#include <xen/xen-front-pgdir-shbuf.h> #include "xen_snd_front.h" #include "xen_snd_front_alsa.h" #include "xen_snd_front_cfg.h" #include "xen_snd_front_evtchnl.h" -#include "xen_snd_front_shbuf.h" struct xen_snd_front_pcm_stream_info { struct xen_snd_front_info *front_info; struct xen_snd_front_evtchnl_pair *evt_pair; - struct xen_snd_front_shbuf sh_buf; + + /* This is the shared buffer with its backing storage. */ + struct xen_front_pgdir_shbuf shbuf; + u8 *buffer; + size_t buffer_sz; + int num_pages; + struct page **pages; + int index; bool is_open; @@ -214,12 +221,20 @@ static void stream_clear(struct xen_snd_front_pcm_stream_info *stream) stream->out_frames = 0; atomic_set(&stream->hw_ptr, 0); xen_snd_front_evtchnl_pair_clear(stream->evt_pair); - xen_snd_front_shbuf_clear(&stream->sh_buf); + memset(&stream->shbuf, 0, sizeof(stream->shbuf)); + stream->buffer = NULL; + stream->buffer_sz = 0; + stream->pages = NULL; + stream->num_pages = 0; } static void stream_free(struct xen_snd_front_pcm_stream_info *stream) { - xen_snd_front_shbuf_free(&stream->sh_buf); + xen_front_pgdir_shbuf_unmap(&stream->shbuf); + xen_front_pgdir_shbuf_free(&stream->shbuf); + if (stream->buffer) + free_pages_exact(stream->buffer, stream->buffer_sz); + kfree(stream->pages); stream_clear(stream); } @@ -421,10 +436,34 @@ static int alsa_close(struct snd_pcm_substream *substream) return 0; } +static int shbuf_setup_backstore(struct xen_snd_front_pcm_stream_info *stream, + size_t buffer_sz) +{ + int i; + + stream->buffer = alloc_pages_exact(stream->buffer_sz, GFP_KERNEL); + if (!stream->buffer) + return -ENOMEM; + + stream->buffer_sz = buffer_sz; + stream->num_pages = DIV_ROUND_UP(stream->buffer_sz, PAGE_SIZE); + stream->pages = kcalloc(stream->num_pages, sizeof(struct page *), + GFP_KERNEL); + if (!stream->pages) + return -ENOMEM; + + for (i = 0; i < stream->num_pages; i++) + stream->pages[i] = virt_to_page(stream->buffer + i * PAGE_SIZE); + + return 0; +} + static int alsa_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { struct xen_snd_front_pcm_stream_info *stream = stream_get(substream); + struct xen_snd_front_info *front_info = stream->front_info; + struct xen_front_pgdir_shbuf_cfg buf_cfg; int ret; /* @@ -432,19 +471,32 @@ static int alsa_hw_params(struct snd_pcm_substream *substream, * so free the previously allocated shared buffer if any. */ stream_free(stream); + ret = shbuf_setup_backstore(stream, params_buffer_bytes(params)); + if (ret < 0) + goto fail; - ret = xen_snd_front_shbuf_alloc(stream->front_info->xb_dev, - &stream->sh_buf, - params_buffer_bytes(params)); - if (ret < 0) { - stream_free(stream); - dev_err(&stream->front_info->xb_dev->dev, - "Failed to allocate buffers for stream with index %d\n", - stream->index); - return ret; - } + memset(&buf_cfg, 0, sizeof(buf_cfg)); + buf_cfg.xb_dev = front_info->xb_dev; + buf_cfg.pgdir = &stream->shbuf; + buf_cfg.num_pages = stream->num_pages; + buf_cfg.pages = stream->pages; + + ret = xen_front_pgdir_shbuf_alloc(&buf_cfg); + if (ret < 0) + goto fail; + + ret = xen_front_pgdir_shbuf_map(&stream->shbuf); + if (ret < 0) + goto fail; return 0; + +fail: + stream_free(stream); + dev_err(&front_info->xb_dev->dev, + "Failed to allocate buffers for stream with index %d\n", + stream->index); + return ret; } static int alsa_hw_free(struct snd_pcm_substream *substream) @@ -476,7 +528,7 @@ static int alsa_prepare(struct snd_pcm_substream *substream) sndif_format = ret; ret = xen_snd_front_stream_prepare(&stream->evt_pair->req, - &stream->sh_buf, + &stream->shbuf, sndif_format, runtime->channels, runtime->rate, @@ -556,10 +608,10 @@ static int alsa_pb_copy_user(struct snd_pcm_substream *substream, { struct xen_snd_front_pcm_stream_info *stream = stream_get(substream); - if (unlikely(pos + count > stream->sh_buf.buffer_sz)) + if (unlikely(pos + count > stream->buffer_sz)) return -EINVAL; - if (copy_from_user(stream->sh_buf.buffer + pos, src, count)) + if (copy_from_user(stream->buffer + pos, src, count)) return -EFAULT; return xen_snd_front_stream_write(&stream->evt_pair->req, pos, count); @@ -571,10 +623,10 @@ static int alsa_pb_copy_kernel(struct snd_pcm_substream *substream, { struct xen_snd_front_pcm_stream_info *stream = stream_get(substream); - if (unlikely(pos + count > stream->sh_buf.buffer_sz)) + if (unlikely(pos + count > stream->buffer_sz)) return -EINVAL; - memcpy(stream->sh_buf.buffer + pos, src, count); + memcpy(stream->buffer + pos, src, count); return xen_snd_front_stream_write(&stream->evt_pair->req, pos, count); } @@ -586,14 +638,14 @@ static int alsa_cap_copy_user(struct snd_pcm_substream *substream, struct xen_snd_front_pcm_stream_info *stream = stream_get(substream); int ret; - if (unlikely(pos + count > stream->sh_buf.buffer_sz)) + if (unlikely(pos + count > stream->buffer_sz)) return -EINVAL; ret = xen_snd_front_stream_read(&stream->evt_pair->req, pos, count); if (ret < 0) return ret; - return copy_to_user(dst, stream->sh_buf.buffer + pos, count) ? + return copy_to_user(dst, stream->buffer + pos, count) ? -EFAULT : 0; } @@ -604,14 +656,14 @@ static int alsa_cap_copy_kernel(struct snd_pcm_substream *substream, struct xen_snd_front_pcm_stream_info *stream = stream_get(substream); int ret; - if (unlikely(pos + count > stream->sh_buf.buffer_sz)) + if (unlikely(pos + count > stream->buffer_sz)) return -EINVAL; ret = xen_snd_front_stream_read(&stream->evt_pair->req, pos, count); if (ret < 0) return ret; - memcpy(dst, stream->sh_buf.buffer + pos, count); + memcpy(dst, stream->buffer + pos, count); return 0; } @@ -622,10 +674,10 @@ static int alsa_pb_fill_silence(struct snd_pcm_substream *substream, { struct xen_snd_front_pcm_stream_info *stream = stream_get(substream); - if (unlikely(pos + count > stream->sh_buf.buffer_sz)) + if (unlikely(pos + count > stream->buffer_sz)) return -EINVAL; - memset(stream->sh_buf.buffer + pos, 0, count); + memset(stream->buffer + pos, 0, count); return xen_snd_front_stream_write(&stream->evt_pair->req, pos, count); } diff --git a/sound/xen/xen_snd_front_shbuf.c b/sound/xen/xen_snd_front_shbuf.c deleted file mode 100644 index 07ac176a41ba..000000000000 --- a/sound/xen/xen_snd_front_shbuf.c +++ /dev/null @@ -1,194 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 OR MIT - -/* - * Xen para-virtual sound device - * - * Copyright (C) 2016-2018 EPAM Systems Inc. - * - * Author: Oleksandr Andrushchenko <oleksandr_andrushchenko@epam.com> - */ - -#include <linux/kernel.h> -#include <xen/xen.h> -#include <xen/xenbus.h> - -#include "xen_snd_front_shbuf.h" - -grant_ref_t xen_snd_front_shbuf_get_dir_start(struct xen_snd_front_shbuf *buf) -{ - if (!buf->grefs) - return GRANT_INVALID_REF; - - return buf->grefs[0]; -} - -void xen_snd_front_shbuf_clear(struct xen_snd_front_shbuf *buf) -{ - memset(buf, 0, sizeof(*buf)); -} - -void xen_snd_front_shbuf_free(struct xen_snd_front_shbuf *buf) -{ - int i; - - if (buf->grefs) { - for (i = 0; i < buf->num_grefs; i++) - if (buf->grefs[i] != GRANT_INVALID_REF) - gnttab_end_foreign_access(buf->grefs[i], - 0, 0UL); - kfree(buf->grefs); - } - kfree(buf->directory); - free_pages_exact(buf->buffer, buf->buffer_sz); - xen_snd_front_shbuf_clear(buf); -} - -/* - * number of grant references a page can hold with respect to the - * xensnd_page_directory header - */ -#define XENSND_NUM_GREFS_PER_PAGE ((XEN_PAGE_SIZE - \ - offsetof(struct xensnd_page_directory, gref)) / \ - sizeof(grant_ref_t)) - -static void fill_page_dir(struct xen_snd_front_shbuf *buf, - int num_pages_dir) -{ - struct xensnd_page_directory *page_dir; - unsigned char *ptr; - int i, cur_gref, grefs_left, to_copy; - - ptr = buf->directory; - grefs_left = buf->num_grefs - num_pages_dir; - /* - * skip grant references at the beginning, they are for pages granted - * for the page directory itself - */ - cur_gref = num_pages_dir; - for (i = 0; i < num_pages_dir; i++) { - page_dir = (struct xensnd_page_directory *)ptr; - if (grefs_left <= XENSND_NUM_GREFS_PER_PAGE) { - to_copy = grefs_left; - page_dir->gref_dir_next_page = GRANT_INVALID_REF; - } else { - to_copy = XENSND_NUM_GREFS_PER_PAGE; - page_dir->gref_dir_next_page = buf->grefs[i + 1]; - } - - memcpy(&page_dir->gref, &buf->grefs[cur_gref], - to_copy * sizeof(grant_ref_t)); - - ptr += XEN_PAGE_SIZE; - grefs_left -= to_copy; - cur_gref += to_copy; - } -} - -static int grant_references(struct xenbus_device *xb_dev, - struct xen_snd_front_shbuf *buf, - int num_pages_dir, int num_pages_buffer, - int num_grefs) -{ - grant_ref_t priv_gref_head; - unsigned long frame; - int ret, i, j, cur_ref; - int otherend_id; - - ret = gnttab_alloc_grant_references(num_grefs, &priv_gref_head); - if (ret) - return ret; - - buf->num_grefs = num_grefs; - otherend_id = xb_dev->otherend_id; - j = 0; - - for (i = 0; i < num_pages_dir; i++) { - cur_ref = gnttab_claim_grant_reference(&priv_gref_head); - if (cur_ref < 0) { - ret = cur_ref; - goto fail; - } - - frame = xen_page_to_gfn(virt_to_page(buf->directory + - XEN_PAGE_SIZE * i)); - gnttab_grant_foreign_access_ref(cur_ref, otherend_id, frame, 0); - buf->grefs[j++] = cur_ref; - } - - for (i = 0; i < num_pages_buffer; i++) { - cur_ref = gnttab_claim_grant_reference(&priv_gref_head); - if (cur_ref < 0) { - ret = cur_ref; - goto fail; - } - - frame = xen_page_to_gfn(virt_to_page(buf->buffer + - XEN_PAGE_SIZE * i)); - gnttab_grant_foreign_access_ref(cur_ref, otherend_id, frame, 0); - buf->grefs[j++] = cur_ref; - } - - gnttab_free_grant_references(priv_gref_head); - fill_page_dir(buf, num_pages_dir); - return 0; - -fail: - gnttab_free_grant_references(priv_gref_head); - return ret; -} - -static int alloc_int_buffers(struct xen_snd_front_shbuf *buf, - int num_pages_dir, int num_pages_buffer, - int num_grefs) -{ - buf->grefs = kcalloc(num_grefs, sizeof(*buf->grefs), GFP_KERNEL); - if (!buf->grefs) - return -ENOMEM; - - buf->directory = kcalloc(num_pages_dir, XEN_PAGE_SIZE, GFP_KERNEL); - if (!buf->directory) - goto fail; - - buf->buffer_sz = num_pages_buffer * XEN_PAGE_SIZE; - buf->buffer = alloc_pages_exact(buf->buffer_sz, GFP_KERNEL); - if (!buf->buffer) - goto fail; - - return 0; - -fail: - kfree(buf->grefs); - buf->grefs = NULL; - kfree(buf->directory); - buf->directory = NULL; - return -ENOMEM; -} - -int xen_snd_front_shbuf_alloc(struct xenbus_device *xb_dev, - struct xen_snd_front_shbuf *buf, - unsigned int buffer_sz) -{ - int num_pages_buffer, num_pages_dir, num_grefs; - int ret; - - xen_snd_front_shbuf_clear(buf); - - num_pages_buffer = DIV_ROUND_UP(buffer_sz, XEN_PAGE_SIZE); - /* number of pages the page directory consumes itself */ - num_pages_dir = DIV_ROUND_UP(num_pages_buffer, - XENSND_NUM_GREFS_PER_PAGE); - num_grefs = num_pages_buffer + num_pages_dir; - - ret = alloc_int_buffers(buf, num_pages_dir, - num_pages_buffer, num_grefs); - if (ret < 0) - return ret; - - ret = grant_references(xb_dev, buf, num_pages_dir, num_pages_buffer, - num_grefs); - if (ret < 0) - return ret; - - fill_page_dir(buf, num_pages_dir); - return 0; -} diff --git a/sound/xen/xen_snd_front_shbuf.h b/sound/xen/xen_snd_front_shbuf.h deleted file mode 100644 index d28e97c47b2c..000000000000 --- a/sound/xen/xen_snd_front_shbuf.h +++ /dev/null @@ -1,36 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 OR MIT */ - -/* - * Xen para-virtual sound device - * - * Copyright (C) 2016-2018 EPAM Systems Inc. - * - * Author: Oleksandr Andrushchenko <oleksandr_andrushchenko@epam.com> - */ - -#ifndef __XEN_SND_FRONT_SHBUF_H -#define __XEN_SND_FRONT_SHBUF_H - -#include <xen/grant_table.h> - -#include "xen_snd_front_evtchnl.h" - -struct xen_snd_front_shbuf { - int num_grefs; - grant_ref_t *grefs; - u8 *directory; - u8 *buffer; - size_t buffer_sz; -}; - -grant_ref_t xen_snd_front_shbuf_get_dir_start(struct xen_snd_front_shbuf *buf); - -int xen_snd_front_shbuf_alloc(struct xenbus_device *xb_dev, - struct xen_snd_front_shbuf *buf, - unsigned int buffer_sz); - -void xen_snd_front_shbuf_clear(struct xen_snd_front_shbuf *buf); - -void xen_snd_front_shbuf_free(struct xen_snd_front_shbuf *buf); - -#endif /* __XEN_SND_FRONT_SHBUF_H */ |