Something went wrong. Try again.
(I'm so sorry!) Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
Something went wrong. Try again.
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434// SPDX-License-Identifier: GPL-2.0+/* * IBM Power Systems Virtual Management Channel Support. * * Copyright (c) 2004, 2018 IBM Corp. * Dave Engebretsen engebret@us.ibm.com * Steven Royer seroyer@linux.vnet.ibm.com * Adam Reznechek adreznec@linux.vnet.ibm.com * Bryant G. Ly <bryantly@linux.vnet.ibm.com> */
#include <linux/module.h>#include <linux/kernel.h>#include <linux/kthread.h>#include <linux/major.h>#include <linux/string.h>#include <linux/fcntl.h>#include <linux/slab.h>#include <linux/poll.h>#include <linux/init.h>#include <linux/fs.h>#include <linux/interrupt.h>#include <linux/spinlock.h>#include <linux/percpu.h>#include <linux/delay.h>#include <linux/uaccess.h>#include <linux/io.h>#include <linux/miscdevice.h>#include <linux/sched/signal.h>
#include <asm/byteorder.h>#include <asm/irq.h>#include <asm/vio.h>
#include "ibmvmc.h"
#define IBMVMC_DRIVER_VERSION "1.0"
/* * Static global variables */static DECLARE_WAIT_QUEUE_HEAD(ibmvmc_read_wait);
static const char ibmvmc_driver_name[] = "ibmvmc";
static struct ibmvmc_struct ibmvmc;static struct ibmvmc_hmc hmcs[MAX_HMCS];static struct crq_server_adapter ibmvmc_adapter;
static int ibmvmc_max_buf_pool_size = DEFAULT_BUF_POOL_SIZE;static int ibmvmc_max_hmcs = DEFAULT_HMCS;static int ibmvmc_max_mtu = DEFAULT_MTU;
static inline long h_copy_rdma(s64 length, u64 sliobn, u64 slioba, u64 dliobn, u64 dlioba){ long rc = 0;
/* Ensure all writes to source memory are visible before hcall */ dma_wmb(); pr_debug("ibmvmc: h_copy_rdma(0x%llx, 0x%llx, 0x%llx, 0x%llx, 0x%llx\n", length, sliobn, slioba, dliobn, dlioba); rc = plpar_hcall_norets(H_COPY_RDMA, length, sliobn, slioba, dliobn, dlioba); pr_debug("ibmvmc: h_copy_rdma rc = 0x%lx\n", rc);
return rc;}
static inline void h_free_crq(uint32_t unit_address){ long rc = 0;
do { if (H_IS_LONG_BUSY(rc)) msleep(get_longbusy_msecs(rc));
rc = plpar_hcall_norets(H_FREE_CRQ, unit_address); } while ((rc == H_BUSY) || (H_IS_LONG_BUSY(rc)));}
/** * h_request_vmc: - request a hypervisor virtual management channel device * @vmc_index: drc index of the vmc device created * * Requests the hypervisor create a new virtual management channel device, * allowing this partition to send hypervisor virtualization control * commands. * * Return: * 0 - Success * Non-zero - Failure */static inline long h_request_vmc(u32 *vmc_index){ long rc = 0; unsigned long retbuf[PLPAR_HCALL_BUFSIZE];
do { if (H_IS_LONG_BUSY(rc)) msleep(get_longbusy_msecs(rc));
/* Call to request the VMC device from phyp */ rc = plpar_hcall(H_REQUEST_VMC, retbuf); pr_debug("ibmvmc: %s rc = 0x%lx\n", __func__, rc); *vmc_index = retbuf[0]; } while ((rc == H_BUSY) || (H_IS_LONG_BUSY(rc)));
return rc;}
/* routines for managing a command/response queue *//** * ibmvmc_handle_event: - Interrupt handler for crq events * @irq: number of irq to handle, not used * @dev_instance: crq_server_adapter that received interrupt * * Disables interrupts and schedules ibmvmc_task * * Always returns IRQ_HANDLED */static irqreturn_t ibmvmc_handle_event(int irq, void *dev_instance){ struct crq_server_adapter *adapter = (struct crq_server_adapter *)dev_instance;
vio_disable_interrupts(to_vio_dev(adapter->dev)); tasklet_schedule(&adapter->work_task);
return IRQ_HANDLED;}
/** * ibmvmc_release_crq_queue - Release CRQ Queue * * @adapter: crq_server_adapter struct * * Return: * 0 - Success * Non-Zero - Failure */static void ibmvmc_release_crq_queue(struct crq_server_adapter *adapter){ struct vio_dev *vdev = to_vio_dev(adapter->dev); struct crq_queue *queue = &adapter->queue;
free_irq(vdev->irq, (void *)adapter); tasklet_kill(&adapter->work_task);
if (adapter->reset_task) kthread_stop(adapter->reset_task);
h_free_crq(vdev->unit_address); dma_unmap_single(adapter->dev, queue->msg_token, queue->size * sizeof(*queue->msgs), DMA_BIDIRECTIONAL); kfree(queue->msgs);}
/** * ibmvmc_reset_crq_queue - Reset CRQ Queue * * @adapter: crq_server_adapter struct * * This function calls h_free_crq and then calls H_REG_CRQ and does all the * bookkeeping to get us back to where we can communicate. * * Return: * 0 - Success * Non-Zero - Failure */static int ibmvmc_reset_crq_queue(struct crq_server_adapter *adapter){ struct vio_dev *vdev = to_vio_dev(adapter->dev); struct crq_queue *queue = &adapter->queue; int rc = 0;
/* Close the CRQ */ h_free_crq(vdev->unit_address);
/* Clean out the queue */ memset(queue->msgs, 0x00, PAGE_SIZE); queue->cur = 0;
/* And re-open it again */ rc = plpar_hcall_norets(H_REG_CRQ, vdev->unit_address, queue->msg_token, PAGE_SIZE); if (rc == 2) /* Adapter is good, but other end is not ready */ dev_warn(adapter->dev, "Partner adapter not ready\n"); else if (rc != 0) dev_err(adapter->dev, "couldn't register crq--rc 0x%x\n", rc);
return rc;}
/** * crq_queue_next_crq: - Returns the next entry in message queue * @queue: crq_queue to use * * Returns pointer to next entry in queue, or NULL if there are no new * entried in the CRQ. */static struct ibmvmc_crq_msg *crq_queue_next_crq(struct crq_queue *queue){ struct ibmvmc_crq_msg *crq; unsigned long flags;
spin_lock_irqsave(&queue->lock, flags); crq = &queue->msgs[queue->cur]; if (crq->valid & 0x80) { if (++queue->cur == queue->size) queue->cur = 0;
/* Ensure the read of the valid bit occurs before reading any * other bits of the CRQ entry */ dma_rmb(); } else { crq = NULL; }
spin_unlock_irqrestore(&queue->lock, flags);
return crq;}
/** * ibmvmc_send_crq - Send CRQ * * @adapter: crq_server_adapter struct * @word1: Word1 Data field * @word2: Word2 Data field * * Return: * 0 - Success * Non-Zero - Failure */static long ibmvmc_send_crq(struct crq_server_adapter *adapter, u64 word1, u64 word2){ struct vio_dev *vdev = to_vio_dev(adapter->dev); long rc = 0;
dev_dbg(adapter->dev, "(0x%x, 0x%016llx, 0x%016llx)\n", vdev->unit_address, word1, word2);
/* * Ensure the command buffer is flushed to memory before handing it * over to the other side to prevent it from fetching any stale data. */ dma_wmb(); rc = plpar_hcall_norets(H_SEND_CRQ, vdev->unit_address, word1, word2); dev_dbg(adapter->dev, "rc = 0x%lx\n", rc);
return rc;}
/** * alloc_dma_buffer - Create DMA Buffer * * @vdev: vio_dev struct * @size: Size field * @dma_handle: DMA address field * * Allocates memory for the command queue and maps remote memory into an * ioba. * * Returns a pointer to the buffer */static void *alloc_dma_buffer(struct vio_dev *vdev, size_t size, dma_addr_t *dma_handle){ /* allocate memory */ void *buffer = kzalloc(size, GFP_ATOMIC);
if (!buffer) { *dma_handle = 0; return NULL; }
/* DMA map */ *dma_handle = dma_map_single(&vdev->dev, buffer, size, DMA_BIDIRECTIONAL);
if (dma_mapping_error(&vdev->dev, *dma_handle)) { *dma_handle = 0; kfree_sensitive(buffer); return NULL; }
return buffer;}
/** * free_dma_buffer - Free DMA Buffer * * @vdev: vio_dev struct * @size: Size field * @vaddr: Address field * @dma_handle: DMA address field * * Releases memory for a command queue and unmaps mapped remote memory. */static void free_dma_buffer(struct vio_dev *vdev, size_t size, void *vaddr, dma_addr_t dma_handle){ /* DMA unmap */ dma_unmap_single(&vdev->dev, dma_handle, size, DMA_BIDIRECTIONAL);
/* deallocate memory */ kfree_sensitive(vaddr);}
/** * ibmvmc_get_valid_hmc_buffer - Retrieve Valid HMC Buffer * * @hmc_index: HMC Index Field * * Return: * Pointer to ibmvmc_buffer */static struct ibmvmc_buffer *ibmvmc_get_valid_hmc_buffer(u8 hmc_index){ struct ibmvmc_buffer *buffer; struct ibmvmc_buffer *ret_buf = NULL; unsigned long i;
if (hmc_index > ibmvmc.max_hmc_index) return NULL;
buffer = hmcs[hmc_index].buffer;
for (i = 0; i < ibmvmc_max_buf_pool_size; i++) { if (buffer[i].valid && buffer[i].free && buffer[i].owner == VMC_BUF_OWNER_ALPHA) { buffer[i].free = 0; ret_buf = &buffer[i]; break; } }
return ret_buf;}
/** * ibmvmc_get_free_hmc_buffer - Get Free HMC Buffer * * @adapter: crq_server_adapter struct * @hmc_index: Hmc Index field * * Return: * Pointer to ibmvmc_buffer */static struct ibmvmc_buffer *ibmvmc_get_free_hmc_buffer(struct crq_server_adapter *adapter, u8 hmc_index){ struct ibmvmc_buffer *buffer; struct ibmvmc_buffer *ret_buf = NULL; unsigned long i;
if (hmc_index > ibmvmc.max_hmc_index) { dev_info(adapter->dev, "get_free_hmc_buffer: invalid hmc_index=0x%x\n", hmc_index); return NULL; }
buffer = hmcs[hmc_index].buffer;
for (i = 0; i < ibmvmc_max_buf_pool_size; i++) { if (buffer[i].free && buffer[i].owner == VMC_BUF_OWNER_ALPHA) { buffer[i].free = 0; ret_buf = &buffer[i]; break; } }
return ret_buf;}
/** * ibmvmc_free_hmc_buffer - Free an HMC Buffer * * @hmc: ibmvmc_hmc struct * @buffer: ibmvmc_buffer struct * */static void ibmvmc_free_hmc_buffer(struct ibmvmc_hmc *hmc, struct ibmvmc_buffer *buffer){ unsigned long flags;
spin_lock_irqsave(&hmc->lock, flags); buffer->free = 1; spin_unlock_irqrestore(&hmc->lock, flags);}
/** * ibmvmc_count_hmc_buffers - Count HMC Buffers * * @hmc_index: HMC Index field * @valid: Valid number of buffers field * @free: Free number of buffers field * */static void ibmvmc_count_hmc_buffers(u8 hmc_index, unsigned int *valid, unsigned int *free){ struct ibmvmc_buffer *buffer; unsigned long i; unsigned long flags;
if (hmc_index > ibmvmc.max_hmc_index) return;
if (!valid || !free) return;
*valid = 0; *free = 0;
buffer = hmcs[hmc_index].buffer; spin_lock_irqsave(&hmcs[hmc_index].lock, flags);
for (i = 0; i < ibmvmc_max_buf_pool_size; i++) { if (buffer[i].valid) { *valid = *valid + 1; if (buffer[i].free) *free = *free + 1; } }
spin_unlock_irqrestore(&hmcs[hmc_index].lock, flags);}
/** * ibmvmc_get_free_hmc - Get Free HMC * * Return: * Pointer to an available HMC Connection * Null otherwise */static struct ibmvmc_hmc *ibmvmc_get_free_hmc(void){ unsigned long i; unsigned long flags;
/* * Find an available HMC connection. */ for (i = 0; i <= ibmvmc.max_hmc_index; i++) { spin_lock_irqsave(&hmcs[i].lock, flags); if (hmcs[i].state == ibmhmc_state_free) { hmcs[i].index = i; hmcs[i].state = ibmhmc_state_initial; spin_unlock_irqrestore(&hmcs[i].lock, flags); return &hmcs[i]; } spin_unlock_irqrestore(&hmcs[i].lock, flags); }
return NULL;}
/** * ibmvmc_return_hmc - Return an HMC Connection * * @hmc: ibmvmc_hmc struct * @release_readers: Number of readers connected to session * * This function releases the HMC connections back into the pool. * * Return: * 0 - Success * Non-zero - Failure */static int ibmvmc_return_hmc(struct ibmvmc_hmc *hmc, bool release_readers){ struct ibmvmc_buffer *buffer; struct crq_server_adapter *adapter; struct vio_dev *vdev; unsigned long i; unsigned long flags;
if (!hmc || !hmc->adapter) return -EIO;
if (release_readers) { if (hmc->file_session) { struct ibmvmc_file_session *session = hmc->file_session;
session->valid = 0; wake_up_interruptible(&ibmvmc_read_wait); } }
adapter = hmc->adapter; vdev = to_vio_dev(adapter->dev);
spin_lock_irqsave(&hmc->lock, flags); hmc->index = 0; hmc->state = ibmhmc_state_free; hmc->queue_head = 0; hmc->queue_tail = 0; buffer = hmc->buffer; for (i = 0; i < ibmvmc_max_buf_pool_size; i++) { if (buffer[i].valid) { free_dma_buffer(vdev, ibmvmc.max_mtu, buffer[i].real_addr_local, buffer[i].dma_addr_local); dev_dbg(adapter->dev, "Forgot buffer id 0x%lx\n", i); } memset(&buffer[i], 0, sizeof(struct ibmvmc_buffer));
hmc->queue_outbound_msgs[i] = VMC_INVALID_BUFFER_ID; }
spin_unlock_irqrestore(&hmc->lock, flags);
return 0;}
/** * ibmvmc_send_open - Interface Open * @buffer: Pointer to ibmvmc_buffer struct * @hmc: Pointer to ibmvmc_hmc struct * * This command is sent by the management partition as the result of a * management partition device request. It causes the hypervisor to * prepare a set of data buffers for the management application connection * indicated HMC idx. A unique HMC Idx would be used if multiple management * applications running concurrently were desired. Before responding to this * command, the hypervisor must provide the management partition with at * least one of these new buffers via the Add Buffer. This indicates whether * the messages are inbound or outbound from the hypervisor. * * Return: * 0 - Success * Non-zero - Failure */static int ibmvmc_send_open(struct ibmvmc_buffer *buffer, struct ibmvmc_hmc *hmc){ struct ibmvmc_crq_msg crq_msg; struct crq_server_adapter *adapter; __be64 *crq_as_u64 = (__be64 *)&crq_msg; int rc = 0;
if (!hmc || !hmc->adapter) return -EIO;
adapter = hmc->adapter;
dev_dbg(adapter->dev, "send_open: 0x%lx 0x%lx 0x%lx 0x%lx 0x%lx\n", (unsigned long)buffer->size, (unsigned long)adapter->liobn, (unsigned long)buffer->dma_addr_local, (unsigned long)adapter->riobn, (unsigned long)buffer->dma_addr_remote);
rc = h_copy_rdma(buffer->size, adapter->liobn, buffer->dma_addr_local, adapter->riobn, buffer->dma_addr_remote); if (rc) { dev_err(adapter->dev, "Error: In send_open, h_copy_rdma rc 0x%x\n", rc); return -EIO; }
hmc->state = ibmhmc_state_opening;
crq_msg.valid = 0x80; crq_msg.type = VMC_MSG_OPEN; crq_msg.status = 0; crq_msg.var1.rsvd = 0; crq_msg.hmc_session = hmc->session; crq_msg.hmc_index = hmc->index; crq_msg.var2.buffer_id = cpu_to_be16(buffer->id); crq_msg.rsvd = 0; crq_msg.var3.rsvd = 0;
ibmvmc_send_crq(adapter, be64_to_cpu(crq_as_u64[0]), be64_to_cpu(crq_as_u64[1]));
return rc;}
/** * ibmvmc_send_close - Interface Close * @hmc: Pointer to ibmvmc_hmc struct * * This command is sent by the management partition to terminate a * management application to hypervisor connection. When this command is * sent, the management partition has quiesced all I/O operations to all * buffers associated with this management application connection, and * has freed any storage for these buffers. * * Return: * 0 - Success * Non-zero - Failure */static int ibmvmc_send_close(struct ibmvmc_hmc *hmc){ struct ibmvmc_crq_msg crq_msg; struct crq_server_adapter *adapter; __be64 *crq_as_u64 = (__be64 *)&crq_msg; int rc = 0;
if (!hmc || !hmc->adapter) return -EIO;
adapter = hmc->adapter;
dev_info(adapter->dev, "CRQ send: close\n");
crq_msg.valid = 0x80; crq_msg.type = VMC_MSG_CLOSE; crq_msg.status = 0; crq_msg.var1.rsvd = 0; crq_msg.hmc_session = hmc->session; crq_msg.hmc_index = hmc->index; crq_msg.var2.rsvd = 0; crq_msg.rsvd = 0; crq_msg.var3.rsvd = 0;
ibmvmc_send_crq(adapter, be64_to_cpu(crq_as_u64[0]), be64_to_cpu(crq_as_u64[1]));
return rc;}
/** * ibmvmc_send_capabilities - Send VMC Capabilities * * @adapter: crq_server_adapter struct * * The capabilities message is an administrative message sent after the CRQ * initialization sequence of messages and is used to exchange VMC capabilities * between the management partition and the hypervisor. The management * partition must send this message and the hypervisor must respond with VMC * capabilities Response message before HMC interface message can begin. Any * HMC interface messages received before the exchange of capabilities has * complete are dropped. * * Return: * 0 - Success */static int ibmvmc_send_capabilities(struct crq_server_adapter *adapter){ struct ibmvmc_admin_crq_msg crq_msg; __be64 *crq_as_u64 = (__be64 *)&crq_msg;
dev_dbg(adapter->dev, "ibmvmc: CRQ send: capabilities\n"); crq_msg.valid = 0x80; crq_msg.type = VMC_MSG_CAP; crq_msg.status = 0; crq_msg.rsvd[0] = 0; crq_msg.rsvd[1] = 0; crq_msg.max_hmc = ibmvmc_max_hmcs; crq_msg.max_mtu = cpu_to_be32(ibmvmc_max_mtu); crq_msg.pool_size = cpu_to_be16(ibmvmc_max_buf_pool_size); crq_msg.crq_size = cpu_to_be16(adapter->queue.size); crq_msg.version = cpu_to_be16(IBMVMC_PROTOCOL_VERSION);
ibmvmc_send_crq(adapter, be64_to_cpu(crq_as_u64[0]), be64_to_cpu(crq_as_u64[1]));
ibmvmc.state = ibmvmc_state_capabilities;
return 0;}
/** * ibmvmc_send_add_buffer_resp - Add Buffer Response * * @adapter: crq_server_adapter struct * @status: Status field * @hmc_session: HMC Session field * @hmc_index: HMC Index field * @buffer_id: Buffer Id field * * This command is sent by the management partition to the hypervisor in * response to the Add Buffer message. The Status field indicates the result of * the command. * * Return: * 0 - Success */static int ibmvmc_send_add_buffer_resp(struct crq_server_adapter *adapter, u8 status, u8 hmc_session, u8 hmc_index, u16 buffer_id){ struct ibmvmc_crq_msg crq_msg; __be64 *crq_as_u64 = (__be64 *)&crq_msg;
dev_dbg(adapter->dev, "CRQ send: add_buffer_resp\n"); crq_msg.valid = 0x80; crq_msg.type = VMC_MSG_ADD_BUF_RESP; crq_msg.status = status; crq_msg.var1.rsvd = 0; crq_msg.hmc_session = hmc_session; crq_msg.hmc_index = hmc_index; crq_msg.var2.buffer_id = cpu_to_be16(buffer_id); crq_msg.rsvd = 0; crq_msg.var3.rsvd = 0;
ibmvmc_send_crq(adapter, be64_to_cpu(crq_as_u64[0]), be64_to_cpu(crq_as_u64[1]));
return 0;}
/** * ibmvmc_send_rem_buffer_resp - Remove Buffer Response * * @adapter: crq_server_adapter struct * @status: Status field * @hmc_session: HMC Session field * @hmc_index: HMC Index field * @buffer_id: Buffer Id field * * This command is sent by the management partition to the hypervisor in * response to the Remove Buffer message. The Buffer ID field indicates * which buffer the management partition selected to remove. The Status * field indicates the result of the command. * * Return: * 0 - Success */static int ibmvmc_send_rem_buffer_resp(struct crq_server_adapter *adapter, u8 status, u8 hmc_session, u8 hmc_index, u16 buffer_id){ struct ibmvmc_crq_msg crq_msg; __be64 *crq_as_u64 = (__be64 *)&crq_msg;
dev_dbg(adapter->dev, "CRQ send: rem_buffer_resp\n"); crq_msg.valid = 0x80; crq_msg.type = VMC_MSG_REM_BUF_RESP; crq_msg.status = status; crq_msg.var1.rsvd = 0; crq_msg.hmc_session = hmc_session; crq_msg.hmc_index = hmc_index; crq_msg.var2.buffer_id = cpu_to_be16(buffer_id); crq_msg.rsvd = 0; crq_msg.var3.rsvd = 0;
ibmvmc_send_crq(adapter, be64_to_cpu(crq_as_u64[0]), be64_to_cpu(crq_as_u64[1]));
return 0;}
/** * ibmvmc_send_msg - Signal Message * * @adapter: crq_server_adapter struct * @buffer: ibmvmc_buffer struct * @hmc: ibmvmc_hmc struct * @msg_len: message length field * * This command is sent between the management partition and the hypervisor * in order to signal the arrival of an HMC protocol message. The command * can be sent by both the management partition and the hypervisor. It is * used for all traffic between the management application and the hypervisor, * regardless of who initiated the communication. * * There is no response to this message. * * Return: * 0 - Success * Non-zero - Failure */static int ibmvmc_send_msg(struct crq_server_adapter *adapter, struct ibmvmc_buffer *buffer, struct ibmvmc_hmc *hmc, int msg_len){ struct ibmvmc_crq_msg crq_msg; __be64 *crq_as_u64 = (__be64 *)&crq_msg; int rc = 0;
dev_dbg(adapter->dev, "CRQ send: rdma to HV\n"); rc = h_copy_rdma(msg_len, adapter->liobn, buffer->dma_addr_local, adapter->riobn, buffer->dma_addr_remote); if (rc) { dev_err(adapter->dev, "Error in send_msg, h_copy_rdma rc 0x%x\n", rc); return rc; }
crq_msg.valid = 0x80; crq_msg.type = VMC_MSG_SIGNAL; crq_msg.status = 0; crq_msg.var1.rsvd = 0; crq_msg.hmc_session = hmc->session; crq_msg.hmc_index = hmc->index; crq_msg.var2.buffer_id = cpu_to_be16(buffer->id); crq_msg.var3.msg_len = cpu_to_be32(msg_len); dev_dbg(adapter->dev, "CRQ send: msg to HV 0x%llx 0x%llx\n", be64_to_cpu(crq_as_u64[0]), be64_to_cpu(crq_as_u64[1]));
buffer->owner = VMC_BUF_OWNER_HV; ibmvmc_send_crq(adapter, be64_to_cpu(crq_as_u64[0]), be64_to_cpu(crq_as_u64[1]));
return rc;}
/** * ibmvmc_open - Open Session * * @inode: inode struct * @file: file struct * * Return: * 0 - Success * Non-zero - Failure */static int ibmvmc_open(struct inode *inode, struct file *file){ struct ibmvmc_file_session *session;
pr_debug("%s: inode = 0x%lx, file = 0x%lx, state = 0x%x\n", __func__, (unsigned long)inode, (unsigned long)file, ibmvmc.state);
session = kzalloc_obj(*session); if (!session) return -ENOMEM;
session->file = file; file->private_data = session;
return 0;}
/** * ibmvmc_close - Close Session * * @inode: inode struct * @file: file struct * * Return: * 0 - Success * Non-zero - Failure */static int ibmvmc_close(struct inode *inode, struct file *file){ struct ibmvmc_file_session *session; struct ibmvmc_hmc *hmc; int rc = 0; unsigned long flags;
pr_debug("%s: file = 0x%lx, state = 0x%x\n", __func__, (unsigned long)file, ibmvmc.state);
session = file->private_data; if (!session) return -EIO;
hmc = session->hmc; if (hmc) { if (!hmc->adapter) return -EIO;
if (ibmvmc.state == ibmvmc_state_failed) { dev_warn(hmc->adapter->dev, "close: state_failed\n"); return -EIO; }
spin_lock_irqsave(&hmc->lock, flags); if (hmc->state >= ibmhmc_state_opening) { rc = ibmvmc_send_close(hmc); if (rc) dev_warn(hmc->adapter->dev, "close: send_close failed.\n"); } spin_unlock_irqrestore(&hmc->lock, flags); }
kfree_sensitive(session);
return rc;}
/** * ibmvmc_read - Read * * @file: file struct * @buf: Character buffer * @nbytes: Size in bytes * @ppos: Offset * * Return: * 0 - Success * Non-zero - Failure */static ssize_t ibmvmc_read(struct file *file, char *buf, size_t nbytes, loff_t *ppos){ struct ibmvmc_file_session *session; struct ibmvmc_hmc *hmc; struct crq_server_adapter *adapter; struct ibmvmc_buffer *buffer; ssize_t n; ssize_t retval = 0; unsigned long flags; DEFINE_WAIT(wait);
pr_debug("ibmvmc: read: file = 0x%lx, buf = 0x%lx, nbytes = 0x%lx\n", (unsigned long)file, (unsigned long)buf, (unsigned long)nbytes);
if (nbytes == 0) return 0;
if (nbytes > ibmvmc.max_mtu) { pr_warn("ibmvmc: read: nbytes invalid 0x%x\n", (unsigned int)nbytes); return -EINVAL; }
session = file->private_data; if (!session) { pr_warn("ibmvmc: read: no session\n"); return -EIO; }
hmc = session->hmc; if (!hmc) { pr_warn("ibmvmc: read: no hmc\n"); return -EIO; }
adapter = hmc->adapter; if (!adapter) { pr_warn("ibmvmc: read: no adapter\n"); return -EIO; }
do { prepare_to_wait(&ibmvmc_read_wait, &wait, TASK_INTERRUPTIBLE);
spin_lock_irqsave(&hmc->lock, flags); if (hmc->queue_tail != hmc->queue_head) /* Data is available */ break;
spin_unlock_irqrestore(&hmc->lock, flags);
if (!session->valid) { retval = -EBADFD; goto out; } if (file->f_flags & O_NONBLOCK) { retval = -EAGAIN; goto out; }
schedule();
if (signal_pending(current)) { retval = -ERESTARTSYS; goto out; } } while (1);
buffer = &(hmc->buffer[hmc->queue_outbound_msgs[hmc->queue_tail]]); hmc->queue_tail++; if (hmc->queue_tail == ibmvmc_max_buf_pool_size) hmc->queue_tail = 0; spin_unlock_irqrestore(&hmc->lock, flags);
nbytes = min_t(size_t, nbytes, buffer->msg_len); n = copy_to_user((void *)buf, buffer->real_addr_local, nbytes); dev_dbg(adapter->dev, "read: copy to user nbytes = 0x%lx.\n", nbytes); ibmvmc_free_hmc_buffer(hmc, buffer); retval = nbytes;
if (n) { dev_warn(adapter->dev, "read: copy to user failed.\n"); retval = -EFAULT; }
out: finish_wait(&ibmvmc_read_wait, &wait); dev_dbg(adapter->dev, "read: out %ld\n", retval); return retval;}
/** * ibmvmc_poll - Poll * * @file: file struct * @wait: Poll Table * * Return: * poll.h return values */static unsigned int ibmvmc_poll(struct file *file, poll_table *wait){ struct ibmvmc_file_session *session; struct ibmvmc_hmc *hmc; unsigned int mask = 0;
session = file->private_data; if (!session) return 0;
hmc = session->hmc; if (!hmc) return 0;
poll_wait(file, &ibmvmc_read_wait, wait);
if (hmc->queue_head != hmc->queue_tail) mask |= POLLIN | POLLRDNORM;
return mask;}
/** * ibmvmc_write - Write * * @file: file struct * @buffer: Character buffer * @count: Count field * @ppos: Offset * * Return: * 0 - Success * Non-zero - Failure */static ssize_t ibmvmc_write(struct file *file, const char *buffer, size_t count, loff_t *ppos){ struct inode *inode; struct ibmvmc_buffer *vmc_buffer = NULL; struct ibmvmc_file_session *session; struct crq_server_adapter *adapter; struct ibmvmc_hmc *hmc; unsigned char *buf; unsigned long flags; size_t bytes; const char *p = buffer; size_t c = count; int ret = 0;
session = file->private_data; if (!session) return -EIO;
hmc = session->hmc; if (!hmc) return -EIO;
spin_lock_irqsave(&hmc->lock, flags); if (hmc->state == ibmhmc_state_free) { /* HMC connection is not valid (possibly was reset under us). */ ret = -EIO; goto out; }
adapter = hmc->adapter; if (!adapter) { ret = -EIO; goto out; }
if (count > ibmvmc.max_mtu) { dev_warn(adapter->dev, "invalid buffer size 0x%lx\n", (unsigned long)count); ret = -EIO; goto out; }
/* Waiting for the open resp message to the ioctl(1) - retry */ if (hmc->state == ibmhmc_state_opening) { ret = -EBUSY; goto out; }
/* Make sure the ioctl() was called & the open msg sent, and that * the HMC connection has not failed. */ if (hmc->state != ibmhmc_state_ready) { ret = -EIO; goto out; }
vmc_buffer = ibmvmc_get_valid_hmc_buffer(hmc->index); if (!vmc_buffer) { /* No buffer available for the msg send, or we have not yet * completed the open/open_resp sequence. Retry until this is * complete. */ ret = -EBUSY; goto out; } if (!vmc_buffer->real_addr_local) { dev_err(adapter->dev, "no buffer storage assigned\n"); ret = -EIO; goto out; } buf = vmc_buffer->real_addr_local;
while (c > 0) { bytes = min_t(size_t, c, vmc_buffer->size);
bytes -= copy_from_user(buf, p, bytes); if (!bytes) { ret = -EFAULT; goto out; } c -= bytes; p += bytes; } if (p == buffer) goto out;
inode = file_inode(file); inode_set_mtime_to_ts(inode, inode_set_ctime_current(inode)); mark_inode_dirty(inode);
dev_dbg(adapter->dev, "write: file = 0x%lx, count = 0x%lx\n", (unsigned long)file, (unsigned long)count);
if (ibmvmc_send_msg(adapter, vmc_buffer, hmc, count)) { ret = -EIO; goto out; } vmc_buffer = NULL; ret = p - buffer; out: if (vmc_buffer) vmc_buffer->free = 1; spin_unlock_irqrestore(&hmc->lock, flags); return (ssize_t)(ret);}
/** * ibmvmc_setup_hmc - Setup the HMC * * @session: ibmvmc_file_session struct * * Return: * 0 - Success * Non-zero - Failure */static long ibmvmc_setup_hmc(struct ibmvmc_file_session *session){ struct ibmvmc_hmc *hmc; unsigned int valid, free, index;
if (ibmvmc.state == ibmvmc_state_failed) { pr_warn("ibmvmc: Reserve HMC: state_failed\n"); return -EIO; }
if (ibmvmc.state < ibmvmc_state_ready) { pr_warn("ibmvmc: Reserve HMC: not state_ready\n"); return -EAGAIN; }
/* Device is busy until capabilities have been exchanged and we * have a generic buffer for each possible HMC connection. */ for (index = 0; index <= ibmvmc.max_hmc_index; index++) { valid = 0; ibmvmc_count_hmc_buffers(index, &valid, &free); if (valid == 0) { pr_warn("ibmvmc: buffers not ready for index %d\n", index); return -ENOBUFS; } }
/* Get an hmc object, and transition to ibmhmc_state_initial */ hmc = ibmvmc_get_free_hmc(); if (!hmc) { pr_warn("%s: free hmc not found\n", __func__); return -EBUSY; }
hmc->session = hmc->session + 1; if (hmc->session == 0xff) hmc->session = 1;
session->hmc = hmc; hmc->adapter = &ibmvmc_adapter; hmc->file_session = session; session->valid = 1;
return 0;}
/** * ibmvmc_ioctl_sethmcid - IOCTL Set HMC ID * * @session: ibmvmc_file_session struct * @new_hmc_id: HMC id field * * IOCTL command to setup the hmc id * * Return: * 0 - Success * Non-zero - Failure */static long ibmvmc_ioctl_sethmcid(struct ibmvmc_file_session *session, unsigned char __user *new_hmc_id){ struct ibmvmc_hmc *hmc; struct ibmvmc_buffer *buffer; size_t bytes; char print_buffer[HMC_ID_LEN + 1]; unsigned long flags; long rc = 0;
/* Reserve HMC session */ hmc = session->hmc; if (!hmc) { rc = ibmvmc_setup_hmc(session); if (rc) return rc;
hmc = session->hmc; if (!hmc) { pr_err("ibmvmc: setup_hmc success but no hmc\n"); return -EIO; } }
if (hmc->state != ibmhmc_state_initial) { pr_warn("ibmvmc: sethmcid: invalid state to send open 0x%x\n", hmc->state); return -EIO; }
bytes = copy_from_user(hmc->hmc_id, new_hmc_id, HMC_ID_LEN); if (bytes) return -EFAULT;
/* Send Open Session command */ spin_lock_irqsave(&hmc->lock, flags); buffer = ibmvmc_get_valid_hmc_buffer(hmc->index); spin_unlock_irqrestore(&hmc->lock, flags);
if (!buffer || !buffer->real_addr_local) { pr_warn("ibmvmc: sethmcid: no buffer available\n"); return -EIO; }
strscpy(print_buffer, hmc->hmc_id, sizeof(print_buffer)); pr_info("ibmvmc: sethmcid: Set HMC ID: \"%s\"\n", print_buffer);
memcpy(buffer->real_addr_local, hmc->hmc_id, HMC_ID_LEN); /* RDMA over ID, send open msg, change state to ibmhmc_state_opening */ rc = ibmvmc_send_open(buffer, hmc);
return rc;}
/** * ibmvmc_ioctl_query - IOCTL Query * * @session: ibmvmc_file_session struct * @ret_struct: ibmvmc_query_struct * * Return: * 0 - Success * Non-zero - Failure */static long ibmvmc_ioctl_query(struct ibmvmc_file_session *session, struct ibmvmc_query_struct __user *ret_struct){ struct ibmvmc_query_struct query_struct; size_t bytes;
memset(&query_struct, 0, sizeof(query_struct)); query_struct.have_vmc = (ibmvmc.state > ibmvmc_state_initial); query_struct.state = ibmvmc.state; query_struct.vmc_drc_index = ibmvmc.vmc_drc_index;
bytes = copy_to_user(ret_struct, &query_struct, sizeof(query_struct)); if (bytes) return -EFAULT;
return 0;}
/** * ibmvmc_ioctl_requestvmc - IOCTL Request VMC * * @session: ibmvmc_file_session struct * @ret_vmc_index: VMC Index * * Return: * 0 - Success * Non-zero - Failure */static long ibmvmc_ioctl_requestvmc(struct ibmvmc_file_session *session, u32 __user *ret_vmc_index){ /* TODO: (adreznec) Add locking to control multiple process access */ size_t bytes; long rc; u32 vmc_drc_index;
/* Call to request the VMC device from phyp*/ rc = h_request_vmc(&vmc_drc_index); pr_debug("ibmvmc: requestvmc: H_REQUEST_VMC rc = 0x%lx\n", rc);
if (rc == H_SUCCESS) { rc = 0; } else if (rc == H_FUNCTION) { pr_err("ibmvmc: requestvmc: h_request_vmc not supported\n"); return -EPERM; } else if (rc == H_AUTHORITY) { pr_err("ibmvmc: requestvmc: hypervisor denied vmc request\n"); return -EPERM; } else if (rc == H_HARDWARE) { pr_err("ibmvmc: requestvmc: hypervisor hardware fault\n"); return -EIO; } else if (rc == H_RESOURCE) { pr_err("ibmvmc: requestvmc: vmc resource unavailable\n"); return -ENODEV; } else if (rc == H_NOT_AVAILABLE) { pr_err("ibmvmc: requestvmc: system cannot be vmc managed\n"); return -EPERM; } else if (rc == H_PARAMETER) { pr_err("ibmvmc: requestvmc: invalid parameter\n"); return -EINVAL; }
/* Success, set the vmc index in global struct */ ibmvmc.vmc_drc_index = vmc_drc_index;
bytes = copy_to_user(ret_vmc_index, &vmc_drc_index, sizeof(*ret_vmc_index)); if (bytes) { pr_warn("ibmvmc: requestvmc: copy to user failed.\n"); return -EFAULT; } return rc;}
/** * ibmvmc_ioctl - IOCTL * * @file: file information * @cmd: cmd field * @arg: Argument field * * Return: * 0 - Success * Non-zero - Failure */static long ibmvmc_ioctl(struct file *file, unsigned int cmd, unsigned long arg){ struct ibmvmc_file_session *session = file->private_data;
pr_debug("ibmvmc: ioctl file=0x%lx, cmd=0x%x, arg=0x%lx, ses=0x%lx\n", (unsigned long)file, cmd, arg, (unsigned long)session);
if (!session) { pr_warn("ibmvmc: ioctl: no session\n"); return -EIO; }
switch (cmd) { case VMC_IOCTL_SETHMCID: return ibmvmc_ioctl_sethmcid(session, (unsigned char __user *)arg); case VMC_IOCTL_QUERY: return ibmvmc_ioctl_query(session, (struct ibmvmc_query_struct __user *)arg); case VMC_IOCTL_REQUESTVMC: return ibmvmc_ioctl_requestvmc(session, (unsigned int __user *)arg); default: pr_warn("ibmvmc: unknown ioctl 0x%x\n", cmd); return -EINVAL; }}
static const struct file_operations ibmvmc_fops = { .owner = THIS_MODULE, .read = ibmvmc_read, .write = ibmvmc_write, .poll = ibmvmc_poll, .unlocked_ioctl = ibmvmc_ioctl, .open = ibmvmc_open, .release = ibmvmc_close,};
/** * ibmvmc_add_buffer - Add Buffer * * @adapter: crq_server_adapter struct * @crq: ibmvmc_crq_msg struct * * This message transfers a buffer from hypervisor ownership to management * partition ownership. The LIOBA is obtained from the virtual TCE table * associated with the hypervisor side of the VMC device, and points to a * buffer of size MTU (as established in the capabilities exchange). * * Typical flow for ading buffers: * 1. A new management application connection is opened by the management * partition. * 2. The hypervisor assigns new buffers for the traffic associated with * that connection. * 3. The hypervisor sends VMC Add Buffer messages to the management * partition, informing it of the new buffers. * 4. The hypervisor sends an HMC protocol message (to the management * application) notifying it of the new buffers. This informs the * application that it has buffers available for sending HMC * commands. * * Return: * 0 - Success * Non-zero - Failure */static int ibmvmc_add_buffer(struct crq_server_adapter *adapter, struct ibmvmc_crq_msg *crq){ struct ibmvmc_buffer *buffer; u8 hmc_index; u8 hmc_session; u16 buffer_id; unsigned long flags; int rc = 0;
if (!crq) return -1;
hmc_session = crq->hmc_session; hmc_index = crq->hmc_index; buffer_id = be16_to_cpu(crq->var2.buffer_id);
if (hmc_index > ibmvmc.max_hmc_index) { dev_err(adapter->dev, "add_buffer: invalid hmc_index = 0x%x\n", hmc_index); ibmvmc_send_add_buffer_resp(adapter, VMC_MSG_INVALID_HMC_INDEX, hmc_session, hmc_index, buffer_id); return -1; }
if (buffer_id >= ibmvmc.max_buffer_pool_size) { dev_err(adapter->dev, "add_buffer: invalid buffer_id = 0x%x\n", buffer_id); ibmvmc_send_add_buffer_resp(adapter, VMC_MSG_INVALID_BUFFER_ID, hmc_session, hmc_index, buffer_id); return -1; }
spin_lock_irqsave(&hmcs[hmc_index].lock, flags); buffer = &hmcs[hmc_index].buffer[buffer_id];
if (buffer->real_addr_local || buffer->dma_addr_local) { dev_warn(adapter->dev, "add_buffer: already allocated id = 0x%lx\n", (unsigned long)buffer_id); spin_unlock_irqrestore(&hmcs[hmc_index].lock, flags); ibmvmc_send_add_buffer_resp(adapter, VMC_MSG_INVALID_BUFFER_ID, hmc_session, hmc_index, buffer_id); return -1; }
buffer->real_addr_local = alloc_dma_buffer(to_vio_dev(adapter->dev), ibmvmc.max_mtu, &buffer->dma_addr_local);
if (!buffer->real_addr_local) { dev_err(adapter->dev, "add_buffer: alloc_dma_buffer failed.\n"); spin_unlock_irqrestore(&hmcs[hmc_index].lock, flags); ibmvmc_send_add_buffer_resp(adapter, VMC_MSG_INTERFACE_FAILURE, hmc_session, hmc_index, buffer_id); return -1; }
buffer->dma_addr_remote = be32_to_cpu(crq->var3.lioba); buffer->size = ibmvmc.max_mtu; buffer->owner = crq->var1.owner; buffer->free = 1; /* Must ensure valid==1 is observable only after all other fields are */ dma_wmb(); buffer->valid = 1; buffer->id = buffer_id;
dev_dbg(adapter->dev, "add_buffer: successfully added a buffer:\n"); dev_dbg(adapter->dev, " index: %d, session: %d, buffer: 0x%x, owner: %d\n", hmc_index, hmc_session, buffer_id, buffer->owner); dev_dbg(adapter->dev, " local: 0x%x, remote: 0x%x\n", (u32)buffer->dma_addr_local, (u32)buffer->dma_addr_remote); spin_unlock_irqrestore(&hmcs[hmc_index].lock, flags);
ibmvmc_send_add_buffer_resp(adapter, VMC_MSG_SUCCESS, hmc_session, hmc_index, buffer_id);
return rc;}
/** * ibmvmc_rem_buffer - Remove Buffer * * @adapter: crq_server_adapter struct * @crq: ibmvmc_crq_msg struct * * This message requests an HMC buffer to be transferred from management * partition ownership to hypervisor ownership. The management partition may * not be able to satisfy the request at a particular point in time if all its * buffers are in use. The management partition requires a depth of at least * one inbound buffer to allow management application commands to flow to the * hypervisor. It is, therefore, an interface error for the hypervisor to * attempt to remove the management partition's last buffer. * * The hypervisor is expected to manage buffer usage with the management * application directly and inform the management partition when buffers may be * removed. The typical flow for removing buffers: * * 1. The management application no longer needs a communication path to a * particular hypervisor function. That function is closed. * 2. The hypervisor and the management application quiesce all traffic to that * function. The hypervisor requests a reduction in buffer pool size. * 3. The management application acknowledges the reduction in buffer pool size. * 4. The hypervisor sends a Remove Buffer message to the management partition, * informing it of the reduction in buffers. * 5. The management partition verifies it can remove the buffer. This is * possible if buffers have been quiesced. * * Return: * 0 - Success * Non-zero - Failure *//* * The hypervisor requested that we pick an unused buffer, and return it. * Before sending the buffer back, we free any storage associated with the * buffer. */static int ibmvmc_rem_buffer(struct crq_server_adapter *adapter, struct ibmvmc_crq_msg *crq){ struct ibmvmc_buffer *buffer; u8 hmc_index; u8 hmc_session; u16 buffer_id = 0; unsigned long flags; int rc = 0;
if (!crq) return -1;
hmc_session = crq->hmc_session; hmc_index = crq->hmc_index;
if (hmc_index > ibmvmc.max_hmc_index) { dev_warn(adapter->dev, "rem_buffer: invalid hmc_index = 0x%x\n", hmc_index); ibmvmc_send_rem_buffer_resp(adapter, VMC_MSG_INVALID_HMC_INDEX, hmc_session, hmc_index, buffer_id); return -1; }
spin_lock_irqsave(&hmcs[hmc_index].lock, flags); buffer = ibmvmc_get_free_hmc_buffer(adapter, hmc_index); if (!buffer) { dev_info(adapter->dev, "rem_buffer: no buffer to remove\n"); spin_unlock_irqrestore(&hmcs[hmc_index].lock, flags); ibmvmc_send_rem_buffer_resp(adapter, VMC_MSG_NO_BUFFER, hmc_session, hmc_index, VMC_INVALID_BUFFER_ID); return -1; }
buffer_id = buffer->id;
if (buffer->valid) free_dma_buffer(to_vio_dev(adapter->dev), ibmvmc.max_mtu, buffer->real_addr_local, buffer->dma_addr_local);
memset(buffer, 0, sizeof(struct ibmvmc_buffer)); spin_unlock_irqrestore(&hmcs[hmc_index].lock, flags);
dev_dbg(adapter->dev, "rem_buffer: removed buffer 0x%x.\n", buffer_id); ibmvmc_send_rem_buffer_resp(adapter, VMC_MSG_SUCCESS, hmc_session, hmc_index, buffer_id);
return rc;}
static int ibmvmc_recv_msg(struct crq_server_adapter *adapter, struct ibmvmc_crq_msg *crq){ struct ibmvmc_buffer *buffer; struct ibmvmc_hmc *hmc; unsigned long msg_len; u8 hmc_index; u8 hmc_session; u16 buffer_id; unsigned long flags; int rc = 0;
if (!crq) return -1;
/* Hypervisor writes CRQs directly into our memory in big endian */ dev_dbg(adapter->dev, "Recv_msg: msg from HV 0x%016llx 0x%016llx\n", be64_to_cpu(*((unsigned long *)crq)), be64_to_cpu(*(((unsigned long *)crq) + 1)));
hmc_session = crq->hmc_session; hmc_index = crq->hmc_index; buffer_id = be16_to_cpu(crq->var2.buffer_id); msg_len = be32_to_cpu(crq->var3.msg_len);
if (hmc_index > ibmvmc.max_hmc_index) { dev_err(adapter->dev, "Recv_msg: invalid hmc_index = 0x%x\n", hmc_index); ibmvmc_send_add_buffer_resp(adapter, VMC_MSG_INVALID_HMC_INDEX, hmc_session, hmc_index, buffer_id); return -1; }
if (buffer_id >= ibmvmc.max_buffer_pool_size) { dev_err(adapter->dev, "Recv_msg: invalid buffer_id = 0x%x\n", buffer_id); ibmvmc_send_add_buffer_resp(adapter, VMC_MSG_INVALID_BUFFER_ID, hmc_session, hmc_index, buffer_id); return -1; }
hmc = &hmcs[hmc_index]; spin_lock_irqsave(&hmc->lock, flags);
if (hmc->state == ibmhmc_state_free) { dev_err(adapter->dev, "Recv_msg: invalid hmc state = 0x%x\n", hmc->state); /* HMC connection is not valid (possibly was reset under us). */ spin_unlock_irqrestore(&hmc->lock, flags); return -1; }
buffer = &hmc->buffer[buffer_id];
if (buffer->valid == 0 || buffer->owner == VMC_BUF_OWNER_ALPHA) { dev_err(adapter->dev, "Recv_msg: not valid, or not HV. 0x%x 0x%x\n", buffer->valid, buffer->owner); spin_unlock_irqrestore(&hmc->lock, flags); return -1; }
if (msg_len > buffer->size) { dev_err(adapter->dev, "Recv_msg: msg_len 0x%lx exceeds buffer size 0x%x\n", msg_len, buffer->size); spin_unlock_irqrestore(&hmc->lock, flags); return -1; }
/* RDMA the data into the partition. */ rc = h_copy_rdma(msg_len, adapter->riobn, buffer->dma_addr_remote, adapter->liobn, buffer->dma_addr_local);
dev_dbg(adapter->dev, "Recv_msg: msg_len = 0x%x, buffer_id = 0x%x, queue_head = 0x%x, hmc_idx = 0x%x\n", (unsigned int)msg_len, (unsigned int)buffer_id, (unsigned int)hmc->queue_head, (unsigned int)hmc_index); buffer->msg_len = msg_len; buffer->free = 0; buffer->owner = VMC_BUF_OWNER_ALPHA;
if (rc) { dev_err(adapter->dev, "Failure in recv_msg: h_copy_rdma = 0x%x\n", rc); spin_unlock_irqrestore(&hmc->lock, flags); return -1; }
/* Must be locked because read operates on the same data */ hmc->queue_outbound_msgs[hmc->queue_head] = buffer_id; hmc->queue_head++; if (hmc->queue_head == ibmvmc_max_buf_pool_size) hmc->queue_head = 0;
if (hmc->queue_head == hmc->queue_tail) dev_err(adapter->dev, "outbound buffer queue wrapped.\n");
spin_unlock_irqrestore(&hmc->lock, flags);
wake_up_interruptible(&ibmvmc_read_wait);
return 0;}
/** * ibmvmc_process_capabilities - Process Capabilities * * @adapter: crq_server_adapter struct * @crqp: ibmvmc_crq_msg struct * */static void ibmvmc_process_capabilities(struct crq_server_adapter *adapter, struct ibmvmc_crq_msg *crqp){ struct ibmvmc_admin_crq_msg *crq = (struct ibmvmc_admin_crq_msg *)crqp;
if ((be16_to_cpu(crq->version) >> 8) != (IBMVMC_PROTOCOL_VERSION >> 8)) { dev_err(adapter->dev, "init failed, incompatible versions 0x%x 0x%x\n", be16_to_cpu(crq->version), IBMVMC_PROTOCOL_VERSION); ibmvmc.state = ibmvmc_state_failed; return; }
ibmvmc.max_mtu = min_t(u32, ibmvmc_max_mtu, be32_to_cpu(crq->max_mtu)); ibmvmc.max_buffer_pool_size = min_t(u16, ibmvmc_max_buf_pool_size, be16_to_cpu(crq->pool_size)); ibmvmc.max_hmc_index = min_t(u8, ibmvmc_max_hmcs, crq->max_hmc) - 1; ibmvmc.state = ibmvmc_state_ready;
dev_info(adapter->dev, "Capabilities: mtu=0x%x, pool_size=0x%x, max_hmc=0x%x\n", ibmvmc.max_mtu, ibmvmc.max_buffer_pool_size, ibmvmc.max_hmc_index);}
/** * ibmvmc_validate_hmc_session - Validate HMC Session * * @adapter: crq_server_adapter struct * @crq: ibmvmc_crq_msg struct * * Return: * 0 - Success * Non-zero - Failure */static int ibmvmc_validate_hmc_session(struct crq_server_adapter *adapter, struct ibmvmc_crq_msg *crq){ unsigned char hmc_index;
hmc_index = crq->hmc_index;
if (crq->hmc_session == 0) return 0;
if (hmc_index > ibmvmc.max_hmc_index) return -1;
if (hmcs[hmc_index].session != crq->hmc_session) { dev_warn(adapter->dev, "Drop, bad session: expected 0x%x, recv 0x%x\n", hmcs[hmc_index].session, crq->hmc_session); return -1; }
return 0;}
/** * ibmvmc_reset - Reset * * @adapter: crq_server_adapter struct * @xport_event: export_event field * * Closes all HMC sessions and conditionally schedules a CRQ reset. * @xport_event: If true, the partner closed their CRQ; we don't need to reset. * If false, we need to schedule a CRQ reset. */static void ibmvmc_reset(struct crq_server_adapter *adapter, bool xport_event){ int i;
if (ibmvmc.state != ibmvmc_state_sched_reset) { dev_info(adapter->dev, "*** Reset to initial state.\n"); for (i = 0; i < ibmvmc_max_hmcs; i++) ibmvmc_return_hmc(&hmcs[i], xport_event);
if (xport_event) { /* CRQ was closed by the partner. We don't need to do * anything except set ourself to the correct state to * handle init msgs. */ ibmvmc.state = ibmvmc_state_crqinit; } else { /* The partner did not close their CRQ - instead, we're * closing the CRQ on our end. Need to schedule this * for process context, because CRQ reset may require a * sleep. * * Setting ibmvmc.state here immediately prevents * ibmvmc_open from completing until the reset * completes in process context. */ ibmvmc.state = ibmvmc_state_sched_reset; dev_dbg(adapter->dev, "Device reset scheduled"); wake_up_interruptible(&adapter->reset_wait_queue); } }}
/** * ibmvmc_reset_task - Reset Task * * @data: Data field * * Performs a CRQ reset of the VMC device in process context. * NOTE: This function should not be called directly, use ibmvmc_reset. */static int ibmvmc_reset_task(void *data){ struct crq_server_adapter *adapter = data; int rc;
set_user_nice(current, -20);
while (!kthread_should_stop()) { wait_event_interruptible(adapter->reset_wait_queue, (ibmvmc.state == ibmvmc_state_sched_reset) || kthread_should_stop());
if (kthread_should_stop()) break;
dev_dbg(adapter->dev, "CRQ resetting in process context"); tasklet_disable(&adapter->work_task);
rc = ibmvmc_reset_crq_queue(adapter);
if (rc != H_SUCCESS && rc != H_RESOURCE) { dev_err(adapter->dev, "Error initializing CRQ. rc = 0x%x\n", rc); ibmvmc.state = ibmvmc_state_failed; } else { ibmvmc.state = ibmvmc_state_crqinit;
if (ibmvmc_send_crq(adapter, 0xC001000000000000LL, 0) != 0 && rc != H_RESOURCE) dev_warn(adapter->dev, "Failed to send initialize CRQ message\n"); }
vio_enable_interrupts(to_vio_dev(adapter->dev)); tasklet_enable(&adapter->work_task); }
return 0;}
/** * ibmvmc_process_open_resp - Process Open Response * * @crq: ibmvmc_crq_msg struct * @adapter: crq_server_adapter struct * * This command is sent by the hypervisor in response to the Interface * Open message. When this message is received, the indicated buffer is * again available for management partition use. */static void ibmvmc_process_open_resp(struct ibmvmc_crq_msg *crq, struct crq_server_adapter *adapter){ unsigned char hmc_index; unsigned short buffer_id;
hmc_index = crq->hmc_index; if (hmc_index > ibmvmc.max_hmc_index) { /* Why would PHYP give an index > max negotiated? */ ibmvmc_reset(adapter, false); return; }
if (crq->status) { dev_warn(adapter->dev, "open_resp: failed - status 0x%x\n", crq->status); ibmvmc_return_hmc(&hmcs[hmc_index], false); return; }
if (hmcs[hmc_index].state == ibmhmc_state_opening) { buffer_id = be16_to_cpu(crq->var2.buffer_id); if (buffer_id >= ibmvmc.max_buffer_pool_size) { dev_err(adapter->dev, "open_resp: invalid buffer_id = 0x%x\n", buffer_id); hmcs[hmc_index].state = ibmhmc_state_failed; } else { ibmvmc_free_hmc_buffer(&hmcs[hmc_index], &hmcs[hmc_index].buffer[buffer_id]); hmcs[hmc_index].state = ibmhmc_state_ready; dev_dbg(adapter->dev, "open_resp: set hmc state = ready\n"); } } else { dev_warn(adapter->dev, "open_resp: invalid hmc state (0x%x)\n", hmcs[hmc_index].state); }}
/** * ibmvmc_process_close_resp - Process Close Response * * @crq: ibmvmc_crq_msg struct * @adapter: crq_server_adapter struct * * This command is sent by the hypervisor in response to the managemant * application Interface Close message. * * If the close fails, simply reset the entire driver as the state of the VMC * must be in tough shape. */static void ibmvmc_process_close_resp(struct ibmvmc_crq_msg *crq, struct crq_server_adapter *adapter){ unsigned char hmc_index;
hmc_index = crq->hmc_index; if (hmc_index > ibmvmc.max_hmc_index) { ibmvmc_reset(adapter, false); return; }
if (crq->status) { dev_warn(adapter->dev, "close_resp: failed - status 0x%x\n", crq->status); ibmvmc_reset(adapter, false); return; }
ibmvmc_return_hmc(&hmcs[hmc_index], false);}
/** * ibmvmc_crq_process - Process CRQ * * @adapter: crq_server_adapter struct * @crq: ibmvmc_crq_msg struct * * Process the CRQ message based upon the type of message received. * */static void ibmvmc_crq_process(struct crq_server_adapter *adapter, struct ibmvmc_crq_msg *crq){ switch (crq->type) { case VMC_MSG_CAP_RESP: dev_dbg(adapter->dev, "CRQ recv: capabilities resp (0x%x)\n", crq->type); if (ibmvmc.state == ibmvmc_state_capabilities) ibmvmc_process_capabilities(adapter, crq); else dev_warn(adapter->dev, "caps msg invalid in state 0x%x\n", ibmvmc.state); break; case VMC_MSG_OPEN_RESP: dev_dbg(adapter->dev, "CRQ recv: open resp (0x%x)\n", crq->type); if (ibmvmc_validate_hmc_session(adapter, crq) == 0) ibmvmc_process_open_resp(crq, adapter); break; case VMC_MSG_ADD_BUF: dev_dbg(adapter->dev, "CRQ recv: add buf (0x%x)\n", crq->type); if (ibmvmc_validate_hmc_session(adapter, crq) == 0) ibmvmc_add_buffer(adapter, crq); break; case VMC_MSG_REM_BUF: dev_dbg(adapter->dev, "CRQ recv: rem buf (0x%x)\n", crq->type); if (ibmvmc_validate_hmc_session(adapter, crq) == 0) ibmvmc_rem_buffer(adapter, crq); break; case VMC_MSG_SIGNAL: dev_dbg(adapter->dev, "CRQ recv: signal msg (0x%x)\n", crq->type); if (ibmvmc_validate_hmc_session(adapter, crq) == 0) ibmvmc_recv_msg(adapter, crq); break; case VMC_MSG_CLOSE_RESP: dev_dbg(adapter->dev, "CRQ recv: close resp (0x%x)\n", crq->type); if (ibmvmc_validate_hmc_session(adapter, crq) == 0) ibmvmc_process_close_resp(crq, adapter); break; case VMC_MSG_CAP: case VMC_MSG_OPEN: case VMC_MSG_CLOSE: case VMC_MSG_ADD_BUF_RESP: case VMC_MSG_REM_BUF_RESP: dev_warn(adapter->dev, "CRQ recv: unexpected msg (0x%x)\n", crq->type); break; default: dev_warn(adapter->dev, "CRQ recv: unknown msg (0x%x)\n", crq->type); break; }}
/** * ibmvmc_handle_crq_init - Handle CRQ Init * * @crq: ibmvmc_crq_msg struct * @adapter: crq_server_adapter struct * * Handle the type of crq initialization based on whether * it is a message or a response. * */static void ibmvmc_handle_crq_init(struct ibmvmc_crq_msg *crq, struct crq_server_adapter *adapter){ switch (crq->type) { case 0x01: /* Initialization message */ dev_dbg(adapter->dev, "CRQ recv: CRQ init msg - state 0x%x\n", ibmvmc.state); if (ibmvmc.state == ibmvmc_state_crqinit) { /* Send back a response */ if (ibmvmc_send_crq(adapter, 0xC002000000000000, 0) == 0) ibmvmc_send_capabilities(adapter); else dev_err(adapter->dev, " Unable to send init rsp\n"); } else { dev_err(adapter->dev, "Invalid state 0x%x mtu = 0x%x\n", ibmvmc.state, ibmvmc.max_mtu); }
break; case 0x02: /* Initialization response */ dev_dbg(adapter->dev, "CRQ recv: initialization resp msg - state 0x%x\n", ibmvmc.state); if (ibmvmc.state == ibmvmc_state_crqinit) ibmvmc_send_capabilities(adapter); break; default: dev_warn(adapter->dev, "Unknown crq message type 0x%lx\n", (unsigned long)crq->type); }}
/** * ibmvmc_handle_crq - Handle CRQ * * @crq: ibmvmc_crq_msg struct * @adapter: crq_server_adapter struct * * Read the command elements from the command queue and execute the * requests based upon the type of crq message. * */static void ibmvmc_handle_crq(struct ibmvmc_crq_msg *crq, struct crq_server_adapter *adapter){ switch (crq->valid) { case 0xC0: /* initialization */ ibmvmc_handle_crq_init(crq, adapter); break; case 0xFF: /* Hypervisor telling us the connection is closed */ dev_warn(adapter->dev, "CRQ recv: virtual adapter failed - resetting.\n"); ibmvmc_reset(adapter, true); break; case 0x80: /* real payload */ ibmvmc_crq_process(adapter, crq); break; default: dev_warn(adapter->dev, "CRQ recv: unknown msg 0x%02x.\n", crq->valid); break; }}
static void ibmvmc_task(unsigned long data){ struct crq_server_adapter *adapter = (struct crq_server_adapter *)data; struct vio_dev *vdev = to_vio_dev(adapter->dev); struct ibmvmc_crq_msg *crq; int done = 0;
while (!done) { /* Pull all the valid messages off the CRQ */ while ((crq = crq_queue_next_crq(&adapter->queue)) != NULL) { ibmvmc_handle_crq(crq, adapter); crq->valid = 0x00; /* CRQ reset was requested, stop processing CRQs. * Interrupts will be re-enabled by the reset task. */ if (ibmvmc.state == ibmvmc_state_sched_reset) return; }
vio_enable_interrupts(vdev); crq = crq_queue_next_crq(&adapter->queue); if (crq) { vio_disable_interrupts(vdev); ibmvmc_handle_crq(crq, adapter); crq->valid = 0x00; /* CRQ reset was requested, stop processing CRQs. * Interrupts will be re-enabled by the reset task. */ if (ibmvmc.state == ibmvmc_state_sched_reset) return; } else { done = 1; } }}
/** * ibmvmc_init_crq_queue - Init CRQ Queue * * @adapter: crq_server_adapter struct * * Return: * 0 - Success * Non-zero - Failure */static int ibmvmc_init_crq_queue(struct crq_server_adapter *adapter){ struct vio_dev *vdev = to_vio_dev(adapter->dev); struct crq_queue *queue = &adapter->queue; int rc = 0; int retrc = 0;
queue->msgs = kzalloc(PAGE_SIZE, GFP_KERNEL);
if (!queue->msgs) goto malloc_failed;
queue->size = PAGE_SIZE / sizeof(*queue->msgs);
queue->msg_token = dma_map_single(adapter->dev, queue->msgs, queue->size * sizeof(*queue->msgs), DMA_BIDIRECTIONAL);
if (dma_mapping_error(adapter->dev, queue->msg_token)) goto map_failed;
retrc = plpar_hcall_norets(H_REG_CRQ, vdev->unit_address, queue->msg_token, PAGE_SIZE); rc = retrc;
if (rc == H_RESOURCE) rc = ibmvmc_reset_crq_queue(adapter);
if (rc == 2) { dev_warn(adapter->dev, "Partner adapter not ready\n"); retrc = 0; } else if (rc != 0) { dev_err(adapter->dev, "Error %d opening adapter\n", rc); goto reg_crq_failed; }
queue->cur = 0; spin_lock_init(&queue->lock);
tasklet_init(&adapter->work_task, ibmvmc_task, (unsigned long)adapter);
if (request_irq(vdev->irq, ibmvmc_handle_event, 0, "ibmvmc", (void *)adapter) != 0) { dev_err(adapter->dev, "couldn't register irq 0x%x\n", vdev->irq); goto req_irq_failed; }
rc = vio_enable_interrupts(vdev); if (rc != 0) { dev_err(adapter->dev, "Error %d enabling interrupts!!!\n", rc); free_irq(vdev->irq, (void *)adapter); goto req_irq_failed; }
return retrc;
req_irq_failed: /* Cannot have any work since we either never got our IRQ registered, * or never got interrupts enabled */ tasklet_kill(&adapter->work_task); h_free_crq(vdev->unit_address);reg_crq_failed: dma_unmap_single(adapter->dev, queue->msg_token, queue->size * sizeof(*queue->msgs), DMA_BIDIRECTIONAL);map_failed: kfree(queue->msgs);malloc_failed: return -ENOMEM;}
/* Fill in the liobn and riobn fields on the adapter */static int read_dma_window(struct vio_dev *vdev, struct crq_server_adapter *adapter){ const __be32 *dma_window; const __be32 *prop;
/* TODO Using of_parse_dma_window would be better, but it doesn't give * a way to read multiple windows without already knowing the size of * a window or the number of windows */ dma_window = (const __be32 *)vio_get_attribute(vdev, "ibm,my-dma-window", NULL); if (!dma_window) { dev_warn(adapter->dev, "Couldn't find ibm,my-dma-window property\n"); return -1; }
adapter->liobn = be32_to_cpu(*dma_window); dma_window++;
prop = (const __be32 *)vio_get_attribute(vdev, "ibm,#dma-address-cells", NULL); if (!prop) { dev_warn(adapter->dev, "Couldn't find ibm,#dma-address-cells property\n"); dma_window++; } else { dma_window += be32_to_cpu(*prop); }
prop = (const __be32 *)vio_get_attribute(vdev, "ibm,#dma-size-cells", NULL); if (!prop) { dev_warn(adapter->dev, "Couldn't find ibm,#dma-size-cells property\n"); dma_window++; } else { dma_window += be32_to_cpu(*prop); }
/* dma_window should point to the second window now */ adapter->riobn = be32_to_cpu(*dma_window);
return 0;}
static int ibmvmc_probe(struct vio_dev *vdev, const struct vio_device_id *id){ struct crq_server_adapter *adapter = &ibmvmc_adapter; int rc;
dev_set_drvdata(&vdev->dev, NULL); memset(adapter, 0, sizeof(*adapter)); adapter->dev = &vdev->dev;
dev_info(adapter->dev, "Probe for UA 0x%x\n", vdev->unit_address);
rc = read_dma_window(vdev, adapter); if (rc != 0) { ibmvmc.state = ibmvmc_state_failed; return -1; }
dev_dbg(adapter->dev, "Probe: liobn 0x%x, riobn 0x%x\n", adapter->liobn, adapter->riobn);
init_waitqueue_head(&adapter->reset_wait_queue); adapter->reset_task = kthread_run(ibmvmc_reset_task, adapter, "ibmvmc"); if (IS_ERR(adapter->reset_task)) { dev_err(adapter->dev, "Failed to start reset thread\n"); ibmvmc.state = ibmvmc_state_failed; rc = PTR_ERR(adapter->reset_task); adapter->reset_task = NULL; return rc; }
rc = ibmvmc_init_crq_queue(adapter); if (rc != 0 && rc != H_RESOURCE) { dev_err(adapter->dev, "Error initializing CRQ. rc = 0x%x\n", rc); ibmvmc.state = ibmvmc_state_failed; goto crq_failed; }
ibmvmc.state = ibmvmc_state_crqinit;
/* Try to send an initialization message. Note that this is allowed * to fail if the other end is not acive. In that case we just wait * for the other side to initialize. */ if (ibmvmc_send_crq(adapter, 0xC001000000000000LL, 0) != 0 && rc != H_RESOURCE) dev_warn(adapter->dev, "Failed to send initialize CRQ message\n");
dev_set_drvdata(&vdev->dev, adapter);
return 0;
crq_failed: kthread_stop(adapter->reset_task); adapter->reset_task = NULL; return -EPERM;}
static void ibmvmc_remove(struct vio_dev *vdev){ struct crq_server_adapter *adapter = dev_get_drvdata(&vdev->dev);
dev_info(adapter->dev, "Entering remove for UA 0x%x\n", vdev->unit_address); ibmvmc_release_crq_queue(adapter);}
static struct vio_device_id ibmvmc_device_table[] = { { .type = "ibm,vmc", .compat = "IBM,vmc" }, { }};MODULE_DEVICE_TABLE(vio, ibmvmc_device_table);
static struct vio_driver ibmvmc_driver = { .name = ibmvmc_driver_name, .id_table = ibmvmc_device_table, .probe = ibmvmc_probe, .remove = ibmvmc_remove,};
static void __init ibmvmc_scrub_module_parms(void){ if (ibmvmc_max_mtu > MAX_MTU) { pr_warn("ibmvmc: Max MTU reduced to %d\n", MAX_MTU); ibmvmc_max_mtu = MAX_MTU; } else if (ibmvmc_max_mtu < MIN_MTU) { pr_warn("ibmvmc: Max MTU increased to %d\n", MIN_MTU); ibmvmc_max_mtu = MIN_MTU; }
if (ibmvmc_max_buf_pool_size > MAX_BUF_POOL_SIZE) { pr_warn("ibmvmc: Max buffer pool size reduced to %d\n", MAX_BUF_POOL_SIZE); ibmvmc_max_buf_pool_size = MAX_BUF_POOL_SIZE; } else if (ibmvmc_max_buf_pool_size < MIN_BUF_POOL_SIZE) { pr_warn("ibmvmc: Max buffer pool size increased to %d\n", MIN_BUF_POOL_SIZE); ibmvmc_max_buf_pool_size = MIN_BUF_POOL_SIZE; }
if (ibmvmc_max_hmcs > MAX_HMCS) { pr_warn("ibmvmc: Max HMCs reduced to %d\n", MAX_HMCS); ibmvmc_max_hmcs = MAX_HMCS; } else if (ibmvmc_max_hmcs < MIN_HMCS) { pr_warn("ibmvmc: Max HMCs increased to %d\n", MIN_HMCS); ibmvmc_max_hmcs = MIN_HMCS; }}
static struct miscdevice ibmvmc_miscdev = { .name = ibmvmc_driver_name, .minor = MISC_DYNAMIC_MINOR, .fops = &ibmvmc_fops,};
static int __init ibmvmc_module_init(void){ int rc, i, j;
ibmvmc.state = ibmvmc_state_initial; pr_info("ibmvmc: version %s\n", IBMVMC_DRIVER_VERSION);
rc = misc_register(&ibmvmc_miscdev); if (rc) { pr_err("ibmvmc: misc registration failed\n"); goto misc_register_failed; } pr_info("ibmvmc: node %d:%d\n", MISC_MAJOR, ibmvmc_miscdev.minor);
/* Initialize data structures */ memset(hmcs, 0, sizeof(struct ibmvmc_hmc) * MAX_HMCS); for (i = 0; i < MAX_HMCS; i++) { spin_lock_init(&hmcs[i].lock); hmcs[i].state = ibmhmc_state_free; for (j = 0; j < MAX_BUF_POOL_SIZE; j++) hmcs[i].queue_outbound_msgs[j] = VMC_INVALID_BUFFER_ID; }
/* Sanity check module parms */ ibmvmc_scrub_module_parms();
/* * Initialize some reasonable values. Might be negotiated smaller * values during the capabilities exchange. */ ibmvmc.max_mtu = ibmvmc_max_mtu; ibmvmc.max_buffer_pool_size = ibmvmc_max_buf_pool_size; ibmvmc.max_hmc_index = ibmvmc_max_hmcs - 1;
rc = vio_register_driver(&ibmvmc_driver);
if (rc) { pr_err("ibmvmc: rc %d from vio_register_driver\n", rc); goto vio_reg_failed; }
return 0;
vio_reg_failed: misc_deregister(&ibmvmc_miscdev);misc_register_failed: return rc;}
static void __exit ibmvmc_module_exit(void){ pr_info("ibmvmc: module exit\n"); vio_unregister_driver(&ibmvmc_driver); misc_deregister(&ibmvmc_miscdev);}
module_init(ibmvmc_module_init);module_exit(ibmvmc_module_exit);
module_param_named(buf_pool_size, ibmvmc_max_buf_pool_size, int, 0644);MODULE_PARM_DESC(buf_pool_size, "Buffer pool size");module_param_named(max_hmcs, ibmvmc_max_hmcs, int, 0644);MODULE_PARM_DESC(max_hmcs, "Max HMCs");module_param_named(max_mtu, ibmvmc_max_mtu, int, 0644);MODULE_PARM_DESC(max_mtu, "Max MTU");
MODULE_AUTHOR("Steven Royer <seroyer@linux.vnet.ibm.com>");MODULE_DESCRIPTION("IBM VMC");MODULE_VERSION(IBMVMC_DRIVER_VERSION);MODULE_LICENSE("GPL v2");