/******************************************************************************
* fs-front.c
*
* Frontend driver for FS split device driver.
*
* Copyright (c) 2007, Grzegorz Milos, Sun Microsystems, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
#undef NDEBUG
#include <os.h>
#include <list.h>
#include <xmalloc.h>
#include <xenbus.h>
#include <gnttab.h>
#include <events.h>
#include <xen/io/fsif.h>
#include <fs.h>
#include <sched.h>
#define preempt_disable()
#define preempt_enable()
#define cmpxchg(p,o,n) synch_cmpxchg(p,o,n)
#ifdef FS_DEBUG
#define DEBUG(_f, _a...) \
printk("MINI_OS(file=fs-front.c, line=%d) " _f "\n", __LINE__, ## _a)
#else
#define DEBUG(_f, _a...) ((void)0)
#endif
struct fs_request;
struct fs_import *fs_import;
/******************************************************************************/
/* RING REQUEST/RESPONSES HANDLING */
/******************************************************************************/
struct fs_request
{
void *page;
grant_ref_t gref;
struct thread *thread; /* Thread blocked on this request */
struct fsif_response shadow_rsp; /* Response copy writen by the
interrupt handler */
};
/* Ring operations:
* FSIF ring is used differently to Linux-like split devices. This stems from
* the fact that no I/O request queue is present. The use of some of the macros
* defined in ring.h is not allowed, in particular:
* RING_PUSH_REQUESTS_AND_CHECK_NOTIFY cannot be used.
*
* The protocol used for FSIF ring is described below:
*
* In order to reserve a request the frontend:
* a) saves current frontend_ring->req_prod_pvt into a local variable
* b) checks that there are free request using the local req_prod_pvt
* c) tries to reserve the request using cmpxchg on frontend_ring->req_prod_pvt
* if cmpxchg fails, it means that someone reserved the request, start from
* a)
*
* In order to commit a request to the shared ring:
* a) cmpxchg shared_ring->req_prod from local req_prod_pvt to req_prod_pvt+1
* Loop if unsuccessful.
* NOTE: Request should be commited to the shared ring as quickly as possible,
* because otherwise other threads might busy loop trying to commit next
* requests. It also follows that preemption should be disabled, if
* possible, for the duration of the request construction.
*/
/* Number of free requests (for use on front side only). */
#define FS_RING_FREE_REQUESTS(_r, _req_prod_pvt) \
(RING_SIZE(_r) - (_req_prod_pvt - (_r)->rsp_cons))
static RING_IDX reserve_fsif_request(struct fs_import *import)
{
RING_IDX idx;
down(&import->reqs_sem);
preempt_disable();
again:
/* We will attempt to reserve slot idx */
idx = import->ring.req_prod_pvt;
ASSERT (FS_RING_FREE_REQUESTS(&import->ring, idx));
/* Attempt to reserve */
if(cmpxchg(&import->ring.req_prod_pvt, idx, idx+1) != idx)
goto again;
return idx;
}
static void commit_fsif_request(struct fs_import *import, RING_IDX idx)
{
while(cmpxchg(&import->ring.sring->req_prod, idx, idx+1) != idx)
{
printk("Failed to commit a request: req_prod=%d, idx=%d\n",
import->ring.sring->req_prod, idx);
}
preempt_enable();
/* NOTE: we cannot do anything clever about rsp_event, to hold off
* notifications, because we don't know if we are a single request (in which
* case we have to notify always), or a part of a larger request group
* (when, in some cases, notification isn't required) */
notify_remote_via_evtchn(import->local_port);
}
static inline void add_id_to_freelist(unsigned int id,unsigned short* freelist)