On Fri, Aug 24, 2012 at 01:16:51PM -0400, Jim Ramsay wrote:
> I will be posting a sample userland application that demonstrates how to upload
> a page table via the netlink interface in a later message.
Here it is:
--- ptupload.c ---
/*
* Copyright (c) 2010-2012 by Dell Inc. All rights reserved.
*
* This file is released under the GPL.
*
* Description:
*
* file: ptupload.c
* authors: Kevin_OKelley@dell.com
* Jim_Ramsay@dell.com
* Vanshil_Shah@dell.com
*
* This file contains an example implementation for uploading a page table over
* the netlink socket to the proposed "switch" target.
*/
"Where:
"
" pagesize - The size of each page, in sectors.
"
" device - The device to upload (path to device node, or 'major:minor')
"
"If no file is given, or the file is "-", expects the page table on STDIN
"
"Page table format:
"
"------------------
"
"The page table must ascii text, containing a list of page-to-path mappings.
"
"Each mapping is represented by a single hexadecimal digit, thus the maximum
"
"number of paths is 0xf (15). Whitespace and non-hex characters are ignored.
"
"Assumes each path is used at least once in the map (or at least the highest-
"
"numbered path, since the total number of paths is inferred from the largest
"
"entry).
");
}
/* Create and bind a netlink generic socket
* Returns the socket FD, or a negative number on failure
*/
int CreateNLSocket()
{
struct sockaddr_nl local;
int fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_GENERIC);
if (fd < 0) {
perror("Unable to create netlink socket");
return fd;
}
/* Assembles the Netlink and Generic Nelink messages and sends them off.
* Returns >= 0 on succes, <0 on failure with errno set appropriately.
*/
int SendPayload(int socket, int familyid, struct IpcPgTable *payload, size_t payloadSize)
{
static char IpcSendBuffer[MAX_IPC_MSG_LEN];
static int seq = 0;
/* Copy the actual payload into nlattr data region */
memcpy(NLA_DATA(na), payload, payloadSize);
memset(&nladdr, 0, sizeof(nladdr));
nladdr.nl_family = AF_NETLINK;
r = sendto(socket, IpcSendBuffer, n->nlmsg_len, 0,
(struct sockaddr*)&nladdr, sizeof(nladdr));
if (r < 0) {
perror("Failed to send message to kernel");
}
return r;
}
/*
* Returns the dm-switch IpcResponse (which is a pointer into a static buffer)
* or 'NULL' on receive failure.
*/
struct IpcResponse *RecvMsg(int socket)
{
static char IpcRecvBuffer[MAX_IPC_MSG_LEN];
struct nlmsghdr *n = (struct nlmsghdr*)IpcRecvBuffer;
struct genlmsghdr *g = (struct genlmsghdr*)NLMSG_DATA(n);
struct nlattr *na = (struct nlattr*)GENLMSG_DATA(g);
struct IpcResponse *resp = (struct IpcResponse*)NLA_DATA(na);
fd_set readfds;
struct timeval timeout;
int rsp;
/* Wait up to 1s for a response */
FD_ZERO(&readfds);
FD_SET(socket, &readfds);
memset(&timeout, 0, sizeof(timeout));
timeout.tv_sec = 1;
rsp = select(socket + 1, &readfds, NULL, NULL, &timeout );
if (rsp < 0) {
perror("Error calling select() on netlink socket");
return NULL;
}
else if (rsp == 0) {
printf("Timeout waiting for response
");
errno = ETIMEDOUT;
return NULL;
}
/* Issue a non-blocking read */
rsp = recv(socket, IpcRecvBuffer, MAX_IPC_MSG_LEN, MSG_DONTWAIT);
if (rsp < 0) {
perror("Error from recv()");
return NULL;
}
/* Validate response message */
if (n->nlmsg_type == NLMSG_ERROR) {
printf("Error from netlink socket
");
errno = EIO;
return NULL;
}
if (!NLMSG_OK((n), (unsigned int)rsp)) {
printf("Invalid reply message from netlink socket
");
errno = EINVAL;
return NULL;
}
return resp;
}
/* Given a page size, major and minor device node information, number of total
* devices, and array of page table entries, constructs the appropriate netlink
* message and sends the bit-packed page table (in peices if necessary) to the
* kernel driver.
*
* Returns 0 on success, -1 on failure with errno set appropriately.
*/
int upload(uint32_t pagesize, uint32_t maj, uint32_t min, uint16_t devcount,
const uint8_t *table, size_t total_pte)
{
int socket, familyid, r = 0;
static const uint32_t bits[] = { 0, 1, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4 };
struct IpcPgTable *payload;
struct IpcResponse *response;
const size_t header = sizeof(*payload) - sizeof(payload->ptbl_buff[0]);
const size_t max_payload = MAX_IPC_MSG_LEN - ALL_NL_HEADERS;
size_t remaining = total_pte;
uint32_t pte_bits, pte_fields, pte_max;
const uint8_t *src;
uint8_t pte_mask;
int nCurrentPage = 0;
/* Ensure the loaded switch module is one we can talk to */
if (checkVersion() < 0) {
errno = EINVAL;
r = -1;
goto out_error;
}
familyid = getFamilyId();
if (familyid <= 0) {
errno = EINVAL;
r = -1;
goto out_error;
}
socket = CreateNLSocket();
if (socket < 0) {
r = -1;
goto out_error;
}
/* Bit-packing:
* Fields are packed with the least significant fields in the
* low-order bytes so the kernel can use a division remainder
* to find the byte offset, then just shift to line up the
* proper value.
*/
for (i = 0; i < words; ++i) {
size_t j;
size_t toPack = MIN(remaining, pte_fields);
uint32_t word = 0;
for (j = 0; j < toPack; ++j) {
word |= (*(src++) & pte_mask) << (pte_bits * j);
remaining--;
}
payload->ptbl_buff[i] = word;
/* Debug output: */
#if 0
printf(" Packed entry %3zu: 0x%08x
",
i, (unsigned int)word);
#endif
}
/* Send IPC */
r = SendPayload(socket, familyid, payload, payload->total_len);
if (r < 0) {
perror("Send failed");
goto out_free;
}
/* Wait for response */
response = RecvMsg(socket);
if (!response) {
r = -1;
perror("No response");
goto out_free;
}
if (response->status != 0) {
printf("Error from kernel module: %s (%d)
",
response->err_str, response->status);
return -1;
}
printf(" Send successful.
");
nCurrentPage++;
}
out_free:
free(payload);
out_close:
close(socket);
out_error:
return r;
}