From caaeb09afda3a2239e02d495cea7e629136e09ee Mon Sep 17 00:00:00 2001 From: Tomas Winkler Date: Mon, 2 Sep 2013 03:11:00 +0300 Subject: mei: mei_cl_link protect open_handle_count from overflow mei_cl_link is called both from mei_open and also from in-kernel drivers so we need to protect open_handle_count from overflow Signed-off-by: Tomas Winkler Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/misc/mei/client.c b/drivers/misc/mei/client.c index e0684b4..a82b443 100644 --- a/drivers/misc/mei/client.c +++ b/drivers/misc/mei/client.c @@ -287,6 +287,12 @@ int mei_cl_link(struct mei_cl *cl, int id) return -ENOENT; } + if (dev->open_handle_count >= MEI_MAX_OPEN_HANDLE_COUNT) { + dev_err(&dev->pdev->dev, "open_handle_count exceded %d", + MEI_MAX_OPEN_HANDLE_COUNT); + return -ENOENT; + } + dev->open_handle_count++; cl->host_client_id = id; -- cgit v0.10.2 From 23f5a322063cba7c9f5dba5205e2960a24291373 Mon Sep 17 00:00:00 2001 From: Tomas Winkler Date: Mon, 2 Sep 2013 03:11:01 +0300 Subject: mei: make sure that me_clients_map big enough before copying To make static analyzers happy validated that sizeof me_clients_map is larger than sizeof valid_addresses from the enumeration response before memcpy We can use BUILD_ON macro as both arrays are defined statically Signed-off-by: Tomas Winkler Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/misc/mei/hbm.c b/drivers/misc/mei/hbm.c index 6127ab6..95d4dab 100644 --- a/drivers/misc/mei/hbm.c +++ b/drivers/misc/mei/hbm.c @@ -673,7 +673,10 @@ void mei_hbm_dispatch(struct mei_device *dev, struct mei_msg_hdr *hdr) case HOST_ENUM_RES_CMD: enum_res = (struct hbm_host_enum_response *) mei_msg; - memcpy(dev->me_clients_map, enum_res->valid_addresses, 32); + BUILD_BUG_ON(sizeof(dev->me_clients_map) + < sizeof(enum_res->valid_addresses)); + memcpy(dev->me_clients_map, enum_res->valid_addresses, + sizeof(enum_res->valid_addresses)); if (dev->dev_state == MEI_DEV_INIT_CLIENTS && dev->hbm_state == MEI_HBM_ENUM_CLIENTS) { dev->init_clients_timer = 0; -- cgit v0.10.2 From d8b29efa27de17dafe516d45204dbb744ccdfa48 Mon Sep 17 00:00:00 2001 From: Alexander Usyskin Date: Mon, 2 Sep 2013 03:11:02 +0300 Subject: mei: mei_write correct checks for copy_from_user 1. return -EFUALT when copy_from_user fails 2. display error message on failure in error level Signed-off-by: Alexander Usyskin Signed-off-by: Tomas Winkler Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/misc/mei/main.c b/drivers/misc/mei/main.c index 173ff09..5ff810b 100644 --- a/drivers/misc/mei/main.c +++ b/drivers/misc/mei/main.c @@ -404,8 +404,11 @@ static ssize_t mei_write(struct file *file, const char __user *ubuf, goto out; rets = copy_from_user(write_cb->request_buffer.data, ubuf, length); - if (rets) + if (rets) { + dev_err(&dev->pdev->dev, "failed to copy data from userland\n"); + rets = -EFAULT; goto out; + } if (cl == &dev->iamthif_cl) { rets = mei_amthif_write(dev, write_cb); @@ -567,7 +570,7 @@ static long mei_ioctl(struct file *file, unsigned int cmd, unsigned long data) dev_dbg(&dev->pdev->dev, "copy connect data from user\n"); if (copy_from_user(connect_data, (char __user *)data, sizeof(struct mei_connect_client_data))) { - dev_dbg(&dev->pdev->dev, "failed to copy data from userland\n"); + dev_err(&dev->pdev->dev, "failed to copy data from userland\n"); rets = -EFAULT; goto out; } -- cgit v0.10.2 From e19555ce893f7567c7a72f91dafe6bdb93f0198f Mon Sep 17 00:00:00 2001 From: Tomas Winkler Date: Mon, 2 Sep 2013 03:11:03 +0300 Subject: mei: fix format compilation warrning on 32 bit architecture hbm.c: In function mei_hbm_me_cl_allocate: hbm.c:52:212: warning: format %zd expects argument of type signed size_t but argument 4 has type long unsigned Signed-off-by: Tomas Winkler Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/misc/mei/hbm.c b/drivers/misc/mei/hbm.c index 95d4dab..f706fe8 100644 --- a/drivers/misc/mei/hbm.c +++ b/drivers/misc/mei/hbm.c @@ -45,7 +45,7 @@ static void mei_hbm_me_cl_allocate(struct mei_device *dev) kfree(dev->me_clients); dev->me_clients = NULL; - dev_dbg(&dev->pdev->dev, "memory allocation for ME clients size=%zd.\n", + dev_dbg(&dev->pdev->dev, "memory allocation for ME clients size=%ld.\n", dev->me_clients_num * sizeof(struct mei_me_client)); /* allocate storage for ME clients representation */ clients = kcalloc(dev->me_clients_num, -- cgit v0.10.2 From dd5de1f165ade430357960459491a067c7e3d21c Mon Sep 17 00:00:00 2001 From: Tomas Winkler Date: Mon, 2 Sep 2013 03:11:04 +0300 Subject: mei: revamp read and write length checks 1. Return zero on zero length read and writes 2. For a too large write return -EFBIG as defined in man write(2) EFBIG An attempt was made to write a file that exceeds the implementation-defined maximum file size or the process's file size limit, or to write at a position past the maximum allowed offset. Signed-off-by: Tomas Winkler Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/misc/mei/main.c b/drivers/misc/mei/main.c index 5ff810b..7404584 100644 --- a/drivers/misc/mei/main.c +++ b/drivers/misc/mei/main.c @@ -203,12 +203,18 @@ static ssize_t mei_read(struct file *file, char __user *ubuf, dev = cl->dev; + mutex_lock(&dev->device_lock); if (dev->dev_state != MEI_DEV_ENABLED) { rets = -ENODEV; goto out; } + if (length == 0) { + rets = 0; + goto out; + } + if (cl == &dev->iamthif_cl) { rets = mei_amthif_read(dev, file, ubuf, length, offset); goto out; @@ -350,8 +356,14 @@ static ssize_t mei_write(struct file *file, const char __user *ubuf, rets = -ENODEV; goto out; } - if (length > dev->me_clients[id].props.max_msg_length || length <= 0) { - rets = -EMSGSIZE; + + if (length == 0) { + rets = 0; + goto out; + } + + if (length > dev->me_clients[id].props.max_msg_length) { + rets = -EFBIG; goto out; } -- cgit v0.10.2 From a9c8a17aea8ced19598b388db618e9094f3c1245 Mon Sep 17 00:00:00 2001 From: Tomas Winkler Date: Sun, 15 Sep 2013 18:11:06 +0300 Subject: mei: mei_release: drop redundant check if cb is NULL mei_io_cb_free follows kfree design and check for NULL internally Signed-off-by: Tomas Winkler Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/misc/mei/main.c b/drivers/misc/mei/main.c index 7404584..9aa3b78 100644 --- a/drivers/misc/mei/main.c +++ b/drivers/misc/mei/main.c @@ -165,10 +165,7 @@ static int mei_release(struct inode *inode, struct file *file) file->private_data = NULL; - if (cb) { - mei_io_cb_free(cb); - cb = NULL; - } + mei_io_cb_free(cb); kfree(cl); out: -- cgit v0.10.2 From c0abffbd982ccf9460187206a074e52cb23e8be3 Mon Sep 17 00:00:00 2001 From: Alexander Usyskin Date: Sun, 15 Sep 2013 18:11:07 +0300 Subject: mei: prefix client log messages with client me and host ids define cl_dbg and cl_err macros that add me and host id prefix for debug and error log messages so we can track for the client context of the flow. Signed-off-by: Alexander Usyskin Signed-off-by: Tomas Winkler Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/misc/mei/client.c b/drivers/misc/mei/client.c index a82b443..bccc316 100644 --- a/drivers/misc/mei/client.c +++ b/drivers/misc/mei/client.c @@ -187,10 +187,14 @@ int mei_io_cb_alloc_resp_buf(struct mei_cl_cb *cb, size_t length) */ int mei_cl_flush_queues(struct mei_cl *cl) { + struct mei_device *dev; + if (WARN_ON(!cl || !cl->dev)) return -EINVAL; - dev_dbg(&cl->dev->pdev->dev, "remove list entry belonging to cl\n"); + dev = cl->dev; + + cl_dbg(dev, cl, "remove list entry belonging to cl\n"); mei_io_list_flush(&cl->dev->read_list, cl); mei_io_list_flush(&cl->dev->write_list, cl); mei_io_list_flush(&cl->dev->write_waiting_list, cl); @@ -302,7 +306,7 @@ int mei_cl_link(struct mei_cl *cl, int id) cl->state = MEI_FILE_INITIALIZING; - dev_dbg(&dev->pdev->dev, "link cl host id = %d\n", cl->host_client_id); + cl_dbg(dev, cl, "link cl\n"); return 0; } @@ -328,7 +332,7 @@ int mei_cl_unlink(struct mei_cl *cl) list_for_each_entry_safe(pos, next, &dev->file_list, link) { if (cl->host_client_id == pos->host_client_id) { - dev_dbg(&dev->pdev->dev, "remove host client = %d, ME client = %d\n", + cl_dbg(dev, cl, "remove host client = %d, ME client = %d\n", pos->host_client_id, pos->me_client_id); list_del_init(&pos->link); break; @@ -396,6 +400,8 @@ int mei_cl_disconnect(struct mei_cl *cl) dev = cl->dev; + cl_dbg(dev, cl, "disconnecting"); + if (cl->state != MEI_FILE_DISCONNECTING) return 0; @@ -408,13 +414,13 @@ int mei_cl_disconnect(struct mei_cl *cl) dev->hbuf_is_ready = false; if (mei_hbm_cl_disconnect_req(dev, cl)) { rets = -ENODEV; - dev_err(&dev->pdev->dev, "failed to disconnect.\n"); + cl_err(dev, cl, "failed to disconnect.\n"); goto free; } mdelay(10); /* Wait for hardware disconnection ready */ list_add_tail(&cb->list, &dev->ctrl_rd_list.list); } else { - dev_dbg(&dev->pdev->dev, "add disconnect cb to control write list\n"); + cl_dbg(dev, cl, "add disconnect cb to control write list\n"); list_add_tail(&cb->list, &dev->ctrl_wr_list.list); } @@ -427,18 +433,17 @@ int mei_cl_disconnect(struct mei_cl *cl) mutex_lock(&dev->device_lock); if (MEI_FILE_DISCONNECTED == cl->state) { rets = 0; - dev_dbg(&dev->pdev->dev, "successfully disconnected from FW client.\n"); + cl_dbg(dev, cl, "successfully disconnected from FW client.\n"); } else { rets = -ENODEV; if (MEI_FILE_DISCONNECTED != cl->state) - dev_dbg(&dev->pdev->dev, "wrong status client disconnect.\n"); + cl_err(dev, cl, "wrong status client disconnect.\n"); if (err) - dev_dbg(&dev->pdev->dev, - "wait failed disconnect err=%08x\n", + cl_dbg(dev, cl, "wait failed disconnect err=%08x\n", err); - dev_dbg(&dev->pdev->dev, "failed to disconnect from FW client.\n"); + cl_err(dev, cl, "failed to disconnect from FW client.\n"); } mei_io_list_flush(&dev->ctrl_rd_list, cl); @@ -645,13 +650,12 @@ int mei_cl_read_start(struct mei_cl *cl, size_t length) return -ENODEV; if (cl->read_cb) { - dev_dbg(&dev->pdev->dev, "read is pending.\n"); + cl_dbg(dev, cl, "read is pending.\n"); return -EBUSY; } i = mei_me_cl_by_id(dev, cl->me_client_id); if (i < 0) { - dev_err(&dev->pdev->dev, "no such me client %d\n", - cl->me_client_id); + cl_err(dev, cl, "no such me client %d\n", cl->me_client_id); return -ENODEV; } @@ -670,6 +674,7 @@ int mei_cl_read_start(struct mei_cl *cl, size_t length) if (dev->hbuf_is_ready) { dev->hbuf_is_ready = false; if (mei_hbm_cl_flow_control_req(dev, cl)) { + cl_err(dev, cl, "flow control send failed\n"); rets = -ENODEV; goto err; } @@ -720,9 +725,8 @@ int mei_cl_irq_write_complete(struct mei_cl *cl, struct mei_cl_cb *cb, return 0; } - dev_dbg(&dev->pdev->dev, "buf: size = %d idx = %lu\n", + cl_dbg(dev, cl, "buf: size = %d idx = %lu\n", cb->request_buffer.size, cb->buf_idx); - dev_dbg(&dev->pdev->dev, MEI_HDR_FMT, MEI_HDR_PRM(&mei_hdr)); *slots -= msg_slots; if (mei_write_message(dev, &mei_hdr, @@ -773,7 +777,7 @@ int mei_cl_write(struct mei_cl *cl, struct mei_cl_cb *cb, bool blocking) buf = &cb->request_buffer; - dev_dbg(&dev->pdev->dev, "mei_cl_write %d\n", buf->size); + cl_dbg(dev, cl, "mei_cl_write %d\n", buf->size); cb->fop_type = MEI_FOP_WRITE; @@ -806,9 +810,6 @@ int mei_cl_write(struct mei_cl *cl, struct mei_cl_cb *cb, bool blocking) mei_hdr.me_addr = cl->me_client_id; mei_hdr.reserved = 0; - dev_dbg(&dev->pdev->dev, "write " MEI_HDR_FMT "\n", - MEI_HDR_PRM(&mei_hdr)); - if (mei_write_message(dev, &mei_hdr, buf->data)) { rets = -EIO; @@ -904,11 +905,11 @@ void mei_cl_all_wakeup(struct mei_device *dev) struct mei_cl *cl, *next; list_for_each_entry_safe(cl, next, &dev->file_list, link) { if (waitqueue_active(&cl->rx_wait)) { - dev_dbg(&dev->pdev->dev, "Waking up reading client!\n"); + cl_dbg(dev, cl, "Waking up reading client!\n"); wake_up_interruptible(&cl->rx_wait); } if (waitqueue_active(&cl->tx_wait)) { - dev_dbg(&dev->pdev->dev, "Waking up writing client!\n"); + cl_dbg(dev, cl, "Waking up writing client!\n"); wake_up_interruptible(&cl->tx_wait); } } diff --git a/drivers/misc/mei/client.h b/drivers/misc/mei/client.h index 9eb031e..6374ed9 100644 --- a/drivers/misc/mei/client.h +++ b/drivers/misc/mei/client.h @@ -109,4 +109,13 @@ void mei_cl_all_disconnect(struct mei_device *dev); void mei_cl_all_wakeup(struct mei_device *dev); void mei_cl_all_write_clear(struct mei_device *dev); +#define MEI_CL_FMT "cl:host=%02d me=%02d " +#define MEI_CL_PRM(cl) (cl)->host_client_id, (cl)->me_client_id + +#define cl_dbg(dev, cl, format, arg...) \ + dev_dbg(&(dev)->pdev->dev, MEI_CL_FMT format, MEI_CL_PRM(cl), ##arg) + +#define cl_err(dev, cl, format, arg...) \ + dev_err(&(dev)->pdev->dev, MEI_CL_FMT format, MEI_CL_PRM(cl), ##arg) + #endif /* _MEI_CLIENT_H_ */ diff --git a/drivers/misc/mei/interrupt.c b/drivers/misc/mei/interrupt.c index 4b59cb7..d27804e 100644 --- a/drivers/misc/mei/interrupt.c +++ b/drivers/misc/mei/interrupt.c @@ -113,13 +113,13 @@ static int mei_cl_irq_read_msg(struct mei_device *dev, if (cb->response_buffer.size == 0 || cb->response_buffer.data == NULL) { - dev_err(&dev->pdev->dev, "response buffer is not allocated.\n"); + cl_err(dev, cl, "response buffer is not allocated.\n"); list_del(&cb->list); return -ENOMEM; } if (cb->response_buffer.size < mei_hdr->length + cb->buf_idx) { - dev_dbg(&dev->pdev->dev, "message overflow. size %d len %d idx %ld\n", + cl_dbg(dev, cl, "message overflow. size %d len %d idx %ld\n", cb->response_buffer.size, mei_hdr->length, cb->buf_idx); buffer = krealloc(cb->response_buffer.data, @@ -127,7 +127,7 @@ static int mei_cl_irq_read_msg(struct mei_device *dev, GFP_KERNEL); if (!buffer) { - dev_err(&dev->pdev->dev, "allocation failed.\n"); + cl_err(dev, cl, "allocation failed.\n"); list_del(&cb->list); return -ENOMEM; } @@ -143,9 +143,7 @@ static int mei_cl_irq_read_msg(struct mei_device *dev, if (mei_hdr->msg_complete) { cl->status = 0; list_del(&cb->list); - dev_dbg(&dev->pdev->dev, "completed read H cl = %d, ME cl = %d, length = %lu\n", - cl->host_client_id, - cl->me_client_id, + cl_dbg(dev, cl, "completed read length = %lu\n", cb->buf_idx); list_add_tail(&cb->list, &complete_list->list); } @@ -423,12 +421,12 @@ int mei_irq_write_handler(struct mei_device *dev, struct mei_cl_cb *cmpl_list) if (MEI_WRITING == cl->writing_state && cb->fop_type == MEI_FOP_WRITE && cl != &dev->iamthif_cl) { - dev_dbg(&dev->pdev->dev, "MEI WRITE COMPLETE\n"); + cl_dbg(dev, cl, "MEI WRITE COMPLETE\n"); cl->writing_state = MEI_WRITE_COMPLETE; list_add_tail(&cb->list, &cmpl_list->list); } if (cl == &dev->iamthif_cl) { - dev_dbg(&dev->pdev->dev, "check iamthif flow control.\n"); + cl_dbg(dev, cl, "check iamthif flow control.\n"); if (dev->iamthif_flow_control_pending) { ret = mei_amthif_irq_read(dev, &slots); if (ret) @@ -510,9 +508,7 @@ int mei_irq_write_handler(struct mei_device *dev, struct mei_cl_cb *cmpl_list) if (cl == NULL) continue; if (mei_cl_flow_ctrl_creds(cl) <= 0) { - dev_dbg(&dev->pdev->dev, - "No flow control credentials for client %d, not sending.\n", - cl->host_client_id); + cl_dbg(dev, cl, "No flow control credentials, not sending.\n"); continue; } -- cgit v0.10.2 From 0da90747353c0f5b663c9c25fd56cd21440d222c Mon Sep 17 00:00:00 2001 From: Alexander Usyskin Date: Mon, 16 Sep 2013 23:44:42 +0300 Subject: mei: fix function names in debug prints Fix calling function names in debug prints. Signed-off-by: Alexander Usyskin Signed-off-by: Tomas Winkler Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/misc/mei/interrupt.c b/drivers/misc/mei/interrupt.c index d27804e..be42c70 100644 --- a/drivers/misc/mei/interrupt.c +++ b/drivers/misc/mei/interrupt.c @@ -343,14 +343,14 @@ int mei_irq_read_handler(struct mei_device *dev, /* decide where to read the message too */ if (!mei_hdr->host_addr) { - dev_dbg(&dev->pdev->dev, "call mei_irq_thread_read_bus_message.\n"); + dev_dbg(&dev->pdev->dev, "call mei_hbm_dispatch.\n"); mei_hbm_dispatch(dev, mei_hdr); - dev_dbg(&dev->pdev->dev, "end mei_irq_thread_read_bus_message.\n"); + dev_dbg(&dev->pdev->dev, "end mei_hbm_dispatch.\n"); } else if (mei_hdr->host_addr == dev->iamthif_cl.host_client_id && (MEI_FILE_CONNECTED == dev->iamthif_cl.state) && (dev->iamthif_state == MEI_IAMTHIF_READING)) { - dev_dbg(&dev->pdev->dev, "call mei_irq_thread_read_iamthif_message.\n"); + dev_dbg(&dev->pdev->dev, "call mei_amthif_irq_read_msg.\n"); dev_dbg(&dev->pdev->dev, MEI_HDR_FMT, MEI_HDR_PRM(mei_hdr)); ret = mei_amthif_irq_read_msg(dev, mei_hdr, cmpl_list); -- cgit v0.10.2 From 2ebf8c94d431078d93599ba56efa58bf850078a1 Mon Sep 17 00:00:00 2001 From: Tomas Winkler Date: Mon, 16 Sep 2013 23:44:43 +0300 Subject: mei: propagate error from write routines instead of ENODEV ENODEV will cause application to try to reconnect since it assumes that device went through the reset write errors are not always fatal it can happen due to resource contention Signed-off-by: Tomas Winkler Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/misc/mei/amthif.c b/drivers/misc/mei/amthif.c index d0fdc13..718f3a1 100644 --- a/drivers/misc/mei/amthif.c +++ b/drivers/misc/mei/amthif.c @@ -312,13 +312,13 @@ static int mei_amthif_send_cmd(struct mei_device *dev, struct mei_cl_cb *cb) mei_hdr.me_addr = dev->iamthif_cl.me_client_id; mei_hdr.reserved = 0; dev->iamthif_msg_buf_index += mei_hdr.length; - if (mei_write_message(dev, &mei_hdr, - (unsigned char *)dev->iamthif_msg_buf)) - return -ENODEV; + ret = mei_write_message(dev, &mei_hdr, dev->iamthif_msg_buf); + if (ret) + return ret; if (mei_hdr.msg_complete) { if (mei_cl_flow_ctrl_reduce(&dev->iamthif_cl)) - return -ENODEV; + return -EIO; dev->iamthif_flow_control_pending = true; dev->iamthif_state = MEI_IAMTHIF_FLOW_CONTROL; dev_dbg(&dev->pdev->dev, "add amthif cb to write waiting list\n"); @@ -458,6 +458,7 @@ int mei_amthif_irq_write_complete(struct mei_cl *cl, struct mei_cl_cb *cb, struct mei_msg_hdr mei_hdr; size_t len = dev->iamthif_msg_buf_size - dev->iamthif_msg_buf_index; u32 msg_slots = mei_data2slots(len); + int rets; mei_hdr.host_addr = cl->host_client_id; mei_hdr.me_addr = cl->me_client_id; @@ -480,16 +481,17 @@ int mei_amthif_irq_write_complete(struct mei_cl *cl, struct mei_cl_cb *cb, dev_dbg(&dev->pdev->dev, MEI_HDR_FMT, MEI_HDR_PRM(&mei_hdr)); *slots -= msg_slots; - if (mei_write_message(dev, &mei_hdr, - dev->iamthif_msg_buf + dev->iamthif_msg_buf_index)) { - dev->iamthif_state = MEI_IAMTHIF_IDLE; - cl->status = -ENODEV; - list_del(&cb->list); - return -ENODEV; + rets = mei_write_message(dev, &mei_hdr, + dev->iamthif_msg_buf + dev->iamthif_msg_buf_index); + if (rets) { + dev->iamthif_state = MEI_IAMTHIF_IDLE; + cl->status = rets; + list_del(&cb->list); + return rets; } if (mei_cl_flow_ctrl_reduce(cl)) - return -ENODEV; + return -EIO; dev->iamthif_msg_buf_index += mei_hdr.length; cl->status = 0; diff --git a/drivers/misc/mei/client.c b/drivers/misc/mei/client.c index bccc316..1a53d96 100644 --- a/drivers/misc/mei/client.c +++ b/drivers/misc/mei/client.c @@ -706,6 +706,7 @@ int mei_cl_irq_write_complete(struct mei_cl *cl, struct mei_cl_cb *cb, struct mei_msg_hdr mei_hdr; size_t len = cb->request_buffer.size - cb->buf_idx; u32 msg_slots = mei_data2slots(len); + int rets; mei_hdr.host_addr = cl->host_client_id; mei_hdr.me_addr = cl->me_client_id; @@ -729,11 +730,12 @@ int mei_cl_irq_write_complete(struct mei_cl *cl, struct mei_cl_cb *cb, cb->request_buffer.size, cb->buf_idx); *slots -= msg_slots; - if (mei_write_message(dev, &mei_hdr, - cb->request_buffer.data + cb->buf_idx)) { - cl->status = -ENODEV; + rets = mei_write_message(dev, &mei_hdr, + cb->request_buffer.data + cb->buf_idx); + if (rets) { + cl->status = rets; list_move_tail(&cb->list, &cmpl_list->list); - return -ENODEV; + return rets; } cl->status = 0; @@ -742,7 +744,7 @@ int mei_cl_irq_write_complete(struct mei_cl *cl, struct mei_cl_cb *cb, if (mei_hdr.msg_complete) { if (mei_cl_flow_ctrl_reduce(cl)) - return -ENODEV; + return -EIO; list_move_tail(&cb->list, &dev->write_waiting_list.list); } @@ -811,10 +813,9 @@ int mei_cl_write(struct mei_cl *cl, struct mei_cl_cb *cb, bool blocking) mei_hdr.reserved = 0; - if (mei_write_message(dev, &mei_hdr, buf->data)) { - rets = -EIO; + rets = mei_write_message(dev, &mei_hdr, buf->data); + if (rets) goto err; - } cl->writing_state = MEI_WRITING; cb->buf_idx = mei_hdr.length; diff --git a/drivers/misc/mei/hbm.c b/drivers/misc/mei/hbm.c index f706fe8..f1c974a 100644 --- a/drivers/misc/mei/hbm.c +++ b/drivers/misc/mei/hbm.c @@ -170,7 +170,7 @@ int mei_hbm_start_req(struct mei_device *dev) dev_err(&dev->pdev->dev, "version message write failed\n"); dev->dev_state = MEI_DEV_RESETTING; mei_reset(dev, 1); - return -ENODEV; + return -EIO; } dev->hbm_state = MEI_HBM_START; dev->init_clients_timer = MEI_CLIENTS_INIT_TIMEOUT; diff --git a/drivers/misc/mei/interrupt.c b/drivers/misc/mei/interrupt.c index be42c70..e4bb9ae 100644 --- a/drivers/misc/mei/interrupt.c +++ b/drivers/misc/mei/interrupt.c @@ -216,9 +216,11 @@ static int mei_cl_irq_read(struct mei_cl *cl, struct mei_cl_cb *cb, s32 *slots, struct mei_cl_cb *cmpl_list) { struct mei_device *dev = cl->dev; - u32 msg_slots = mei_data2slots(sizeof(struct hbm_flow_control)); + int ret; + + if (*slots < msg_slots) { /* return the cancel routine */ list_del(&cb->list); @@ -227,12 +229,14 @@ static int mei_cl_irq_read(struct mei_cl *cl, struct mei_cl_cb *cb, *slots -= msg_slots; - if (mei_hbm_cl_flow_control_req(dev, cl)) { - cl->status = -ENODEV; + ret = mei_hbm_cl_flow_control_req(dev, cl); + if (ret) { + cl->status = ret; cb->buf_idx = 0; list_move_tail(&cb->list, &cmpl_list->list); - return -ENODEV; + return ret; } + list_move_tail(&cb->list, &dev->read_list.list); return 0; @@ -254,6 +258,7 @@ static int mei_cl_irq_ioctl(struct mei_cl *cl, struct mei_cl_cb *cb, s32 *slots, struct mei_cl_cb *cmpl_list) { struct mei_device *dev = cl->dev; + int ret; u32 msg_slots = mei_data2slots(sizeof(struct hbm_client_connect_request)); @@ -268,11 +273,12 @@ static int mei_cl_irq_ioctl(struct mei_cl *cl, struct mei_cl_cb *cb, cl->state = MEI_FILE_CONNECTING; - if (mei_hbm_cl_connect_req(dev, cl)) { - cl->status = -ENODEV; + ret = mei_hbm_cl_connect_req(dev, cl); + if (ret) { + cl->status = ret; cb->buf_idx = 0; list_del(&cb->list); - return -ENODEV; + return ret; } list_move_tail(&cb->list, &dev->ctrl_rd_list.list); -- cgit v0.10.2 From 136698e535cd1ce59e436cc084b41370fd8f1eff Mon Sep 17 00:00:00 2001 From: Tomas Winkler Date: Mon, 16 Sep 2013 23:44:44 +0300 Subject: mei: push credentials inside the irq write handler this eventually allows as use a single write queue both for control and data messages and removing possible race Signed-off-by: Tomas Winkler Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/misc/mei/amthif.c b/drivers/misc/mei/amthif.c index 718f3a1..04fd385 100644 --- a/drivers/misc/mei/amthif.c +++ b/drivers/misc/mei/amthif.c @@ -460,6 +460,15 @@ int mei_amthif_irq_write_complete(struct mei_cl *cl, struct mei_cl_cb *cb, u32 msg_slots = mei_data2slots(len); int rets; + rets = mei_cl_flow_ctrl_creds(cl); + if (rets < 0) + return rets; + + if (rets == 0) { + cl_dbg(dev, cl, "No flow control credentials: not sending.\n"); + return 0; + } + mei_hdr.host_addr = cl->host_client_id; mei_hdr.me_addr = cl->me_client_id; mei_hdr.reserved = 0; diff --git a/drivers/misc/mei/client.c b/drivers/misc/mei/client.c index 1a53d96..2ab9d16 100644 --- a/drivers/misc/mei/client.c +++ b/drivers/misc/mei/client.c @@ -702,12 +702,33 @@ err: int mei_cl_irq_write_complete(struct mei_cl *cl, struct mei_cl_cb *cb, s32 *slots, struct mei_cl_cb *cmpl_list) { - struct mei_device *dev = cl->dev; + struct mei_device *dev; + struct mei_msg_data *buf; struct mei_msg_hdr mei_hdr; - size_t len = cb->request_buffer.size - cb->buf_idx; - u32 msg_slots = mei_data2slots(len); + size_t len; + u32 msg_slots; int rets; + + if (WARN_ON(!cl || !cl->dev)) + return -ENODEV; + + dev = cl->dev; + + buf = &cb->request_buffer; + + rets = mei_cl_flow_ctrl_creds(cl); + if (rets < 0) + return rets; + + if (rets == 0) { + cl_dbg(dev, cl, "No flow control credentials: not sending.\n"); + return 0; + } + + len = buf->size - cb->buf_idx; + msg_slots = mei_data2slots(len); + mei_hdr.host_addr = cl->host_client_id; mei_hdr.me_addr = cl->me_client_id; mei_hdr.reserved = 0; @@ -730,8 +751,7 @@ int mei_cl_irq_write_complete(struct mei_cl *cl, struct mei_cl_cb *cb, cb->request_buffer.size, cb->buf_idx); *slots -= msg_slots; - rets = mei_write_message(dev, &mei_hdr, - cb->request_buffer.data + cb->buf_idx); + rets = mei_write_message(dev, &mei_hdr, buf->data + cb->buf_idx); if (rets) { cl->status = rets; list_move_tail(&cb->list, &cmpl_list->list); diff --git a/drivers/misc/mei/interrupt.c b/drivers/misc/mei/interrupt.c index e4bb9ae..7a95c07 100644 --- a/drivers/misc/mei/interrupt.c +++ b/drivers/misc/mei/interrupt.c @@ -513,11 +513,6 @@ int mei_irq_write_handler(struct mei_device *dev, struct mei_cl_cb *cmpl_list) cl = cb->cl; if (cl == NULL) continue; - if (mei_cl_flow_ctrl_creds(cl) <= 0) { - cl_dbg(dev, cl, "No flow control credentials, not sending.\n"); - continue; - } - if (cl == &dev->iamthif_cl) ret = mei_amthif_irq_write_complete(cl, cb, &slots, cmpl_list); -- cgit v0.10.2 From a14c44d82fcff280fd1138574d4480b2bdd40216 Mon Sep 17 00:00:00 2001 From: Tomas Winkler Date: Mon, 16 Sep 2013 23:44:45 +0300 Subject: mei: mei_cl_unlink: no need to loop over dev list we can call list_del_init regardless the client is linked or not it is always properly initialized Signed-off-by: Tomas Winkler Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/misc/mei/client.c b/drivers/misc/mei/client.c index 2ab9d16..fbd319c 100644 --- a/drivers/misc/mei/client.c +++ b/drivers/misc/mei/client.c @@ -318,7 +318,6 @@ int mei_cl_link(struct mei_cl *cl, int id) int mei_cl_unlink(struct mei_cl *cl) { struct mei_device *dev; - struct mei_cl *pos, *next; /* don't shout on error exit path */ if (!cl) @@ -330,14 +329,10 @@ int mei_cl_unlink(struct mei_cl *cl) dev = cl->dev; - list_for_each_entry_safe(pos, next, &dev->file_list, link) { - if (cl->host_client_id == pos->host_client_id) { - cl_dbg(dev, cl, "remove host client = %d, ME client = %d\n", - pos->host_client_id, pos->me_client_id); - list_del_init(&pos->link); - break; - } - } + cl_dbg(dev, cl, "unlink client"); + + list_del_init(&cl->link); + return 0; } -- cgit v0.10.2 From d396034aa860e7aaade347a535e831a4307037d3 Mon Sep 17 00:00:00 2001 From: Chen Gang Date: Mon, 2 Sep 2013 15:54:03 +0800 Subject: drivers: misc: bmp085: remove '__init' from mp085_get_of_properties() bmp085_get_of_properties() is called by bmp085_init_client() which is called by bmp085_probe() which is an EXPORT_SYMBOL_GPL function. bmp085_probe() is really used as a probe function by another modules (e.g. bmp085-i2c.c, bmp085-spi.c). Except bmp085_get_of_properties(), all functions have no '__init', so need remove '__init' from bmp085_get_of_properties() too, or at least, it will report related warning: WARNING: vmlinux.o(.text+0x4c8a07): Section mismatch in reference from the variable .LM171 to the variable .init.text:_bmp085_get_of_properties The function .LM171() references the variable __init _bmp085_get_of_properties. This is often because .LM171 lacks a __init annotation or the annotation of _bmp085_get_of_properties is wrong. Signed-off-by: Chen Gang Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/misc/bmp085.c b/drivers/misc/bmp085.c index 849e2fe..2704d88 100644 --- a/drivers/misc/bmp085.c +++ b/drivers/misc/bmp085.c @@ -374,7 +374,7 @@ int bmp085_detect(struct device *dev) } EXPORT_SYMBOL_GPL(bmp085_detect); -static void __init bmp085_get_of_properties(struct bmp085_data *data) +static void bmp085_get_of_properties(struct bmp085_data *data) { #ifdef CONFIG_OF struct device_node *np = data->dev->of_node; -- cgit v0.10.2 From 6d3e0d0c1ca926fd6ab557fcd5efb171148f43d6 Mon Sep 17 00:00:00 2001 From: Tomas Winkler Date: Wed, 4 Sep 2013 15:35:52 +0300 Subject: hpet: remove useless check if fixmem32 is NULL fixmem32 is assigned to address of res->data member so the address is always valid Actually since we are not checking for res != NULL static analyzing is complaining about referencing the pointer and consequent check for null. The code snippet looks confusing also for human eyes. Cc: Randy Dunlap Signed-off-by: Tomas Winkler Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/char/hpet.c b/drivers/char/hpet.c index 448ce5e..c8f4329 100644 --- a/drivers/char/hpet.c +++ b/drivers/char/hpet.c @@ -971,8 +971,6 @@ static acpi_status hpet_resources(struct acpi_resource *res, void *data) struct acpi_resource_fixed_memory32 *fixmem32; fixmem32 = &res->data.fixed_memory32; - if (!fixmem32) - return AE_NO_MEMORY; hdp->hd_phys_address = fixmem32->address; hdp->hd_address = ioremap(fixmem32->address, -- cgit v0.10.2 From 497b46dbc660d53e94abf8a46cda9747023802ba Mon Sep 17 00:00:00 2001 From: Fengguang Wu Date: Mon, 2 Sep 2013 10:38:33 +0200 Subject: drivers: uio: mf624_disable_interrupt() can be static Signed-off-by: Fengguang Wu Signed-off-by: Rostislav Lisovy Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/uio/uio_mf624.c b/drivers/uio/uio_mf624.c index a1768b2..92cf6a9 100644 --- a/drivers/uio/uio_mf624.c +++ b/drivers/uio/uio_mf624.c @@ -42,7 +42,7 @@ enum mf624_interrupt_source {ADC, CTR4, ALL}; -void mf624_disable_interrupt(enum mf624_interrupt_source source, +static void mf624_disable_interrupt(enum mf624_interrupt_source source, struct uio_info *info) { void __iomem *INTCSR_reg = info->mem[0].internal_addr + INTCSR; @@ -70,7 +70,7 @@ void mf624_disable_interrupt(enum mf624_interrupt_source source, } } -void mf624_enable_interrupt(enum mf624_interrupt_source source, +static void mf624_enable_interrupt(enum mf624_interrupt_source source, struct uio_info *info) { void __iomem *INTCSR_reg = info->mem[0].internal_addr + INTCSR; -- cgit v0.10.2 From e6789cd3dfb553077606ccafeb05e0043f072481 Mon Sep 17 00:00:00 2001 From: Michal Simek Date: Thu, 12 Sep 2013 07:39:59 +0200 Subject: uio: Simplify uio error path by using devres functions Using devres functions simplify driver error path. - Use devm_kzalloc - Use devm_request_irq Signed-off-by: Michal Simek Reviewed-by: Pavel Machek Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/uio/uio.c b/drivers/uio/uio.c index ba47563..11d4e0a 100644 --- a/drivers/uio/uio.c +++ b/drivers/uio/uio.c @@ -796,10 +796,9 @@ int __uio_register_device(struct module *owner, info->uio_dev = NULL; - idev = kzalloc(sizeof(*idev), GFP_KERNEL); + idev = devm_kzalloc(parent, sizeof(*idev), GFP_KERNEL); if (!idev) { - ret = -ENOMEM; - goto err_kzalloc; + return -ENOMEM; } idev->owner = owner; @@ -809,7 +808,7 @@ int __uio_register_device(struct module *owner, ret = uio_get_minor(idev); if (ret) - goto err_get_minor; + return ret; idev->dev = device_create(&uio_class, parent, MKDEV(uio_major, idev->minor), idev, @@ -827,7 +826,7 @@ int __uio_register_device(struct module *owner, info->uio_dev = idev; if (info->irq && (info->irq != UIO_IRQ_CUSTOM)) { - ret = request_irq(info->irq, uio_interrupt, + ret = devm_request_irq(parent, info->irq, uio_interrupt, info->irq_flags, info->name, idev); if (ret) goto err_request_irq; @@ -841,9 +840,6 @@ err_uio_dev_add_attributes: device_destroy(&uio_class, MKDEV(uio_major, idev->minor)); err_device_create: uio_free_minor(idev); -err_get_minor: - kfree(idev); -err_kzalloc: return ret; } EXPORT_SYMBOL_GPL(__uio_register_device); @@ -864,13 +860,9 @@ void uio_unregister_device(struct uio_info *info) uio_free_minor(idev); - if (info->irq && (info->irq != UIO_IRQ_CUSTOM)) - free_irq(info->irq, idev); - uio_dev_del_attributes(idev); device_destroy(&uio_class, MKDEV(uio_major, idev->minor)); - kfree(idev); return; } diff --git a/drivers/uio/uio_pdrv_genirq.c b/drivers/uio/uio_pdrv_genirq.c index 90ff17a..7666931 100644 --- a/drivers/uio/uio_pdrv_genirq.c +++ b/drivers/uio/uio_pdrv_genirq.c @@ -112,11 +112,11 @@ static int uio_pdrv_genirq_probe(struct platform_device *pdev) if (pdev->dev.of_node) { /* alloc uioinfo for one device */ - uioinfo = kzalloc(sizeof(*uioinfo), GFP_KERNEL); + uioinfo = devm_kzalloc(&pdev->dev, sizeof(*uioinfo), + GFP_KERNEL); if (!uioinfo) { - ret = -ENOMEM; dev_err(&pdev->dev, "unable to kmalloc\n"); - return ret; + return -ENOMEM; } uioinfo->name = pdev->dev.of_node->name; uioinfo->version = "devicetree"; @@ -125,20 +125,19 @@ static int uio_pdrv_genirq_probe(struct platform_device *pdev) if (!uioinfo || !uioinfo->name || !uioinfo->version) { dev_err(&pdev->dev, "missing platform_data\n"); - goto bad0; + return ret; } if (uioinfo->handler || uioinfo->irqcontrol || uioinfo->irq_flags & IRQF_SHARED) { dev_err(&pdev->dev, "interrupt configuration error\n"); - goto bad0; + return ret; } - priv = kzalloc(sizeof(*priv), GFP_KERNEL); + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); if (!priv) { - ret = -ENOMEM; dev_err(&pdev->dev, "unable to kmalloc\n"); - goto bad0; + return -ENOMEM; } priv->uioinfo = uioinfo; @@ -153,7 +152,7 @@ static int uio_pdrv_genirq_probe(struct platform_device *pdev) uioinfo->irq = UIO_IRQ_NONE; else if (ret < 0) { dev_err(&pdev->dev, "failed to get IRQ\n"); - goto bad1; + return ret; } } @@ -209,20 +208,12 @@ static int uio_pdrv_genirq_probe(struct platform_device *pdev) ret = uio_register_device(&pdev->dev, priv->uioinfo); if (ret) { dev_err(&pdev->dev, "unable to register uio device\n"); - goto bad2; + pm_runtime_disable(&pdev->dev); + return ret; } platform_set_drvdata(pdev, priv); return 0; - bad2: - pm_runtime_disable(&pdev->dev); - bad1: - kfree(priv); - bad0: - /* kfree uioinfo for OF */ - if (pdev->dev.of_node) - kfree(uioinfo); - return ret; } static int uio_pdrv_genirq_remove(struct platform_device *pdev) @@ -235,11 +226,6 @@ static int uio_pdrv_genirq_remove(struct platform_device *pdev) priv->uioinfo->handler = NULL; priv->uioinfo->irqcontrol = NULL; - /* kfree uioinfo for OF */ - if (pdev->dev.of_node) - kfree(priv->uioinfo); - - kfree(priv); return 0; } -- cgit v0.10.2 From e56516867f3de862df5fd9cf30e54a0b73921e83 Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Thu, 12 Sep 2013 15:37:46 +0900 Subject: uio: uio_aec: remove unnecessary pci_set_drvdata() The driver core clears the driver data to NULL after device_release or on probe failure. Thus, it is not needed to manually clear the device driver data to NULL. Signed-off-by: Jingoo Han Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/uio/uio_aec.c b/drivers/uio/uio_aec.c index f3611c2..1549fab 100644 --- a/drivers/uio/uio_aec.c +++ b/drivers/uio/uio_aec.c @@ -147,7 +147,6 @@ static void remove(struct pci_dev *pdev) uio_unregister_device(info); pci_release_regions(pdev); pci_disable_device(pdev); - pci_set_drvdata(pdev, NULL); iounmap(info->priv); kfree(info); -- cgit v0.10.2 From 8a4c10f8ec89fa40b0de33ce3ebe00781d010f3d Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Thu, 12 Sep 2013 15:38:44 +0900 Subject: uio: uio_cif: remove unnecessary pci_set_drvdata() The driver core clears the driver data to NULL after device_release or on probe failure. Thus, it is not needed to manually clear the device driver data to NULL. Signed-off-by: Jingoo Han Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/uio/uio_cif.c b/drivers/uio/uio_cif.c index 22cdf38..30f533c 100644 --- a/drivers/uio/uio_cif.c +++ b/drivers/uio/uio_cif.c @@ -106,7 +106,6 @@ static void hilscher_pci_remove(struct pci_dev *dev) uio_unregister_device(info); pci_release_regions(dev); pci_disable_device(dev); - pci_set_drvdata(dev, NULL); iounmap(info->mem[0].internal_addr); kfree (info); -- cgit v0.10.2 From 1129308885bb9031a2d714523368894338cf9e18 Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Thu, 12 Sep 2013 15:39:08 +0900 Subject: uio: uio_mf624: remove unnecessary pci_set_drvdata() The driver core clears the driver data to NULL after device_release or on probe failure. Thus, it is not needed to manually clear the device driver data to NULL. Signed-off-by: Jingoo Han Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/uio/uio_mf624.c b/drivers/uio/uio_mf624.c index 92cf6a9..f764adb 100644 --- a/drivers/uio/uio_mf624.c +++ b/drivers/uio/uio_mf624.c @@ -220,7 +220,6 @@ static void mf624_pci_remove(struct pci_dev *dev) uio_unregister_device(info); pci_release_regions(dev); pci_disable_device(dev); - pci_set_drvdata(dev, NULL); iounmap(info->mem[0].internal_addr); iounmap(info->mem[1].internal_addr); -- cgit v0.10.2 From c7a8eb001f4cdeeab912a9d59b02d93686998138 Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Thu, 12 Sep 2013 15:39:26 +0900 Subject: uio: uio_netx: remove unnecessary pci_set_drvdata() The driver core clears the driver data to NULL after device_release or on probe failure. Thus, it is not needed to manually clear the device driver data to NULL. Signed-off-by: Jingoo Han Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/uio/uio_netx.c b/drivers/uio/uio_netx.c index 28a766b..4c345db 100644 --- a/drivers/uio/uio_netx.c +++ b/drivers/uio/uio_netx.c @@ -127,7 +127,6 @@ static void netx_pci_remove(struct pci_dev *dev) uio_unregister_device(info); pci_release_regions(dev); pci_disable_device(dev); - pci_set_drvdata(dev, NULL); iounmap(info->mem[0].internal_addr); kfree(info); -- cgit v0.10.2 From 37ecdbb012bf3a466d4dda85a93d1c69dc483d3a Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Thu, 12 Sep 2013 15:39:55 +0900 Subject: uio: uio_sercos3: remove unnecessary pci_set_drvdata() The driver core clears the driver data to NULL after device_release or on probe failure. Thus, it is not needed to manually clear the device driver data to NULL. Signed-off-by: Jingoo Han Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/uio/uio_sercos3.c b/drivers/uio/uio_sercos3.c index 5419832..9cfdfca 100644 --- a/drivers/uio/uio_sercos3.c +++ b/drivers/uio/uio_sercos3.c @@ -188,7 +188,6 @@ static void sercos3_pci_remove(struct pci_dev *dev) uio_unregister_device(info); pci_release_regions(dev); pci_disable_device(dev); - pci_set_drvdata(dev, NULL); for (i = 0; i < 5; i++) { if (info->mem[i].internal_addr) iounmap(info->mem[i].internal_addr); -- cgit v0.10.2 From 20055477566958e751a20290aa45ae21bd4b5e11 Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Mon, 9 Sep 2013 14:14:50 +0900 Subject: char: xilinx_hwicap: Remove casting the return value which is a void pointer Casting the return value which is a void pointer is redundant. The conversion from void pointer to any other pointer type is guaranteed by the C programming language. Signed-off-by: Jingoo Han Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/char/xilinx_hwicap/xilinx_hwicap.c b/drivers/char/xilinx_hwicap/xilinx_hwicap.c index 5224da5..a7f65c2 100644 --- a/drivers/char/xilinx_hwicap/xilinx_hwicap.c +++ b/drivers/char/xilinx_hwicap/xilinx_hwicap.c @@ -721,7 +721,7 @@ static int hwicap_remove(struct device *dev) { struct hwicap_drvdata *drvdata; - drvdata = (struct hwicap_drvdata *)dev_get_drvdata(dev); + drvdata = dev_get_drvdata(dev); if (!drvdata) return 0; -- cgit v0.10.2 From bbf831dfe8538ef3797c9603b7897d4ad9ef9d4a Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Mon, 9 Sep 2013 14:15:52 +0900 Subject: misc: ibmasm: Remove casting the return value which is a void pointer Casting the return value which is a void pointer is redundant. The conversion from void pointer to any other pointer type is guaranteed by the C programming language. Signed-off-by: Jingoo Han Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/misc/ibmasm/module.c b/drivers/misc/ibmasm/module.c index 0346d87..0c290e4 100644 --- a/drivers/misc/ibmasm/module.c +++ b/drivers/misc/ibmasm/module.c @@ -165,7 +165,7 @@ error_resources: static void ibmasm_remove_one(struct pci_dev *pdev) { - struct service_processor *sp = (struct service_processor *)pci_get_drvdata(pdev); + struct service_processor *sp = pci_get_drvdata(pdev); dbg("Unregistering UART\n"); ibmasm_unregister_uart(sp); -- cgit v0.10.2 From 684116caa44d1642fcbd4d1d5b9cb975572cfa0b Mon Sep 17 00:00:00 2001 From: Elad Wexler Date: Sat, 14 Sep 2013 18:02:11 +0300 Subject: Drivers: char: misc: 'misc_deregister()' changed the 'mutex_unlock' logic upon an error This change improves code readability & is less error-prone. For example: case adding more error paths one should remember to call 'mutex_unlock' Signed-off-by: Elad Wexler Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/char/misc.c b/drivers/char/misc.c index 190d442..2f685f6 100644 --- a/drivers/char/misc.c +++ b/drivers/char/misc.c @@ -193,8 +193,8 @@ int misc_register(struct miscdevice * misc) if (misc->minor == MISC_DYNAMIC_MINOR) { int i = find_first_zero_bit(misc_minors, DYNAMIC_MINORS); if (i >= DYNAMIC_MINORS) { - mutex_unlock(&misc_mtx); - return -EBUSY; + err = -EBUSY; + goto out; } misc->minor = DYNAMIC_MINORS - i - 1; set_bit(i, misc_minors); @@ -203,8 +203,8 @@ int misc_register(struct miscdevice * misc) list_for_each_entry(c, &misc_list, list) { if (c->minor == misc->minor) { - mutex_unlock(&misc_mtx); - return -EBUSY; + err = -EBUSY; + goto out; } } } -- cgit v0.10.2 From 3173463418af18b6c7b30bc6a94777780129291d Mon Sep 17 00:00:00 2001 From: Peter Senna Tschudin Date: Sun, 22 Sep 2013 00:27:34 +0200 Subject: hv: Change variable type to bool The variable execute_shutdown is only assigned the values true and false. Change its type to bool. The simplified semantic patch that find this problem is as follows (http://coccinelle.lip6.fr/): @exists@ type T; identifier b; @@ - T + bool b = ...; ... when any b = \(true\|false\) Signed-off-by: Peter Senna Tschudin Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/hv/hv_util.c b/drivers/hv/hv_util.c index cb82233..9113850 100644 --- a/drivers/hv/hv_util.c +++ b/drivers/hv/hv_util.c @@ -82,7 +82,7 @@ static void shutdown_onchannelcallback(void *context) struct vmbus_channel *channel = context; u32 recvlen; u64 requestid; - u8 execute_shutdown = false; + bool execute_shutdown = false; u8 *shut_txf_buf = util_shutdown.recv_buffer; struct shutdown_msg_data *shutdown_msg; -- cgit v0.10.2 From ae647589a419d65778a7d06bd608a6fd526676e4 Mon Sep 17 00:00:00 2001 From: Libo Chen Date: Fri, 13 Sep 2013 14:49:41 -0700 Subject: drivers/pcmcia/pd6729.c: convert to module_pci_driver Use module_pci_driver instead of init/exit, make code clean. Signed-off-by: Libo Chen Cc: Bill Pemberton Cc: Eric Miao Cc: Dominik Brodowski Signed-off-by: Andrew Morton Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/pcmcia/pd6729.c b/drivers/pcmcia/pd6729.c index a4c16ee..622dd6f 100644 --- a/drivers/pcmcia/pd6729.c +++ b/drivers/pcmcia/pd6729.c @@ -777,15 +777,4 @@ static struct pci_driver pd6729_pci_driver = { .remove = pd6729_pci_remove, }; -static int pd6729_module_init(void) -{ - return pci_register_driver(&pd6729_pci_driver); -} - -static void pd6729_module_exit(void) -{ - pci_unregister_driver(&pd6729_pci_driver); -} - -module_init(pd6729_module_init); -module_exit(pd6729_module_exit); +module_pci_driver(pd6729_pci_driver); -- cgit v0.10.2 From 7d19143fe6bc928aab9968fd47fe39b4996a3297 Mon Sep 17 00:00:00 2001 From: Libo Chen Date: Fri, 13 Sep 2013 14:49:42 -0700 Subject: drivers/pcmcia/yenta_socket.c: convert to module_pci_driver Use module_pci_driver instead of init/exit, make code clean. Signed-off-by: Libo Chen Cc: Dominik Brodowski Cc: Bill Pemberton Cc: Greg Kroah-Hartman Cc: Eric Miao Cc: Bjorn Helgaas Signed-off-by: Andrew Morton Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/pcmcia/yenta_socket.c b/drivers/pcmcia/yenta_socket.c index 6b4ff09..dc18a3a 100644 --- a/drivers/pcmcia/yenta_socket.c +++ b/drivers/pcmcia/yenta_socket.c @@ -1439,20 +1439,6 @@ static struct pci_driver yenta_cardbus_driver = { .driver.pm = YENTA_PM_OPS, }; - -static int __init yenta_socket_init(void) -{ - return pci_register_driver(¥ta_cardbus_driver); -} - - -static void __exit yenta_socket_exit(void) -{ - pci_unregister_driver(¥ta_cardbus_driver); -} - - -module_init(yenta_socket_init); -module_exit(yenta_socket_exit); +module_pci_driver(yenta_cardbus_driver); MODULE_LICENSE("GPL"); -- cgit v0.10.2 From 03f3a9107f5e848a42f25e3ab133db9f3b6546c7 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Fri, 13 Sep 2013 11:32:49 -0700 Subject: hv: use dev_groups for device attributes This patch is the first in a series that moves the hv bus code to use the dev_groups field instead of dev_attrs, as dev_attrs is going away in future kernel releases. It moves the id sysfs file to the dev_groups structure, and creates the needed show/store functions, instead of relying on one "universal" function for this. By doing this, it removes the need for this to be in a temporary structure. Tested-by: "K. Y. Srinivasan" Cc: Haiyang Zhang Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/hv/channel.c b/drivers/hv/channel.c index 6de6c98..b58a178 100644 --- a/drivers/hv/channel.c +++ b/drivers/hv/channel.c @@ -69,7 +69,6 @@ void vmbus_get_debug_info(struct vmbus_channel *channel, u8 monitor_group = (u8)channel->offermsg.monitorid / 32; u8 monitor_offset = (u8)channel->offermsg.monitorid % 32; - debuginfo->relid = channel->offermsg.child_relid; debuginfo->state = channel->state; memcpy(&debuginfo->interfacetype, &channel->offermsg.offer.if_type, sizeof(uuid_le)); diff --git a/drivers/hv/vmbus_drv.c b/drivers/hv/vmbus_drv.c index f9fe46f..5c21b22 100644 --- a/drivers/hv/vmbus_drv.c +++ b/drivers/hv/vmbus_drv.c @@ -47,7 +47,6 @@ static struct completion probe_event; static int irq; struct hv_device_info { - u32 chn_id; u32 chn_state; uuid_le chn_type; uuid_le chn_instance; @@ -83,7 +82,6 @@ static void get_channel_info(struct hv_device *device, vmbus_get_debug_info(device->channel, &debug_info); - info->chn_id = debug_info.relid; info->chn_state = debug_info.state; memcpy(&info->chn_type, &debug_info.interfacetype, sizeof(uuid_le)); @@ -156,8 +154,6 @@ static ssize_t vmbus_show_device_attr(struct device *dev, ret = sprintf(buf, "vmbus:%s\n", alias_name); } else if (!strcmp(dev_attr->attr.name, "state")) { ret = sprintf(buf, "%d\n", device_info->chn_state); - } else if (!strcmp(dev_attr->attr.name, "id")) { - ret = sprintf(buf, "%d\n", device_info->chn_id); } else if (!strcmp(dev_attr->attr.name, "out_intr_mask")) { ret = sprintf(buf, "%d\n", device_info->outbound.int_mask); } else if (!strcmp(dev_attr->attr.name, "out_read_index")) { @@ -204,9 +200,25 @@ static ssize_t vmbus_show_device_attr(struct device *dev, return ret; } +static ssize_t id_show(struct device *dev, struct device_attribute *dev_attr, + char *buf) +{ + struct hv_device *hv_dev = device_to_hv_device(dev); + + if (!hv_dev->channel) + return -ENODEV; + return sprintf(buf, "%d\n", hv_dev->channel->offermsg.child_relid); +} +static DEVICE_ATTR_RO(id); + +static struct attribute *vmbus_attrs[] = { + &dev_attr_id.attr, + NULL, +}; +ATTRIBUTE_GROUPS(vmbus); + /* Set up per device attributes in /sys/bus/vmbus/devices/ */ static struct device_attribute vmbus_device_attrs[] = { - __ATTR(id, S_IRUGO, vmbus_show_device_attr, NULL), __ATTR(state, S_IRUGO, vmbus_show_device_attr, NULL), __ATTR(class_id, S_IRUGO, vmbus_show_device_attr, NULL), __ATTR(device_id, S_IRUGO, vmbus_show_device_attr, NULL), @@ -384,6 +396,7 @@ static struct bus_type hv_bus = { .probe = vmbus_probe, .uevent = vmbus_uevent, .dev_attrs = vmbus_device_attrs, + .dev_groups = vmbus_groups, }; static const char *driver_name = "hyperv"; diff --git a/include/linux/hyperv.h b/include/linux/hyperv.h index a3b8b2e..45e9b65 100644 --- a/include/linux/hyperv.h +++ b/include/linux/hyperv.h @@ -900,7 +900,6 @@ enum vmbus_channel_state { }; struct vmbus_channel_debug_info { - u32 relid; enum vmbus_channel_state state; uuid_le interfacetype; uuid_le interface_instance; -- cgit v0.10.2 From a8fb5f3d582d3b4997d9449ceb82a0223d169486 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Fri, 13 Sep 2013 11:32:50 -0700 Subject: hv: move "state" bus attribute to dev_groups This moves the "state" bus attribute to the dev_groups structure, removing the need for it to be in a temporary structure. Tested-by: "K. Y. Srinivasan" Cc: Haiyang Zhang Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/hv/channel.c b/drivers/hv/channel.c index b58a178..64f19f2 100644 --- a/drivers/hv/channel.c +++ b/drivers/hv/channel.c @@ -69,7 +69,6 @@ void vmbus_get_debug_info(struct vmbus_channel *channel, u8 monitor_group = (u8)channel->offermsg.monitorid / 32; u8 monitor_offset = (u8)channel->offermsg.monitorid % 32; - debuginfo->state = channel->state; memcpy(&debuginfo->interfacetype, &channel->offermsg.offer.if_type, sizeof(uuid_le)); memcpy(&debuginfo->interface_instance, diff --git a/drivers/hv/vmbus_drv.c b/drivers/hv/vmbus_drv.c index 5c21b22..1e5bc9c 100644 --- a/drivers/hv/vmbus_drv.c +++ b/drivers/hv/vmbus_drv.c @@ -47,7 +47,6 @@ static struct completion probe_event; static int irq; struct hv_device_info { - u32 chn_state; uuid_le chn_type; uuid_le chn_instance; @@ -82,7 +81,6 @@ static void get_channel_info(struct hv_device *device, vmbus_get_debug_info(device->channel, &debug_info); - info->chn_state = debug_info.state; memcpy(&info->chn_type, &debug_info.interfacetype, sizeof(uuid_le)); memcpy(&info->chn_instance, &debug_info.interface_instance, @@ -152,8 +150,6 @@ static ssize_t vmbus_show_device_attr(struct device *dev, } else if (!strcmp(dev_attr->attr.name, "modalias")) { print_alias_name(hv_dev, alias_name); ret = sprintf(buf, "vmbus:%s\n", alias_name); - } else if (!strcmp(dev_attr->attr.name, "state")) { - ret = sprintf(buf, "%d\n", device_info->chn_state); } else if (!strcmp(dev_attr->attr.name, "out_intr_mask")) { ret = sprintf(buf, "%d\n", device_info->outbound.int_mask); } else if (!strcmp(dev_attr->attr.name, "out_read_index")) { @@ -211,15 +207,26 @@ static ssize_t id_show(struct device *dev, struct device_attribute *dev_attr, } static DEVICE_ATTR_RO(id); +static ssize_t state_show(struct device *dev, struct device_attribute *dev_attr, + char *buf) +{ + struct hv_device *hv_dev = device_to_hv_device(dev); + + if (!hv_dev->channel) + return -ENODEV; + return sprintf(buf, "%d\n", hv_dev->channel->state); +} +static DEVICE_ATTR_RO(state); + static struct attribute *vmbus_attrs[] = { &dev_attr_id.attr, + &dev_attr_state.attr, NULL, }; ATTRIBUTE_GROUPS(vmbus); /* Set up per device attributes in /sys/bus/vmbus/devices/ */ static struct device_attribute vmbus_device_attrs[] = { - __ATTR(state, S_IRUGO, vmbus_show_device_attr, NULL), __ATTR(class_id, S_IRUGO, vmbus_show_device_attr, NULL), __ATTR(device_id, S_IRUGO, vmbus_show_device_attr, NULL), __ATTR(monitor_id, S_IRUGO, vmbus_show_device_attr, NULL), diff --git a/include/linux/hyperv.h b/include/linux/hyperv.h index 45e9b65..b350a8c 100644 --- a/include/linux/hyperv.h +++ b/include/linux/hyperv.h @@ -900,7 +900,6 @@ enum vmbus_channel_state { }; struct vmbus_channel_debug_info { - enum vmbus_channel_state state; uuid_le interfacetype; uuid_le interface_instance; u32 monitorid; -- cgit v0.10.2 From 5ffd00e241ea30fdd907d56419170a1e1338664c Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Fri, 13 Sep 2013 11:32:51 -0700 Subject: hv: move "monitor_id" bus attribute to dev_groups This moves the "state" bus attribute to the dev_groups structure, removing the need for it to be in a temporary structure. Tested-by: "K. Y. Srinivasan" Cc: Haiyang Zhang Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/hv/channel.c b/drivers/hv/channel.c index 64f19f2..f26a5d2 100644 --- a/drivers/hv/channel.c +++ b/drivers/hv/channel.c @@ -77,8 +77,6 @@ void vmbus_get_debug_info(struct vmbus_channel *channel, monitorpage = (struct hv_monitor_page *)vmbus_connection.monitor_pages; - debuginfo->monitorid = channel->offermsg.monitorid; - debuginfo->servermonitor_pending = monitorpage->trigger_group[monitor_group].pending; debuginfo->servermonitor_latency = diff --git a/drivers/hv/vmbus_drv.c b/drivers/hv/vmbus_drv.c index 1e5bc9c..2114ecb 100644 --- a/drivers/hv/vmbus_drv.c +++ b/drivers/hv/vmbus_drv.c @@ -50,7 +50,6 @@ struct hv_device_info { uuid_le chn_type; uuid_le chn_instance; - u32 monitor_id; u32 server_monitor_pending; u32 server_monitor_latency; u32 server_monitor_conn_id; @@ -86,8 +85,6 @@ static void get_channel_info(struct hv_device *device, memcpy(&info->chn_instance, &debug_info.interface_instance, sizeof(uuid_le)); - info->monitor_id = debug_info.monitorid; - info->server_monitor_pending = debug_info.servermonitor_pending; info->server_monitor_latency = debug_info.servermonitor_latency; info->server_monitor_conn_id = debug_info.servermonitor_connectionid; @@ -174,8 +171,6 @@ static ssize_t vmbus_show_device_attr(struct device *dev, } else if (!strcmp(dev_attr->attr.name, "in_write_bytes_avail")) { ret = sprintf(buf, "%d\n", device_info->inbound.bytes_avail_towrite); - } else if (!strcmp(dev_attr->attr.name, "monitor_id")) { - ret = sprintf(buf, "%d\n", device_info->monitor_id); } else if (!strcmp(dev_attr->attr.name, "server_monitor_pending")) { ret = sprintf(buf, "%d\n", device_info->server_monitor_pending); } else if (!strcmp(dev_attr->attr.name, "server_monitor_latency")) { @@ -218,9 +213,21 @@ static ssize_t state_show(struct device *dev, struct device_attribute *dev_attr, } static DEVICE_ATTR_RO(state); +static ssize_t monitor_id_show(struct device *dev, + struct device_attribute *dev_attr, char *buf) +{ + struct hv_device *hv_dev = device_to_hv_device(dev); + + if (!hv_dev->channel) + return -ENODEV; + return sprintf(buf, "%d\n", hv_dev->channel->offermsg.monitorid); +} +static DEVICE_ATTR_RO(monitor_id); + static struct attribute *vmbus_attrs[] = { &dev_attr_id.attr, &dev_attr_state.attr, + &dev_attr_monitor_id.attr, NULL, }; ATTRIBUTE_GROUPS(vmbus); @@ -229,7 +236,6 @@ ATTRIBUTE_GROUPS(vmbus); static struct device_attribute vmbus_device_attrs[] = { __ATTR(class_id, S_IRUGO, vmbus_show_device_attr, NULL), __ATTR(device_id, S_IRUGO, vmbus_show_device_attr, NULL), - __ATTR(monitor_id, S_IRUGO, vmbus_show_device_attr, NULL), __ATTR(modalias, S_IRUGO, vmbus_show_device_attr, NULL), __ATTR(server_monitor_pending, S_IRUGO, vmbus_show_device_attr, NULL), diff --git a/include/linux/hyperv.h b/include/linux/hyperv.h index b350a8c..888a8e5 100644 --- a/include/linux/hyperv.h +++ b/include/linux/hyperv.h @@ -902,7 +902,6 @@ enum vmbus_channel_state { struct vmbus_channel_debug_info { uuid_le interfacetype; uuid_le interface_instance; - u32 monitorid; u32 servermonitor_pending; u32 servermonitor_latency; u32 servermonitor_connectionid; -- cgit v0.10.2 From 647fa371c404606f82fb1c043c87807b5df48f2c Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Fri, 13 Sep 2013 11:32:52 -0700 Subject: hv: move "modalias" bus attribute to dev_groups This moves the "state" bus attribute to the dev_groups structure. Tested-by: "K. Y. Srinivasan" Cc: Haiyang Zhang Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/hv/vmbus_drv.c b/drivers/hv/vmbus_drv.c index 2114ecb..e3f4370 100644 --- a/drivers/hv/vmbus_drv.c +++ b/drivers/hv/vmbus_drv.c @@ -131,7 +131,6 @@ static ssize_t vmbus_show_device_attr(struct device *dev, { struct hv_device *hv_dev = device_to_hv_device(dev); struct hv_device_info *device_info; - char alias_name[VMBUS_ALIAS_LEN + 1]; int ret = 0; device_info = kzalloc(sizeof(struct hv_device_info), GFP_KERNEL); @@ -144,9 +143,6 @@ static ssize_t vmbus_show_device_attr(struct device *dev, ret = sprintf(buf, "{%pUl}\n", device_info->chn_type.b); } else if (!strcmp(dev_attr->attr.name, "device_id")) { ret = sprintf(buf, "{%pUl}\n", device_info->chn_instance.b); - } else if (!strcmp(dev_attr->attr.name, "modalias")) { - print_alias_name(hv_dev, alias_name); - ret = sprintf(buf, "vmbus:%s\n", alias_name); } else if (!strcmp(dev_attr->attr.name, "out_intr_mask")) { ret = sprintf(buf, "%d\n", device_info->outbound.int_mask); } else if (!strcmp(dev_attr->attr.name, "out_read_index")) { @@ -224,10 +220,22 @@ static ssize_t monitor_id_show(struct device *dev, } static DEVICE_ATTR_RO(monitor_id); +static ssize_t modalias_show(struct device *dev, + struct device_attribute *dev_attr, char *buf) +{ + struct hv_device *hv_dev = device_to_hv_device(dev); + char alias_name[VMBUS_ALIAS_LEN + 1]; + + print_alias_name(hv_dev, alias_name); + return sprintf(buf, "vmbus:%s\n", alias_name); +} +static DEVICE_ATTR_RO(modalias); + static struct attribute *vmbus_attrs[] = { &dev_attr_id.attr, &dev_attr_state.attr, &dev_attr_monitor_id.attr, + &dev_attr_modalias.attr, NULL, }; ATTRIBUTE_GROUPS(vmbus); @@ -236,7 +244,6 @@ ATTRIBUTE_GROUPS(vmbus); static struct device_attribute vmbus_device_attrs[] = { __ATTR(class_id, S_IRUGO, vmbus_show_device_attr, NULL), __ATTR(device_id, S_IRUGO, vmbus_show_device_attr, NULL), - __ATTR(modalias, S_IRUGO, vmbus_show_device_attr, NULL), __ATTR(server_monitor_pending, S_IRUGO, vmbus_show_device_attr, NULL), __ATTR(server_monitor_latency, S_IRUGO, vmbus_show_device_attr, NULL), -- cgit v0.10.2 From 68234c049cc1637a8898ebb3743c8587560929b7 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Fri, 13 Sep 2013 11:32:53 -0700 Subject: hv: move "class_id" bus attribute to dev_groups This moves the "class_id" bus attribute to the dev_groups structure, removing the need for it to be in a temporary structure. Tested-by: "K. Y. Srinivasan" Cc: Haiyang Zhang Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/hv/channel.c b/drivers/hv/channel.c index f26a5d2..df9499c 100644 --- a/drivers/hv/channel.c +++ b/drivers/hv/channel.c @@ -69,8 +69,6 @@ void vmbus_get_debug_info(struct vmbus_channel *channel, u8 monitor_group = (u8)channel->offermsg.monitorid / 32; u8 monitor_offset = (u8)channel->offermsg.monitorid % 32; - memcpy(&debuginfo->interfacetype, - &channel->offermsg.offer.if_type, sizeof(uuid_le)); memcpy(&debuginfo->interface_instance, &channel->offermsg.offer.if_instance, sizeof(uuid_le)); diff --git a/drivers/hv/vmbus_drv.c b/drivers/hv/vmbus_drv.c index e3f4370..48258ae 100644 --- a/drivers/hv/vmbus_drv.c +++ b/drivers/hv/vmbus_drv.c @@ -47,7 +47,6 @@ static struct completion probe_event; static int irq; struct hv_device_info { - uuid_le chn_type; uuid_le chn_instance; u32 server_monitor_pending; @@ -80,8 +79,6 @@ static void get_channel_info(struct hv_device *device, vmbus_get_debug_info(device->channel, &debug_info); - memcpy(&info->chn_type, &debug_info.interfacetype, - sizeof(uuid_le)); memcpy(&info->chn_instance, &debug_info.interface_instance, sizeof(uuid_le)); @@ -139,9 +136,7 @@ static ssize_t vmbus_show_device_attr(struct device *dev, get_channel_info(hv_dev, device_info); - if (!strcmp(dev_attr->attr.name, "class_id")) { - ret = sprintf(buf, "{%pUl}\n", device_info->chn_type.b); - } else if (!strcmp(dev_attr->attr.name, "device_id")) { + if (!strcmp(dev_attr->attr.name, "device_id")) { ret = sprintf(buf, "{%pUl}\n", device_info->chn_instance.b); } else if (!strcmp(dev_attr->attr.name, "out_intr_mask")) { ret = sprintf(buf, "%d\n", device_info->outbound.int_mask); @@ -220,6 +215,18 @@ static ssize_t monitor_id_show(struct device *dev, } static DEVICE_ATTR_RO(monitor_id); +static ssize_t class_id_show(struct device *dev, + struct device_attribute *dev_attr, char *buf) +{ + struct hv_device *hv_dev = device_to_hv_device(dev); + + if (!hv_dev->channel) + return -ENODEV; + return sprintf(buf, "{%pUl}\n", + hv_dev->channel->offermsg.offer.if_type.b); +} +static DEVICE_ATTR_RO(class_id); + static ssize_t modalias_show(struct device *dev, struct device_attribute *dev_attr, char *buf) { @@ -231,10 +238,12 @@ static ssize_t modalias_show(struct device *dev, } static DEVICE_ATTR_RO(modalias); + static struct attribute *vmbus_attrs[] = { &dev_attr_id.attr, &dev_attr_state.attr, &dev_attr_monitor_id.attr, + &dev_attr_class_id.attr, &dev_attr_modalias.attr, NULL, }; @@ -242,7 +251,6 @@ ATTRIBUTE_GROUPS(vmbus); /* Set up per device attributes in /sys/bus/vmbus/devices/ */ static struct device_attribute vmbus_device_attrs[] = { - __ATTR(class_id, S_IRUGO, vmbus_show_device_attr, NULL), __ATTR(device_id, S_IRUGO, vmbus_show_device_attr, NULL), __ATTR(server_monitor_pending, S_IRUGO, vmbus_show_device_attr, NULL), diff --git a/include/linux/hyperv.h b/include/linux/hyperv.h index 888a8e5..8ccf6f6 100644 --- a/include/linux/hyperv.h +++ b/include/linux/hyperv.h @@ -900,7 +900,6 @@ enum vmbus_channel_state { }; struct vmbus_channel_debug_info { - uuid_le interfacetype; uuid_le interface_instance; u32 servermonitor_pending; u32 servermonitor_latency; -- cgit v0.10.2 From 7c55e1d0e64cf5acd8cf4a25927e2c7c322063aa Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Fri, 13 Sep 2013 11:32:54 -0700 Subject: hv: move "device_id" bus attribute to dev_groups This moves the "device_id" bus attribute to the dev_groups structure, removing the need for it to be in a temporary structure. Tested-by: "K. Y. Srinivasan" Cc: Haiyang Zhang Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/hv/channel.c b/drivers/hv/channel.c index df9499c..dde30b4 100644 --- a/drivers/hv/channel.c +++ b/drivers/hv/channel.c @@ -69,10 +69,6 @@ void vmbus_get_debug_info(struct vmbus_channel *channel, u8 monitor_group = (u8)channel->offermsg.monitorid / 32; u8 monitor_offset = (u8)channel->offermsg.monitorid % 32; - memcpy(&debuginfo->interface_instance, - &channel->offermsg.offer.if_instance, - sizeof(uuid_le)); - monitorpage = (struct hv_monitor_page *)vmbus_connection.monitor_pages; debuginfo->servermonitor_pending = diff --git a/drivers/hv/vmbus_drv.c b/drivers/hv/vmbus_drv.c index 48258ae..944dc4b 100644 --- a/drivers/hv/vmbus_drv.c +++ b/drivers/hv/vmbus_drv.c @@ -47,8 +47,6 @@ static struct completion probe_event; static int irq; struct hv_device_info { - uuid_le chn_instance; - u32 server_monitor_pending; u32 server_monitor_latency; u32 server_monitor_conn_id; @@ -79,9 +77,6 @@ static void get_channel_info(struct hv_device *device, vmbus_get_debug_info(device->channel, &debug_info); - memcpy(&info->chn_instance, &debug_info.interface_instance, - sizeof(uuid_le)); - info->server_monitor_pending = debug_info.servermonitor_pending; info->server_monitor_latency = debug_info.servermonitor_latency; info->server_monitor_conn_id = debug_info.servermonitor_connectionid; @@ -136,9 +131,7 @@ static ssize_t vmbus_show_device_attr(struct device *dev, get_channel_info(hv_dev, device_info); - if (!strcmp(dev_attr->attr.name, "device_id")) { - ret = sprintf(buf, "{%pUl}\n", device_info->chn_instance.b); - } else if (!strcmp(dev_attr->attr.name, "out_intr_mask")) { + if (!strcmp(dev_attr->attr.name, "out_intr_mask")) { ret = sprintf(buf, "%d\n", device_info->outbound.int_mask); } else if (!strcmp(dev_attr->attr.name, "out_read_index")) { ret = sprintf(buf, "%d\n", device_info->outbound.read_idx); @@ -227,6 +220,18 @@ static ssize_t class_id_show(struct device *dev, } static DEVICE_ATTR_RO(class_id); +static ssize_t device_id_show(struct device *dev, + struct device_attribute *dev_attr, char *buf) +{ + struct hv_device *hv_dev = device_to_hv_device(dev); + + if (!hv_dev->channel) + return -ENODEV; + return sprintf(buf, "{%pUl}\n", + hv_dev->channel->offermsg.offer.if_instance.b); +} +static DEVICE_ATTR_RO(device_id); + static ssize_t modalias_show(struct device *dev, struct device_attribute *dev_attr, char *buf) { @@ -244,6 +249,7 @@ static struct attribute *vmbus_attrs[] = { &dev_attr_state.attr, &dev_attr_monitor_id.attr, &dev_attr_class_id.attr, + &dev_attr_device_id.attr, &dev_attr_modalias.attr, NULL, }; @@ -251,8 +257,6 @@ ATTRIBUTE_GROUPS(vmbus); /* Set up per device attributes in /sys/bus/vmbus/devices/ */ static struct device_attribute vmbus_device_attrs[] = { - __ATTR(device_id, S_IRUGO, vmbus_show_device_attr, NULL), - __ATTR(server_monitor_pending, S_IRUGO, vmbus_show_device_attr, NULL), __ATTR(server_monitor_latency, S_IRUGO, vmbus_show_device_attr, NULL), __ATTR(server_monitor_conn_id, S_IRUGO, vmbus_show_device_attr, NULL), diff --git a/include/linux/hyperv.h b/include/linux/hyperv.h index 8ccf6f6..687c01b 100644 --- a/include/linux/hyperv.h +++ b/include/linux/hyperv.h @@ -900,7 +900,6 @@ enum vmbus_channel_state { }; struct vmbus_channel_debug_info { - uuid_le interface_instance; u32 servermonitor_pending; u32 servermonitor_latency; u32 servermonitor_connectionid; -- cgit v0.10.2 From 8681db445ed407167cd0d78c48727773b132367b Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Fri, 13 Sep 2013 11:32:55 -0700 Subject: hv: make "monitor_pages" a "real" pointer array monitor_pages was a void pointer, containing an unknown number of arrays that we just "knew" were a child and parent array of a specific size. Instead of that implicit knowledge, let's make them a real pointer, allowing us to have type safety, and a semblance of sane addressing schemes. Tested-by: "K. Y. Srinivasan" Cc: Haiyang Zhang Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/hv/channel.c b/drivers/hv/channel.c index dde30b4..04bf065 100644 --- a/drivers/hv/channel.c +++ b/drivers/hv/channel.c @@ -47,8 +47,8 @@ static void vmbus_setevent(struct vmbus_channel *channel) (unsigned long *) vmbus_connection.send_int_page + (channel->offermsg.child_relid >> 5)); - monitorpage = vmbus_connection.monitor_pages; - monitorpage++; /* Get the child to parent monitor page */ + /* Get the child to parent monitor page */ + monitorpage = vmbus_connection.monitor_pages[1]; sync_set_bit(channel->monitor_bit, (unsigned long *)&monitorpage->trigger_group @@ -69,8 +69,7 @@ void vmbus_get_debug_info(struct vmbus_channel *channel, u8 monitor_group = (u8)channel->offermsg.monitorid / 32; u8 monitor_offset = (u8)channel->offermsg.monitorid % 32; - monitorpage = (struct hv_monitor_page *)vmbus_connection.monitor_pages; - + monitorpage = vmbus_connection.monitor_pages[0]; debuginfo->servermonitor_pending = monitorpage->trigger_group[monitor_group].pending; debuginfo->servermonitor_latency = @@ -79,8 +78,7 @@ void vmbus_get_debug_info(struct vmbus_channel *channel, monitorpage->parameter[monitor_group] [monitor_offset].connectionid.u.id; - monitorpage++; - + monitorpage = vmbus_connection.monitor_pages[1]; debuginfo->clientmonitor_pending = monitorpage->trigger_group[monitor_group].pending; debuginfo->clientmonitor_latency = diff --git a/drivers/hv/connection.c b/drivers/hv/connection.c index 8f4743a..4faea97 100644 --- a/drivers/hv/connection.c +++ b/drivers/hv/connection.c @@ -76,10 +76,8 @@ static int vmbus_negotiate_version(struct vmbus_channel_msginfo *msginfo, msg->header.msgtype = CHANNELMSG_INITIATE_CONTACT; msg->vmbus_version_requested = version; msg->interrupt_page = virt_to_phys(vmbus_connection.int_page); - msg->monitor_page1 = virt_to_phys(vmbus_connection.monitor_pages); - msg->monitor_page2 = virt_to_phys( - (void *)((unsigned long)vmbus_connection.monitor_pages + - PAGE_SIZE)); + msg->monitor_page1 = virt_to_phys(vmbus_connection.monitor_pages[0]); + msg->monitor_page2 = virt_to_phys(vmbus_connection.monitor_pages[1]); /* * Add to list before we send the request since we may @@ -169,9 +167,10 @@ int vmbus_connect(void) * Setup the monitor notification facility. The 1st page for * parent->child and the 2nd page for child->parent */ - vmbus_connection.monitor_pages = - (void *)__get_free_pages((GFP_KERNEL|__GFP_ZERO), 1); - if (vmbus_connection.monitor_pages == NULL) { + vmbus_connection.monitor_pages[0] = (void *)__get_free_pages((GFP_KERNEL|__GFP_ZERO), 0); + vmbus_connection.monitor_pages[1] = (void *)__get_free_pages((GFP_KERNEL|__GFP_ZERO), 0); + if ((vmbus_connection.monitor_pages[0] == NULL) || + (vmbus_connection.monitor_pages[1] == NULL)) { ret = -ENOMEM; goto cleanup; } @@ -229,10 +228,10 @@ cleanup: vmbus_connection.int_page = NULL; } - if (vmbus_connection.monitor_pages) { - free_pages((unsigned long)vmbus_connection.monitor_pages, 1); - vmbus_connection.monitor_pages = NULL; - } + free_pages((unsigned long)vmbus_connection.monitor_pages[0], 1); + free_pages((unsigned long)vmbus_connection.monitor_pages[1], 1); + vmbus_connection.monitor_pages[0] = NULL; + vmbus_connection.monitor_pages[1] = NULL; kfree(msginfo); diff --git a/drivers/hv/hyperv_vmbus.h b/drivers/hv/hyperv_vmbus.h index d84918f..d58c22f 100644 --- a/drivers/hv/hyperv_vmbus.h +++ b/drivers/hv/hyperv_vmbus.h @@ -612,7 +612,7 @@ struct vmbus_connection { * 2 pages - 1st page for parent->child notification and 2nd * is child->parent notification */ - void *monitor_pages; + struct hv_monitor_page *monitor_pages[2]; struct list_head chn_msg_list; spinlock_t channelmsg_lock; -- cgit v0.10.2 From 76c52bbe5e5ffc6812dcd49729c09f5a207b4a9a Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Fri, 13 Sep 2013 11:32:56 -0700 Subject: hv: move "client/server_monitor_pending" bus attributes to dev_groups This moves the "client_monitor_pending" and "server_monitor_pending" bus attributes to the dev_groups structure, removing the need for it to be in a temporary structure. Tested-by: "K. Y. Srinivasan" Cc: Haiyang Zhang Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/hv/channel.c b/drivers/hv/channel.c index 04bf065..d600360 100644 --- a/drivers/hv/channel.c +++ b/drivers/hv/channel.c @@ -70,8 +70,6 @@ void vmbus_get_debug_info(struct vmbus_channel *channel, u8 monitor_offset = (u8)channel->offermsg.monitorid % 32; monitorpage = vmbus_connection.monitor_pages[0]; - debuginfo->servermonitor_pending = - monitorpage->trigger_group[monitor_group].pending; debuginfo->servermonitor_latency = monitorpage->latency[monitor_group][monitor_offset]; debuginfo->servermonitor_connectionid = @@ -79,8 +77,6 @@ void vmbus_get_debug_info(struct vmbus_channel *channel, [monitor_offset].connectionid.u.id; monitorpage = vmbus_connection.monitor_pages[1]; - debuginfo->clientmonitor_pending = - monitorpage->trigger_group[monitor_group].pending; debuginfo->clientmonitor_latency = monitorpage->latency[monitor_group][monitor_offset]; debuginfo->clientmonitor_connectionid = diff --git a/drivers/hv/vmbus_drv.c b/drivers/hv/vmbus_drv.c index 944dc4b..5a44957 100644 --- a/drivers/hv/vmbus_drv.c +++ b/drivers/hv/vmbus_drv.c @@ -47,10 +47,8 @@ static struct completion probe_event; static int irq; struct hv_device_info { - u32 server_monitor_pending; u32 server_monitor_latency; u32 server_monitor_conn_id; - u32 client_monitor_pending; u32 client_monitor_latency; u32 client_monitor_conn_id; @@ -77,11 +75,9 @@ static void get_channel_info(struct hv_device *device, vmbus_get_debug_info(device->channel, &debug_info); - info->server_monitor_pending = debug_info.servermonitor_pending; info->server_monitor_latency = debug_info.servermonitor_latency; info->server_monitor_conn_id = debug_info.servermonitor_connectionid; - info->client_monitor_pending = debug_info.clientmonitor_pending; info->client_monitor_latency = debug_info.clientmonitor_latency; info->client_monitor_conn_id = debug_info.clientmonitor_connectionid; @@ -155,15 +151,11 @@ static ssize_t vmbus_show_device_attr(struct device *dev, } else if (!strcmp(dev_attr->attr.name, "in_write_bytes_avail")) { ret = sprintf(buf, "%d\n", device_info->inbound.bytes_avail_towrite); - } else if (!strcmp(dev_attr->attr.name, "server_monitor_pending")) { - ret = sprintf(buf, "%d\n", device_info->server_monitor_pending); } else if (!strcmp(dev_attr->attr.name, "server_monitor_latency")) { ret = sprintf(buf, "%d\n", device_info->server_monitor_latency); } else if (!strcmp(dev_attr->attr.name, "server_monitor_conn_id")) { ret = sprintf(buf, "%d\n", device_info->server_monitor_conn_id); - } else if (!strcmp(dev_attr->attr.name, "client_monitor_pending")) { - ret = sprintf(buf, "%d\n", device_info->client_monitor_pending); } else if (!strcmp(dev_attr->attr.name, "client_monitor_latency")) { ret = sprintf(buf, "%d\n", device_info->client_monitor_latency); } else if (!strcmp(dev_attr->attr.name, "client_monitor_conn_id")) { @@ -175,6 +167,23 @@ static ssize_t vmbus_show_device_attr(struct device *dev, return ret; } +static u8 channel_monitor_group(struct vmbus_channel *channel) +{ + return (u8)channel->offermsg.monitorid / 32; +} + +static u8 channel_monitor_offset(struct vmbus_channel *channel) +{ + return (u8)channel->offermsg.monitorid % 32; +} + +static u32 channel_pending(struct vmbus_channel *channel, + struct hv_monitor_page *monitor_page) +{ + u8 monitor_group = channel_monitor_group(channel); + return monitor_page->trigger_group[monitor_group].pending; +} + static ssize_t id_show(struct device *dev, struct device_attribute *dev_attr, char *buf) { @@ -243,6 +252,33 @@ static ssize_t modalias_show(struct device *dev, } static DEVICE_ATTR_RO(modalias); +static ssize_t server_monitor_pending_show(struct device *dev, + struct device_attribute *dev_attr, + char *buf) +{ + struct hv_device *hv_dev = device_to_hv_device(dev); + + if (!hv_dev->channel) + return -ENODEV; + return sprintf(buf, "%d\n", + channel_pending(hv_dev->channel, + vmbus_connection.monitor_pages[1])); +} +static DEVICE_ATTR_RO(server_monitor_pending); + +static ssize_t client_monitor_pending_show(struct device *dev, + struct device_attribute *dev_attr, + char *buf) +{ + struct hv_device *hv_dev = device_to_hv_device(dev); + + if (!hv_dev->channel) + return -ENODEV; + return sprintf(buf, "%d\n", + channel_pending(hv_dev->channel, + vmbus_connection.monitor_pages[1])); +} +static DEVICE_ATTR_RO(client_monitor_pending); static struct attribute *vmbus_attrs[] = { &dev_attr_id.attr, @@ -251,17 +287,17 @@ static struct attribute *vmbus_attrs[] = { &dev_attr_class_id.attr, &dev_attr_device_id.attr, &dev_attr_modalias.attr, + &dev_attr_server_monitor_pending.attr, + &dev_attr_client_monitor_pending.attr, NULL, }; ATTRIBUTE_GROUPS(vmbus); /* Set up per device attributes in /sys/bus/vmbus/devices/ */ static struct device_attribute vmbus_device_attrs[] = { - __ATTR(server_monitor_pending, S_IRUGO, vmbus_show_device_attr, NULL), __ATTR(server_monitor_latency, S_IRUGO, vmbus_show_device_attr, NULL), __ATTR(server_monitor_conn_id, S_IRUGO, vmbus_show_device_attr, NULL), - __ATTR(client_monitor_pending, S_IRUGO, vmbus_show_device_attr, NULL), __ATTR(client_monitor_latency, S_IRUGO, vmbus_show_device_attr, NULL), __ATTR(client_monitor_conn_id, S_IRUGO, vmbus_show_device_attr, NULL), diff --git a/include/linux/hyperv.h b/include/linux/hyperv.h index 687c01b..f0a7d27 100644 --- a/include/linux/hyperv.h +++ b/include/linux/hyperv.h @@ -900,10 +900,8 @@ enum vmbus_channel_state { }; struct vmbus_channel_debug_info { - u32 servermonitor_pending; u32 servermonitor_latency; u32 servermonitor_connectionid; - u32 clientmonitor_pending; u32 clientmonitor_latency; u32 clientmonitor_connectionid; -- cgit v0.10.2 From 1cee272b0249c5007391da6cf42903b8f30dbc5a Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Fri, 13 Sep 2013 11:32:57 -0700 Subject: hv: move "client/server_monitor_latency" bus attributes to dev_groups This moves the "client_monitor_latency" and "server_monitor_latency" bus attributes to the dev_groups structure, removing the need for it to be in a temporary structure. Tested-by: "K. Y. Srinivasan" Cc: Haiyang Zhang Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/hv/channel.c b/drivers/hv/channel.c index d600360..ff61464 100644 --- a/drivers/hv/channel.c +++ b/drivers/hv/channel.c @@ -70,15 +70,11 @@ void vmbus_get_debug_info(struct vmbus_channel *channel, u8 monitor_offset = (u8)channel->offermsg.monitorid % 32; monitorpage = vmbus_connection.monitor_pages[0]; - debuginfo->servermonitor_latency = - monitorpage->latency[monitor_group][monitor_offset]; debuginfo->servermonitor_connectionid = monitorpage->parameter[monitor_group] [monitor_offset].connectionid.u.id; monitorpage = vmbus_connection.monitor_pages[1]; - debuginfo->clientmonitor_latency = - monitorpage->latency[monitor_group][monitor_offset]; debuginfo->clientmonitor_connectionid = monitorpage->parameter[monitor_group] [monitor_offset].connectionid.u.id; diff --git a/drivers/hv/vmbus_drv.c b/drivers/hv/vmbus_drv.c index 5a44957..461b989 100644 --- a/drivers/hv/vmbus_drv.c +++ b/drivers/hv/vmbus_drv.c @@ -47,9 +47,7 @@ static struct completion probe_event; static int irq; struct hv_device_info { - u32 server_monitor_latency; u32 server_monitor_conn_id; - u32 client_monitor_latency; u32 client_monitor_conn_id; struct hv_dev_port_info inbound; @@ -75,10 +73,8 @@ static void get_channel_info(struct hv_device *device, vmbus_get_debug_info(device->channel, &debug_info); - info->server_monitor_latency = debug_info.servermonitor_latency; info->server_monitor_conn_id = debug_info.servermonitor_connectionid; - info->client_monitor_latency = debug_info.clientmonitor_latency; info->client_monitor_conn_id = debug_info.clientmonitor_connectionid; info->inbound.int_mask = debug_info.inbound.current_interrupt_mask; @@ -151,13 +147,9 @@ static ssize_t vmbus_show_device_attr(struct device *dev, } else if (!strcmp(dev_attr->attr.name, "in_write_bytes_avail")) { ret = sprintf(buf, "%d\n", device_info->inbound.bytes_avail_towrite); - } else if (!strcmp(dev_attr->attr.name, "server_monitor_latency")) { - ret = sprintf(buf, "%d\n", device_info->server_monitor_latency); } else if (!strcmp(dev_attr->attr.name, "server_monitor_conn_id")) { ret = sprintf(buf, "%d\n", device_info->server_monitor_conn_id); - } else if (!strcmp(dev_attr->attr.name, "client_monitor_latency")) { - ret = sprintf(buf, "%d\n", device_info->client_monitor_latency); } else if (!strcmp(dev_attr->attr.name, "client_monitor_conn_id")) { ret = sprintf(buf, "%d\n", device_info->client_monitor_conn_id); @@ -184,6 +176,14 @@ static u32 channel_pending(struct vmbus_channel *channel, return monitor_page->trigger_group[monitor_group].pending; } +static u32 channel_latency(struct vmbus_channel *channel, + struct hv_monitor_page *monitor_page) +{ + u8 monitor_group = channel_monitor_group(channel); + u8 monitor_offset = channel_monitor_offset(channel); + return monitor_page->latency[monitor_group][monitor_offset]; +} + static ssize_t id_show(struct device *dev, struct device_attribute *dev_attr, char *buf) { @@ -280,6 +280,34 @@ static ssize_t client_monitor_pending_show(struct device *dev, } static DEVICE_ATTR_RO(client_monitor_pending); +static ssize_t server_monitor_latency_show(struct device *dev, + struct device_attribute *dev_attr, + char *buf) +{ + struct hv_device *hv_dev = device_to_hv_device(dev); + + if (!hv_dev->channel) + return -ENODEV; + return sprintf(buf, "%d\n", + channel_latency(hv_dev->channel, + vmbus_connection.monitor_pages[0])); +} +static DEVICE_ATTR_RO(server_monitor_latency); + +static ssize_t client_monitor_latency_show(struct device *dev, + struct device_attribute *dev_attr, + char *buf) +{ + struct hv_device *hv_dev = device_to_hv_device(dev); + + if (!hv_dev->channel) + return -ENODEV; + return sprintf(buf, "%d\n", + channel_latency(hv_dev->channel, + vmbus_connection.monitor_pages[1])); +} +static DEVICE_ATTR_RO(client_monitor_latency); + static struct attribute *vmbus_attrs[] = { &dev_attr_id.attr, &dev_attr_state.attr, @@ -289,16 +317,16 @@ static struct attribute *vmbus_attrs[] = { &dev_attr_modalias.attr, &dev_attr_server_monitor_pending.attr, &dev_attr_client_monitor_pending.attr, + &dev_attr_server_monitor_latency.attr, + &dev_attr_client_monitor_latency.attr, NULL, }; ATTRIBUTE_GROUPS(vmbus); /* Set up per device attributes in /sys/bus/vmbus/devices/ */ static struct device_attribute vmbus_device_attrs[] = { - __ATTR(server_monitor_latency, S_IRUGO, vmbus_show_device_attr, NULL), __ATTR(server_monitor_conn_id, S_IRUGO, vmbus_show_device_attr, NULL), - __ATTR(client_monitor_latency, S_IRUGO, vmbus_show_device_attr, NULL), __ATTR(client_monitor_conn_id, S_IRUGO, vmbus_show_device_attr, NULL), __ATTR(out_intr_mask, S_IRUGO, vmbus_show_device_attr, NULL), diff --git a/include/linux/hyperv.h b/include/linux/hyperv.h index f0a7d27..ec1e503 100644 --- a/include/linux/hyperv.h +++ b/include/linux/hyperv.h @@ -900,9 +900,7 @@ enum vmbus_channel_state { }; struct vmbus_channel_debug_info { - u32 servermonitor_latency; u32 servermonitor_connectionid; - u32 clientmonitor_latency; u32 clientmonitor_connectionid; struct hv_ring_buffer_debug_info inbound; -- cgit v0.10.2 From 4947c7453b184bc33a0056cf95af61c3cad11ef6 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Fri, 13 Sep 2013 11:32:58 -0700 Subject: hv: move "client/server_monitor_conn_id" bus attributes to dev_groups This moves the "client_monitor_conn_id" and "server_monitor_conn_id" bus attributes to the dev_groups structure, removing the need for it to be in a temporary structure. Tested-by: "K. Y. Srinivasan" Cc: Haiyang Zhang Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/hv/channel.c b/drivers/hv/channel.c index ff61464..75c26da 100644 --- a/drivers/hv/channel.c +++ b/drivers/hv/channel.c @@ -65,20 +65,6 @@ static void vmbus_setevent(struct vmbus_channel *channel) void vmbus_get_debug_info(struct vmbus_channel *channel, struct vmbus_channel_debug_info *debuginfo) { - struct hv_monitor_page *monitorpage; - u8 monitor_group = (u8)channel->offermsg.monitorid / 32; - u8 monitor_offset = (u8)channel->offermsg.monitorid % 32; - - monitorpage = vmbus_connection.monitor_pages[0]; - debuginfo->servermonitor_connectionid = - monitorpage->parameter[monitor_group] - [monitor_offset].connectionid.u.id; - - monitorpage = vmbus_connection.monitor_pages[1]; - debuginfo->clientmonitor_connectionid = - monitorpage->parameter[monitor_group] - [monitor_offset].connectionid.u.id; - hv_ringbuffer_get_debuginfo(&channel->inbound, &debuginfo->inbound); hv_ringbuffer_get_debuginfo(&channel->outbound, &debuginfo->outbound); } diff --git a/drivers/hv/vmbus_drv.c b/drivers/hv/vmbus_drv.c index 461b989..73cb456 100644 --- a/drivers/hv/vmbus_drv.c +++ b/drivers/hv/vmbus_drv.c @@ -47,9 +47,6 @@ static struct completion probe_event; static int irq; struct hv_device_info { - u32 server_monitor_conn_id; - u32 client_monitor_conn_id; - struct hv_dev_port_info inbound; struct hv_dev_port_info outbound; }; @@ -73,10 +70,6 @@ static void get_channel_info(struct hv_device *device, vmbus_get_debug_info(device->channel, &debug_info); - info->server_monitor_conn_id = debug_info.servermonitor_connectionid; - - info->client_monitor_conn_id = debug_info.clientmonitor_connectionid; - info->inbound.int_mask = debug_info.inbound.current_interrupt_mask; info->inbound.read_idx = debug_info.inbound.current_read_index; info->inbound.write_idx = debug_info.inbound.current_write_index; @@ -147,12 +140,6 @@ static ssize_t vmbus_show_device_attr(struct device *dev, } else if (!strcmp(dev_attr->attr.name, "in_write_bytes_avail")) { ret = sprintf(buf, "%d\n", device_info->inbound.bytes_avail_towrite); - } else if (!strcmp(dev_attr->attr.name, "server_monitor_conn_id")) { - ret = sprintf(buf, "%d\n", - device_info->server_monitor_conn_id); - } else if (!strcmp(dev_attr->attr.name, "client_monitor_conn_id")) { - ret = sprintf(buf, "%d\n", - device_info->client_monitor_conn_id); } kfree(device_info); @@ -184,6 +171,14 @@ static u32 channel_latency(struct vmbus_channel *channel, return monitor_page->latency[monitor_group][monitor_offset]; } +static u32 channel_conn_id(struct vmbus_channel *channel, + struct hv_monitor_page *monitor_page) +{ + u8 monitor_group = channel_monitor_group(channel); + u8 monitor_offset = channel_monitor_offset(channel); + return monitor_page->parameter[monitor_group][monitor_offset].connectionid.u.id; +} + static ssize_t id_show(struct device *dev, struct device_attribute *dev_attr, char *buf) { @@ -308,6 +303,34 @@ static ssize_t client_monitor_latency_show(struct device *dev, } static DEVICE_ATTR_RO(client_monitor_latency); +static ssize_t server_monitor_conn_id_show(struct device *dev, + struct device_attribute *dev_attr, + char *buf) +{ + struct hv_device *hv_dev = device_to_hv_device(dev); + + if (!hv_dev->channel) + return -ENODEV; + return sprintf(buf, "%d\n", + channel_conn_id(hv_dev->channel, + vmbus_connection.monitor_pages[0])); +} +static DEVICE_ATTR_RO(server_monitor_conn_id); + +static ssize_t client_monitor_conn_id_show(struct device *dev, + struct device_attribute *dev_attr, + char *buf) +{ + struct hv_device *hv_dev = device_to_hv_device(dev); + + if (!hv_dev->channel) + return -ENODEV; + return sprintf(buf, "%d\n", + channel_conn_id(hv_dev->channel, + vmbus_connection.monitor_pages[1])); +} +static DEVICE_ATTR_RO(client_monitor_conn_id); + static struct attribute *vmbus_attrs[] = { &dev_attr_id.attr, &dev_attr_state.attr, @@ -319,16 +342,14 @@ static struct attribute *vmbus_attrs[] = { &dev_attr_client_monitor_pending.attr, &dev_attr_server_monitor_latency.attr, &dev_attr_client_monitor_latency.attr, + &dev_attr_server_monitor_conn_id.attr, + &dev_attr_client_monitor_conn_id.attr, NULL, }; ATTRIBUTE_GROUPS(vmbus); /* Set up per device attributes in /sys/bus/vmbus/devices/ */ static struct device_attribute vmbus_device_attrs[] = { - __ATTR(server_monitor_conn_id, S_IRUGO, vmbus_show_device_attr, NULL), - - __ATTR(client_monitor_conn_id, S_IRUGO, vmbus_show_device_attr, NULL), - __ATTR(out_intr_mask, S_IRUGO, vmbus_show_device_attr, NULL), __ATTR(out_read_index, S_IRUGO, vmbus_show_device_attr, NULL), __ATTR(out_write_index, S_IRUGO, vmbus_show_device_attr, NULL), diff --git a/include/linux/hyperv.h b/include/linux/hyperv.h index ec1e503..332e80c 100644 --- a/include/linux/hyperv.h +++ b/include/linux/hyperv.h @@ -900,9 +900,6 @@ enum vmbus_channel_state { }; struct vmbus_channel_debug_info { - u32 servermonitor_connectionid; - u32 clientmonitor_connectionid; - struct hv_ring_buffer_debug_info inbound; struct hv_ring_buffer_debug_info outbound; }; -- cgit v0.10.2 From 2c9be3eacc39948af2341595322c014833699ac5 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Fri, 13 Sep 2013 11:32:59 -0700 Subject: hv: delete vmbus_get_debug_info() It's only used once, only contains 2 function calls, so just make those calls directly, deleting the function, and the now unneeded structure entirely. Tested-by: "K. Y. Srinivasan" Cc: Haiyang Zhang Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/hv/channel.c b/drivers/hv/channel.c index 75c26da..94d5459 100644 --- a/drivers/hv/channel.c +++ b/drivers/hv/channel.c @@ -60,16 +60,6 @@ static void vmbus_setevent(struct vmbus_channel *channel) } /* - * vmbus_get_debug_info -Retrieve various channel debug info - */ -void vmbus_get_debug_info(struct vmbus_channel *channel, - struct vmbus_channel_debug_info *debuginfo) -{ - hv_ringbuffer_get_debuginfo(&channel->inbound, &debuginfo->inbound); - hv_ringbuffer_get_debuginfo(&channel->outbound, &debuginfo->outbound); -} - -/* * vmbus_open - Open the specified channel. */ int vmbus_open(struct vmbus_channel *newchannel, u32 send_ringbuffer_size, diff --git a/drivers/hv/vmbus_drv.c b/drivers/hv/vmbus_drv.c index 73cb456..62d9311 100644 --- a/drivers/hv/vmbus_drv.c +++ b/drivers/hv/vmbus_drv.c @@ -63,29 +63,26 @@ static int vmbus_exists(void) static void get_channel_info(struct hv_device *device, struct hv_device_info *info) { - struct vmbus_channel_debug_info debug_info; + struct hv_ring_buffer_debug_info inbound; + struct hv_ring_buffer_debug_info outbound; if (!device->channel) return; - vmbus_get_debug_info(device->channel, &debug_info); - - info->inbound.int_mask = debug_info.inbound.current_interrupt_mask; - info->inbound.read_idx = debug_info.inbound.current_read_index; - info->inbound.write_idx = debug_info.inbound.current_write_index; - info->inbound.bytes_avail_toread = - debug_info.inbound.bytes_avail_toread; - info->inbound.bytes_avail_towrite = - debug_info.inbound.bytes_avail_towrite; - - info->outbound.int_mask = - debug_info.outbound.current_interrupt_mask; - info->outbound.read_idx = debug_info.outbound.current_read_index; - info->outbound.write_idx = debug_info.outbound.current_write_index; - info->outbound.bytes_avail_toread = - debug_info.outbound.bytes_avail_toread; - info->outbound.bytes_avail_towrite = - debug_info.outbound.bytes_avail_towrite; + hv_ringbuffer_get_debuginfo(&device->channel->inbound, &inbound); + hv_ringbuffer_get_debuginfo(&device->channel->outbound, &outbound); + + info->inbound.int_mask = inbound.current_interrupt_mask; + info->inbound.read_idx = inbound.current_read_index; + info->inbound.write_idx = inbound.current_write_index; + info->inbound.bytes_avail_toread = inbound.bytes_avail_toread; + info->inbound.bytes_avail_towrite = inbound.bytes_avail_towrite; + + info->outbound.int_mask = outbound.current_interrupt_mask; + info->outbound.read_idx = outbound.current_read_index; + info->outbound.write_idx = outbound.current_write_index; + info->outbound.bytes_avail_toread = outbound.bytes_avail_toread; + info->outbound.bytes_avail_towrite = outbound.bytes_avail_towrite; } #define VMBUS_ALIAS_LEN ((sizeof((struct hv_vmbus_device_id *)0)->guid) * 2) diff --git a/include/linux/hyperv.h b/include/linux/hyperv.h index 332e80c..c0e8faf 100644 --- a/include/linux/hyperv.h +++ b/include/linux/hyperv.h @@ -899,11 +899,6 @@ enum vmbus_channel_state { CHANNEL_OPENED_STATE, }; -struct vmbus_channel_debug_info { - struct hv_ring_buffer_debug_info inbound; - struct hv_ring_buffer_debug_info outbound; -}; - /* * Represents each channel msg on the vmbus connection This is a * variable-size data structure depending on the msg type itself @@ -1169,9 +1164,6 @@ extern int vmbus_recvpacket_raw(struct vmbus_channel *channel, u64 *requestid); -extern void vmbus_get_debug_info(struct vmbus_channel *channel, - struct vmbus_channel_debug_info *debug); - extern void vmbus_ontimer(unsigned long data); struct hv_dev_port_info { -- cgit v0.10.2 From 1fdde16d1f93376ad2a2df769f756572c2e84cbd Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Fri, 13 Sep 2013 11:33:00 -0700 Subject: hv: delete struct hv_dev_port_info It's no longer needed, and the struct hv_ring_buffer_debug_info structure shouldn't be "global" so move it to the local .h file instead. Tested-by: "K. Y. Srinivasan" Cc: Haiyang Zhang Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/hv/hyperv_vmbus.h b/drivers/hv/hyperv_vmbus.h index d58c22f..e055176 100644 --- a/drivers/hv/hyperv_vmbus.h +++ b/drivers/hv/hyperv_vmbus.h @@ -514,6 +514,13 @@ struct hv_context { extern struct hv_context hv_context; +struct hv_ring_buffer_debug_info { + u32 current_interrupt_mask; + u32 current_read_index; + u32 current_write_index; + u32 bytes_avail_toread; + u32 bytes_avail_towrite; +}; /* Hv Interface */ diff --git a/drivers/hv/vmbus_drv.c b/drivers/hv/vmbus_drv.c index 62d9311..cf3220e 100644 --- a/drivers/hv/vmbus_drv.c +++ b/drivers/hv/vmbus_drv.c @@ -47,8 +47,8 @@ static struct completion probe_event; static int irq; struct hv_device_info { - struct hv_dev_port_info inbound; - struct hv_dev_port_info outbound; + struct hv_ring_buffer_debug_info inbound; + struct hv_ring_buffer_debug_info outbound; }; static int vmbus_exists(void) @@ -63,26 +63,11 @@ static int vmbus_exists(void) static void get_channel_info(struct hv_device *device, struct hv_device_info *info) { - struct hv_ring_buffer_debug_info inbound; - struct hv_ring_buffer_debug_info outbound; - if (!device->channel) return; - hv_ringbuffer_get_debuginfo(&device->channel->inbound, &inbound); - hv_ringbuffer_get_debuginfo(&device->channel->outbound, &outbound); - - info->inbound.int_mask = inbound.current_interrupt_mask; - info->inbound.read_idx = inbound.current_read_index; - info->inbound.write_idx = inbound.current_write_index; - info->inbound.bytes_avail_toread = inbound.bytes_avail_toread; - info->inbound.bytes_avail_towrite = inbound.bytes_avail_towrite; - - info->outbound.int_mask = outbound.current_interrupt_mask; - info->outbound.read_idx = outbound.current_read_index; - info->outbound.write_idx = outbound.current_write_index; - info->outbound.bytes_avail_toread = outbound.bytes_avail_toread; - info->outbound.bytes_avail_towrite = outbound.bytes_avail_towrite; + hv_ringbuffer_get_debuginfo(&device->channel->inbound, &info->inbound); + hv_ringbuffer_get_debuginfo(&device->channel->outbound, &info->outbound); } #define VMBUS_ALIAS_LEN ((sizeof((struct hv_vmbus_device_id *)0)->guid) * 2) @@ -114,11 +99,11 @@ static ssize_t vmbus_show_device_attr(struct device *dev, get_channel_info(hv_dev, device_info); if (!strcmp(dev_attr->attr.name, "out_intr_mask")) { - ret = sprintf(buf, "%d\n", device_info->outbound.int_mask); + ret = sprintf(buf, "%d\n", device_info->outbound.current_interrupt_mask); } else if (!strcmp(dev_attr->attr.name, "out_read_index")) { - ret = sprintf(buf, "%d\n", device_info->outbound.read_idx); + ret = sprintf(buf, "%d\n", device_info->outbound.current_read_index); } else if (!strcmp(dev_attr->attr.name, "out_write_index")) { - ret = sprintf(buf, "%d\n", device_info->outbound.write_idx); + ret = sprintf(buf, "%d\n", device_info->outbound.current_write_index); } else if (!strcmp(dev_attr->attr.name, "out_read_bytes_avail")) { ret = sprintf(buf, "%d\n", device_info->outbound.bytes_avail_toread); @@ -126,11 +111,11 @@ static ssize_t vmbus_show_device_attr(struct device *dev, ret = sprintf(buf, "%d\n", device_info->outbound.bytes_avail_towrite); } else if (!strcmp(dev_attr->attr.name, "in_intr_mask")) { - ret = sprintf(buf, "%d\n", device_info->inbound.int_mask); + ret = sprintf(buf, "%d\n", device_info->inbound.current_interrupt_mask); } else if (!strcmp(dev_attr->attr.name, "in_read_index")) { - ret = sprintf(buf, "%d\n", device_info->inbound.read_idx); + ret = sprintf(buf, "%d\n", device_info->inbound.current_read_index); } else if (!strcmp(dev_attr->attr.name, "in_write_index")) { - ret = sprintf(buf, "%d\n", device_info->inbound.write_idx); + ret = sprintf(buf, "%d\n", device_info->inbound.current_write_index); } else if (!strcmp(dev_attr->attr.name, "in_read_bytes_avail")) { ret = sprintf(buf, "%d\n", device_info->inbound.bytes_avail_toread); diff --git a/include/linux/hyperv.h b/include/linux/hyperv.h index c0e8faf..c68ecfe 100644 --- a/include/linux/hyperv.h +++ b/include/linux/hyperv.h @@ -429,15 +429,6 @@ struct hv_ring_buffer_info { u32 ring_data_startoffset; }; -struct hv_ring_buffer_debug_info { - u32 current_interrupt_mask; - u32 current_read_index; - u32 current_write_index; - u32 bytes_avail_toread; - u32 bytes_avail_towrite; -}; - - /* * * hv_get_ringbuffer_availbytes() @@ -1166,14 +1157,6 @@ extern int vmbus_recvpacket_raw(struct vmbus_channel *channel, extern void vmbus_ontimer(unsigned long data); -struct hv_dev_port_info { - u32 int_mask; - u32 read_idx; - u32 write_idx; - u32 bytes_avail_toread; - u32 bytes_avail_towrite; -}; - /* Base driver object */ struct hv_driver { const char *name; -- cgit v0.10.2 From 98f4c651762c3b4f4dd300504563075c2b726a89 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Fri, 13 Sep 2013 11:33:01 -0700 Subject: hv: move ringbuffer bus attributes to dev_groups This moves the ringbuffer bus attributes to the dev_groups structure, deletes the now unneeded struct hv_device_info, and removes some now unused functions, and variables as everything is now moved to the dev_groups structure, dev_attrs is no longer needed. Tested-by: "K. Y. Srinivasan" Cc: Haiyang Zhang Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/hv/vmbus_drv.c b/drivers/hv/vmbus_drv.c index cf3220e..48aad4f 100644 --- a/drivers/hv/vmbus_drv.c +++ b/drivers/hv/vmbus_drv.c @@ -46,11 +46,6 @@ static struct tasklet_struct msg_dpc; static struct completion probe_event; static int irq; -struct hv_device_info { - struct hv_ring_buffer_debug_info inbound; - struct hv_ring_buffer_debug_info outbound; -}; - static int vmbus_exists(void) { if (hv_acpi_dev == NULL) @@ -59,17 +54,6 @@ static int vmbus_exists(void) return 0; } - -static void get_channel_info(struct hv_device *device, - struct hv_device_info *info) -{ - if (!device->channel) - return; - - hv_ringbuffer_get_debuginfo(&device->channel->inbound, &info->inbound); - hv_ringbuffer_get_debuginfo(&device->channel->outbound, &info->outbound); -} - #define VMBUS_ALIAS_LEN ((sizeof((struct hv_vmbus_device_id *)0)->guid) * 2) static void print_alias_name(struct hv_device *hv_dev, char *alias_name) { @@ -78,56 +62,6 @@ static void print_alias_name(struct hv_device *hv_dev, char *alias_name) sprintf(&alias_name[i], "%02x", hv_dev->dev_type.b[i/2]); } -/* - * vmbus_show_device_attr - Show the device attribute in sysfs. - * - * This is invoked when user does a - * "cat /sys/bus/vmbus/devices//" - */ -static ssize_t vmbus_show_device_attr(struct device *dev, - struct device_attribute *dev_attr, - char *buf) -{ - struct hv_device *hv_dev = device_to_hv_device(dev); - struct hv_device_info *device_info; - int ret = 0; - - device_info = kzalloc(sizeof(struct hv_device_info), GFP_KERNEL); - if (!device_info) - return ret; - - get_channel_info(hv_dev, device_info); - - if (!strcmp(dev_attr->attr.name, "out_intr_mask")) { - ret = sprintf(buf, "%d\n", device_info->outbound.current_interrupt_mask); - } else if (!strcmp(dev_attr->attr.name, "out_read_index")) { - ret = sprintf(buf, "%d\n", device_info->outbound.current_read_index); - } else if (!strcmp(dev_attr->attr.name, "out_write_index")) { - ret = sprintf(buf, "%d\n", device_info->outbound.current_write_index); - } else if (!strcmp(dev_attr->attr.name, "out_read_bytes_avail")) { - ret = sprintf(buf, "%d\n", - device_info->outbound.bytes_avail_toread); - } else if (!strcmp(dev_attr->attr.name, "out_write_bytes_avail")) { - ret = sprintf(buf, "%d\n", - device_info->outbound.bytes_avail_towrite); - } else if (!strcmp(dev_attr->attr.name, "in_intr_mask")) { - ret = sprintf(buf, "%d\n", device_info->inbound.current_interrupt_mask); - } else if (!strcmp(dev_attr->attr.name, "in_read_index")) { - ret = sprintf(buf, "%d\n", device_info->inbound.current_read_index); - } else if (!strcmp(dev_attr->attr.name, "in_write_index")) { - ret = sprintf(buf, "%d\n", device_info->inbound.current_write_index); - } else if (!strcmp(dev_attr->attr.name, "in_read_bytes_avail")) { - ret = sprintf(buf, "%d\n", - device_info->inbound.bytes_avail_toread); - } else if (!strcmp(dev_attr->attr.name, "in_write_bytes_avail")) { - ret = sprintf(buf, "%d\n", - device_info->inbound.bytes_avail_towrite); - } - - kfree(device_info); - return ret; -} - static u8 channel_monitor_group(struct vmbus_channel *channel) { return (u8)channel->offermsg.monitorid / 32; @@ -313,6 +247,142 @@ static ssize_t client_monitor_conn_id_show(struct device *dev, } static DEVICE_ATTR_RO(client_monitor_conn_id); +static ssize_t out_intr_mask_show(struct device *dev, + struct device_attribute *dev_attr, char *buf) +{ + struct hv_device *hv_dev = device_to_hv_device(dev); + struct hv_ring_buffer_debug_info outbound; + + if (!hv_dev->channel) + return -ENODEV; + hv_ringbuffer_get_debuginfo(&hv_dev->channel->outbound, &outbound); + return sprintf(buf, "%d\n", outbound.current_interrupt_mask); +} +static DEVICE_ATTR_RO(out_intr_mask); + +static ssize_t out_read_index_show(struct device *dev, + struct device_attribute *dev_attr, char *buf) +{ + struct hv_device *hv_dev = device_to_hv_device(dev); + struct hv_ring_buffer_debug_info outbound; + + if (!hv_dev->channel) + return -ENODEV; + hv_ringbuffer_get_debuginfo(&hv_dev->channel->outbound, &outbound); + return sprintf(buf, "%d\n", outbound.current_read_index); +} +static DEVICE_ATTR_RO(out_read_index); + +static ssize_t out_write_index_show(struct device *dev, + struct device_attribute *dev_attr, + char *buf) +{ + struct hv_device *hv_dev = device_to_hv_device(dev); + struct hv_ring_buffer_debug_info outbound; + + if (!hv_dev->channel) + return -ENODEV; + hv_ringbuffer_get_debuginfo(&hv_dev->channel->outbound, &outbound); + return sprintf(buf, "%d\n", outbound.current_write_index); +} +static DEVICE_ATTR_RO(out_write_index); + +static ssize_t out_read_bytes_avail_show(struct device *dev, + struct device_attribute *dev_attr, + char *buf) +{ + struct hv_device *hv_dev = device_to_hv_device(dev); + struct hv_ring_buffer_debug_info outbound; + + if (!hv_dev->channel) + return -ENODEV; + hv_ringbuffer_get_debuginfo(&hv_dev->channel->outbound, &outbound); + return sprintf(buf, "%d\n", outbound.bytes_avail_toread); +} +static DEVICE_ATTR_RO(out_read_bytes_avail); + +static ssize_t out_write_bytes_avail_show(struct device *dev, + struct device_attribute *dev_attr, + char *buf) +{ + struct hv_device *hv_dev = device_to_hv_device(dev); + struct hv_ring_buffer_debug_info outbound; + + if (!hv_dev->channel) + return -ENODEV; + hv_ringbuffer_get_debuginfo(&hv_dev->channel->outbound, &outbound); + return sprintf(buf, "%d\n", outbound.bytes_avail_towrite); +} +static DEVICE_ATTR_RO(out_write_bytes_avail); + +static ssize_t in_intr_mask_show(struct device *dev, + struct device_attribute *dev_attr, char *buf) +{ + struct hv_device *hv_dev = device_to_hv_device(dev); + struct hv_ring_buffer_debug_info inbound; + + if (!hv_dev->channel) + return -ENODEV; + hv_ringbuffer_get_debuginfo(&hv_dev->channel->inbound, &inbound); + return sprintf(buf, "%d\n", inbound.current_interrupt_mask); +} +static DEVICE_ATTR_RO(in_intr_mask); + +static ssize_t in_read_index_show(struct device *dev, + struct device_attribute *dev_attr, char *buf) +{ + struct hv_device *hv_dev = device_to_hv_device(dev); + struct hv_ring_buffer_debug_info inbound; + + if (!hv_dev->channel) + return -ENODEV; + hv_ringbuffer_get_debuginfo(&hv_dev->channel->inbound, &inbound); + return sprintf(buf, "%d\n", inbound.current_read_index); +} +static DEVICE_ATTR_RO(in_read_index); + +static ssize_t in_write_index_show(struct device *dev, + struct device_attribute *dev_attr, char *buf) +{ + struct hv_device *hv_dev = device_to_hv_device(dev); + struct hv_ring_buffer_debug_info inbound; + + if (!hv_dev->channel) + return -ENODEV; + hv_ringbuffer_get_debuginfo(&hv_dev->channel->inbound, &inbound); + return sprintf(buf, "%d\n", inbound.current_write_index); +} +static DEVICE_ATTR_RO(in_write_index); + +static ssize_t in_read_bytes_avail_show(struct device *dev, + struct device_attribute *dev_attr, + char *buf) +{ + struct hv_device *hv_dev = device_to_hv_device(dev); + struct hv_ring_buffer_debug_info inbound; + + if (!hv_dev->channel) + return -ENODEV; + hv_ringbuffer_get_debuginfo(&hv_dev->channel->inbound, &inbound); + return sprintf(buf, "%d\n", inbound.bytes_avail_toread); +} +static DEVICE_ATTR_RO(in_read_bytes_avail); + +static ssize_t in_write_bytes_avail_show(struct device *dev, + struct device_attribute *dev_attr, + char *buf) +{ + struct hv_device *hv_dev = device_to_hv_device(dev); + struct hv_ring_buffer_debug_info inbound; + + if (!hv_dev->channel) + return -ENODEV; + hv_ringbuffer_get_debuginfo(&hv_dev->channel->inbound, &inbound); + return sprintf(buf, "%d\n", inbound.bytes_avail_towrite); +} +static DEVICE_ATTR_RO(in_write_bytes_avail); + +/* Set up per device attributes in /sys/bus/vmbus/devices/ */ static struct attribute *vmbus_attrs[] = { &dev_attr_id.attr, &dev_attr_state.attr, @@ -326,27 +396,20 @@ static struct attribute *vmbus_attrs[] = { &dev_attr_client_monitor_latency.attr, &dev_attr_server_monitor_conn_id.attr, &dev_attr_client_monitor_conn_id.attr, + &dev_attr_out_intr_mask.attr, + &dev_attr_out_read_index.attr, + &dev_attr_out_write_index.attr, + &dev_attr_out_read_bytes_avail.attr, + &dev_attr_out_write_bytes_avail.attr, + &dev_attr_in_intr_mask.attr, + &dev_attr_in_read_index.attr, + &dev_attr_in_write_index.attr, + &dev_attr_in_read_bytes_avail.attr, + &dev_attr_in_write_bytes_avail.attr, NULL, }; ATTRIBUTE_GROUPS(vmbus); -/* Set up per device attributes in /sys/bus/vmbus/devices/ */ -static struct device_attribute vmbus_device_attrs[] = { - __ATTR(out_intr_mask, S_IRUGO, vmbus_show_device_attr, NULL), - __ATTR(out_read_index, S_IRUGO, vmbus_show_device_attr, NULL), - __ATTR(out_write_index, S_IRUGO, vmbus_show_device_attr, NULL), - __ATTR(out_read_bytes_avail, S_IRUGO, vmbus_show_device_attr, NULL), - __ATTR(out_write_bytes_avail, S_IRUGO, vmbus_show_device_attr, NULL), - - __ATTR(in_intr_mask, S_IRUGO, vmbus_show_device_attr, NULL), - __ATTR(in_read_index, S_IRUGO, vmbus_show_device_attr, NULL), - __ATTR(in_write_index, S_IRUGO, vmbus_show_device_attr, NULL), - __ATTR(in_read_bytes_avail, S_IRUGO, vmbus_show_device_attr, NULL), - __ATTR(in_write_bytes_avail, S_IRUGO, vmbus_show_device_attr, NULL), - __ATTR_NULL -}; - - /* * vmbus_uevent - add uevent for our device * @@ -494,7 +557,6 @@ static struct bus_type hv_bus = { .remove = vmbus_remove, .probe = vmbus_probe, .uevent = vmbus_uevent, - .dev_attrs = vmbus_device_attrs, .dev_groups = vmbus_groups, }; -- cgit v0.10.2 From f464a1d00e42b325f70fa33e4e036032fdc62499 Mon Sep 17 00:00:00 2001 From: Daniel Mack Date: Sun, 22 Sep 2013 21:51:46 +0200 Subject: drivers: misc: ti_dac7512: drop module version Providing a module version doesn't add any value, so drop it. Signed-off-by: Daniel Mack Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/misc/ti_dac7512.c b/drivers/misc/ti_dac7512.c index 9b23722..db47333 100644 --- a/drivers/misc/ti_dac7512.c +++ b/drivers/misc/ti_dac7512.c @@ -24,7 +24,6 @@ #include #define DAC7512_DRV_NAME "dac7512" -#define DRIVER_VERSION "1.0" static ssize_t dac7512_store_val(struct device *dev, struct device_attribute *attr, @@ -89,4 +88,3 @@ module_spi_driver(dac7512_driver); MODULE_AUTHOR("Daniel Mack "); MODULE_DESCRIPTION("DAC7512 16-bit DAC"); MODULE_LICENSE("GPL v2"); -MODULE_VERSION(DRIVER_VERSION); -- cgit v0.10.2 From e06465050e2a62ccab164d116e443101279c14bb Mon Sep 17 00:00:00 2001 From: Daniel Mack Date: Sun, 22 Sep 2013 21:51:47 +0200 Subject: drivers: misc: ti_dac7512: drop DAC7512_DRV_NAME The driver's name can be provided directly, so drop the #define. Signed-off-by: Daniel Mack Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/misc/ti_dac7512.c b/drivers/misc/ti_dac7512.c index db47333..46dfbf3 100644 --- a/drivers/misc/ti_dac7512.c +++ b/drivers/misc/ti_dac7512.c @@ -23,8 +23,6 @@ #include #include -#define DAC7512_DRV_NAME "dac7512" - static ssize_t dac7512_store_val(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) @@ -76,7 +74,7 @@ static int dac7512_remove(struct spi_device *spi) static struct spi_driver dac7512_driver = { .driver = { - .name = DAC7512_DRV_NAME, + .name = "dac7512", .owner = THIS_MODULE, }, .probe = dac7512_probe, -- cgit v0.10.2 From beb900fc2428fa812e4a49251ef8396fa46b77fc Mon Sep 17 00:00:00 2001 From: Daniel Mack Date: Sun, 22 Sep 2013 21:51:48 +0200 Subject: drivers: misc: ti_dac7512: provide a SPI ID table This way, the module can be autoloaded by the SPI core. Signed-off-by: Daniel Mack Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/misc/ti_dac7512.c b/drivers/misc/ti_dac7512.c index 46dfbf3..6393a68 100644 --- a/drivers/misc/ti_dac7512.c +++ b/drivers/misc/ti_dac7512.c @@ -72,6 +72,12 @@ static int dac7512_remove(struct spi_device *spi) return 0; } +static const struct spi_device_id dac7512_id_table[] = { + { "dac7512", 0 }, + { } +}; +MODULE_DEVICE_TABLE(spi, dac7512_id_table); + static struct spi_driver dac7512_driver = { .driver = { .name = "dac7512", @@ -79,6 +85,7 @@ static struct spi_driver dac7512_driver = { }, .probe = dac7512_probe, .remove = dac7512_remove, + .id_table = dac7512_id_table, }; module_spi_driver(dac7512_driver); -- cgit v0.10.2 From f91f9258f61f024d34b7c001f989acf8ee39378a Mon Sep 17 00:00:00 2001 From: Daniel Mack Date: Sun, 22 Sep 2013 21:51:49 +0200 Subject: drivers: misc: ti_dac7512: add support for DT matching Only matching is done via DT, no other details can be passed. Signed-off-by: Daniel Mack Signed-off-by: Greg Kroah-Hartman diff --git a/Documentation/devicetree/bindings/misc/ti,dac7512.txt b/Documentation/devicetree/bindings/misc/ti,dac7512.txt new file mode 100644 index 0000000..1db4593 --- /dev/null +++ b/Documentation/devicetree/bindings/misc/ti,dac7512.txt @@ -0,0 +1,20 @@ +TI DAC7512 DEVICETREE BINDINGS + +Required properties: + + - "compatible" Must be set to "ti,dac7512" + +Property rules described in Documentation/devicetree/bindings/spi/spi-bus.txt +apply. In particular, "reg" and "spi-max-frequency" properties must be given. + + +Example: + + spi_master { + dac7512: dac7512@0 { + compatible = "ti,dac7512"; + reg = <0>; /* CS0 */ + spi-max-frequency = <1000000>; + }; + }; + diff --git a/drivers/misc/ti_dac7512.c b/drivers/misc/ti_dac7512.c index 6393a68..83da711 100644 --- a/drivers/misc/ti_dac7512.c +++ b/drivers/misc/ti_dac7512.c @@ -22,6 +22,7 @@ #include #include #include +#include static ssize_t dac7512_store_val(struct device *dev, struct device_attribute *attr, @@ -78,10 +79,19 @@ static const struct spi_device_id dac7512_id_table[] = { }; MODULE_DEVICE_TABLE(spi, dac7512_id_table); +#ifdef CONFIG_OF +static const struct of_device_id dac7512_of_match[] = { + { .compatible = "ti,dac7512", }, + { } +}; +MODULE_DEVICE_TABLE(of, dac7512_of_match); +#endif + static struct spi_driver dac7512_driver = { .driver = { .name = "dac7512", .owner = THIS_MODULE, + .of_match_table = of_match_ptr(dac7512_of_match), }, .probe = dac7512_probe, .remove = dac7512_remove, -- cgit v0.10.2 From a840a72d7211e176937d99a164c2ceab601777c3 Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Mon, 23 Sep 2013 10:35:06 +0900 Subject: misc: tifm: remove unnecessary pci_set_drvdata() The driver core clears the driver data to NULL after device_release or on probe failure. Thus, it is not needed to manually clear the device driver data to NULL. Signed-off-by: Jingoo Han Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/misc/tifm_7xx1.c b/drivers/misc/tifm_7xx1.c index f8d66543..ae282a1 100644 --- a/drivers/misc/tifm_7xx1.c +++ b/drivers/misc/tifm_7xx1.c @@ -378,7 +378,6 @@ err_out_irq: err_out_unmap: iounmap(fm->addr); err_out_free: - pci_set_drvdata(dev, NULL); tifm_free_adapter(fm); err_out_int: pci_intx(dev, 0); @@ -405,8 +404,6 @@ static void tifm_7xx1_remove(struct pci_dev *dev) for (cnt = 0; cnt < fm->num_sockets; cnt++) tifm_7xx1_sock_power_off(tifm_7xx1_sock_addr(fm->addr, cnt)); - pci_set_drvdata(dev, NULL); - iounmap(fm->addr); pci_intx(dev, 0); pci_release_regions(dev); -- cgit v0.10.2 From 8221b3a8480bcfdc68bae0ee00bbf0d313759149 Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Mon, 23 Sep 2013 10:36:24 +0900 Subject: misc: ibmasm: remove unnecessary pci_set_drvdata() The driver core clears the driver data to NULL after device_release or on probe failure. Thus, it is not needed to manually clear the device driver data to NULL. Signed-off-by: Jingoo Han Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/misc/ibmasm/module.c b/drivers/misc/ibmasm/module.c index 0c290e4..6b3bf9a 100644 --- a/drivers/misc/ibmasm/module.c +++ b/drivers/misc/ibmasm/module.c @@ -153,7 +153,6 @@ error_ioremap: error_heartbeat: ibmasm_event_buffer_exit(sp); error_eventbuffer: - pci_set_drvdata(pdev, NULL); kfree(sp); error_kmalloc: pci_release_regions(pdev); @@ -182,7 +181,6 @@ static void ibmasm_remove_one(struct pci_dev *pdev) ibmasm_free_remote_input_dev(sp); iounmap(sp->base_address); ibmasm_event_buffer_exit(sp); - pci_set_drvdata(pdev, NULL); kfree(sp); pci_release_regions(pdev); pci_disable_device(pdev); -- cgit v0.10.2 From b9be2c60fce7a93723403488a2f5d508ff7d3d2f Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Mon, 23 Sep 2013 10:37:24 +0900 Subject: misc: mei: remove unnecessary pci_set_drvdata() The driver core clears the driver data to NULL after device_release or on probe failure. Thus, it is not needed to manually clear the device driver data to NULL. Signed-off-by: Jingoo Han Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/misc/mei/pci-me.c b/drivers/misc/mei/pci-me.c index 1b3844e..1b8a4c6 100644 --- a/drivers/misc/mei/pci-me.c +++ b/drivers/misc/mei/pci-me.c @@ -239,7 +239,6 @@ static void mei_me_remove(struct pci_dev *pdev) free_irq(pdev->irq, dev); pci_disable_msi(pdev); - pci_set_drvdata(pdev, NULL); if (hw->mem_addr) pci_iounmap(pdev, hw->mem_addr); -- cgit v0.10.2 From 30399bbfa698689e81f9507992034fe09700941b Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Mon, 23 Sep 2013 10:38:26 +0900 Subject: misc: pti: remove unnecessary pci_set_drvdata() The driver core clears the driver data to NULL after device_release or on probe failure. Thus, it is not needed to manually clear the device driver data to NULL. Signed-off-by: Jingoo Han Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/misc/pti.c b/drivers/misc/pti.c index f84ff0c..eda38cb 100644 --- a/drivers/misc/pti.c +++ b/drivers/misc/pti.c @@ -892,7 +892,6 @@ static void pti_pci_remove(struct pci_dev *pdev) } iounmap(drv_data->pti_ioaddr); - pci_set_drvdata(pdev, NULL); kfree(drv_data); pci_release_region(pdev, 1); pci_disable_device(pdev); -- cgit v0.10.2 From c2275d2fa587712d285a4db97a68322a233ad2c8 Mon Sep 17 00:00:00 2001 From: Chanwoo Choi Date: Fri, 23 Aug 2013 10:21:37 +0900 Subject: extcon: Fix up 80 column coding style issues This patch fix 80 column coding sytle issues by using checkpatch script. Cc: Charles Keepax Cc: Mark Brown Signed-off-by: Chanwoo Choi Signed-off-by: Myungjoo Ham Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/extcon/extcon-arizona.c b/drivers/extcon/extcon-arizona.c index e557130..9431092 100644 --- a/drivers/extcon/extcon-arizona.c +++ b/drivers/extcon/extcon-arizona.c @@ -738,8 +738,8 @@ err: static void arizona_micd_timeout_work(struct work_struct *work) { struct arizona_extcon_info *info = container_of(work, - struct arizona_extcon_info, - micd_timeout_work.work); + struct arizona_extcon_info, + micd_timeout_work.work); mutex_lock(&info->lock); @@ -756,8 +756,8 @@ static void arizona_micd_timeout_work(struct work_struct *work) static void arizona_micd_detect(struct work_struct *work) { struct arizona_extcon_info *info = container_of(work, - struct arizona_extcon_info, - micd_detect_work.work); + struct arizona_extcon_info, + micd_detect_work.work); struct arizona *arizona = info->arizona; unsigned int val = 0, lvl; int ret, i, key; @@ -769,7 +769,8 @@ static void arizona_micd_detect(struct work_struct *work) for (i = 0; i < 10 && !(val & 0x7fc); i++) { ret = regmap_read(arizona->regmap, ARIZONA_MIC_DETECT_3, &val); if (ret != 0) { - dev_err(arizona->dev, "Failed to read MICDET: %d\n", ret); + dev_err(arizona->dev, + "Failed to read MICDET: %d\n", ret); mutex_unlock(&info->lock); return; } @@ -777,7 +778,8 @@ static void arizona_micd_detect(struct work_struct *work) dev_dbg(arizona->dev, "MICDET: %x\n", val); if (!(val & ARIZONA_MICD_VALID)) { - dev_warn(arizona->dev, "Microphone detection state invalid\n"); + dev_warn(arizona->dev, + "Microphone detection state invalid\n"); mutex_unlock(&info->lock); return; } @@ -925,8 +927,8 @@ static irqreturn_t arizona_micdet(int irq, void *data) static void arizona_hpdet_work(struct work_struct *work) { struct arizona_extcon_info *info = container_of(work, - struct arizona_extcon_info, - hpdet_work.work); + struct arizona_extcon_info, + hpdet_work.work); mutex_lock(&info->lock); arizona_start_hpdet_acc_id(info); @@ -973,10 +975,13 @@ static irqreturn_t arizona_jackdet(int irq, void *data) &info->hpdet_work, msecs_to_jiffies(HPDET_DEBOUNCE)); - if (cancelled_mic) + if (cancelled_mic) { + int micd_timeout = info->micd_timeout; + queue_delayed_work(system_power_efficient_wq, &info->micd_timeout_work, - msecs_to_jiffies(info->micd_timeout)); + msecs_to_jiffies(micd_timeout)); + } goto out; } diff --git a/drivers/extcon/extcon-class.c b/drivers/extcon/extcon-class.c index 148382f..1754432 100644 --- a/drivers/extcon/extcon-class.c +++ b/drivers/extcon/extcon-class.c @@ -452,7 +452,8 @@ int extcon_register_interest(struct extcon_specific_cable_nb *obj, if (!obj->edev) return -ENODEV; - obj->cable_index = extcon_find_cable_index(obj->edev, cable_name); + obj->cable_index = extcon_find_cable_index(obj->edev, + cable_name); if (obj->cable_index < 0) return obj->cable_index; @@ -460,7 +461,8 @@ int extcon_register_interest(struct extcon_specific_cable_nb *obj, obj->internal_nb.notifier_call = _call_per_cable; - return raw_notifier_chain_register(&obj->edev->nh, &obj->internal_nb); + return raw_notifier_chain_register(&obj->edev->nh, + &obj->internal_nb); } else { struct class_dev_iter iter; struct extcon_dev *extd; diff --git a/drivers/extcon/extcon-max77693.c b/drivers/extcon/extcon-max77693.c index b56bdaa..5ee0344 100644 --- a/drivers/extcon/extcon-max77693.c +++ b/drivers/extcon/extcon-max77693.c @@ -189,14 +189,17 @@ enum max77693_muic_acc_type { /* The below accessories have same ADC value so ADCLow and ADC1K bit is used to separate specific accessory */ - MAX77693_MUIC_GND_USB_OTG = 0x100, /* ADC:0x0, VBVolot:0, ADCLow:0, ADC1K:0 */ - MAX77693_MUIC_GND_USB_OTG_VB = 0x104, /* ADC:0x0, VBVolot:1, ADCLow:0, ADC1K:0 */ - MAX77693_MUIC_GND_AV_CABLE_LOAD = 0x102,/* ADC:0x0, VBVolot:0, ADCLow:1, ADC1K:0 */ - MAX77693_MUIC_GND_MHL = 0x103, /* ADC:0x0, VBVolot:0, ADCLow:1, ADC1K:1 */ - MAX77693_MUIC_GND_MHL_VB = 0x107, /* ADC:0x0, VBVolot:1, ADCLow:1, ADC1K:1 */ + /* ADC|VBVolot|ADCLow|ADC1K| */ + MAX77693_MUIC_GND_USB_OTG = 0x100, /* 0x0| 0| 0| 0| */ + MAX77693_MUIC_GND_USB_OTG_VB = 0x104, /* 0x0| 1| 0| 0| */ + MAX77693_MUIC_GND_AV_CABLE_LOAD = 0x102,/* 0x0| 0| 1| 0| */ + MAX77693_MUIC_GND_MHL = 0x103, /* 0x0| 0| 1| 1| */ + MAX77693_MUIC_GND_MHL_VB = 0x107, /* 0x0| 1| 1| 1| */ }; -/* MAX77693 MUIC device support below list of accessories(external connector) */ +/* + * MAX77693 MUIC device support below list of accessories(external connector) + */ enum { EXTCON_CABLE_USB = 0, EXTCON_CABLE_USB_HOST, @@ -395,12 +398,12 @@ static int max77693_muic_get_cable_type(struct max77693_muic_info *info, vbvolt >>= STATUS2_VBVOLT_SHIFT; /** - * [0x1][VBVolt][ADCLow][ADC1K] - * [0x1 0 0 0 ] : USB_OTG - * [0x1 1 0 0 ] : USB_OTG_VB - * [0x1 0 1 0 ] : Audio Video Cable with load - * [0x1 0 1 1 ] : MHL without charging connector - * [0x1 1 1 1 ] : MHL with charging connector + * [0x1|VBVolt|ADCLow|ADC1K] + * [0x1| 0| 0| 0] USB_OTG + * [0x1| 1| 0| 0] USB_OTG_VB + * [0x1| 0| 1| 0] Audio Video cable with load + * [0x1| 0| 1| 1] MHL without charging cable + * [0x1| 1| 1| 1] MHL with charging cable */ cable_type = ((0x1 << 8) | (vbvolt << 2) @@ -723,11 +726,11 @@ static int max77693_muic_adc_handler(struct max77693_muic_info *info) if (ret < 0) return ret; break; - case MAX77693_MUIC_ADC_REMOTE_S3_BUTTON: /* DOCK_KEY_PREV */ - case MAX77693_MUIC_ADC_REMOTE_S7_BUTTON: /* DOCK_KEY_NEXT */ - case MAX77693_MUIC_ADC_REMOTE_S9_BUTTON: /* DOCK_VOL_DOWN */ - case MAX77693_MUIC_ADC_REMOTE_S10_BUTTON: /* DOCK_VOL_UP */ - case MAX77693_MUIC_ADC_REMOTE_S12_BUTTON: /* DOCK_KEY_PLAY_PAUSE */ + case MAX77693_MUIC_ADC_REMOTE_S3_BUTTON: /* DOCK_KEY_PREV */ + case MAX77693_MUIC_ADC_REMOTE_S7_BUTTON: /* DOCK_KEY_NEXT */ + case MAX77693_MUIC_ADC_REMOTE_S9_BUTTON: /* DOCK_VOL_DOWN */ + case MAX77693_MUIC_ADC_REMOTE_S10_BUTTON: /* DOCK_VOL_UP */ + case MAX77693_MUIC_ADC_REMOTE_S12_BUTTON: /* DOCK_KEY_PLAY_PAUSE */ /* * Button of DOCK device * - the Prev/Next/Volume Up/Volume Down/Play-Pause button @@ -815,19 +818,21 @@ static int max77693_muic_chg_handler(struct max77693_muic_info *info) case MAX77693_MUIC_GND_MHL_VB: /* * MHL cable with MHL_TA(USB/TA) cable - * - MHL cable include two port(HDMI line and separate micro- - * usb port. When the target connect MHL cable, extcon driver - * check whether MHL_TA(USB/TA) cable is connected. If MHL_TA - * cable is connected, extcon driver notify state to notifiee - * for charging battery. + * - MHL cable include two port(HDMI line and separate + * micro-usb port. When the target connect MHL cable, + * extcon driver check whether MHL_TA(USB/TA) cable is + * connected. If MHL_TA cable is connected, extcon + * driver notify state to notifiee for charging battery. * * Features of 'MHL_TA(USB/TA) with MHL cable' * - Support MHL - * - Support charging through micro-usb port without data connection + * - Support charging through micro-usb port without + * data connection */ extcon_set_cable_state(info->edev, "MHL_TA", attached); if (!cable_attached) - extcon_set_cable_state(info->edev, "MHL", cable_attached); + extcon_set_cable_state(info->edev, + "MHL", cable_attached); break; } @@ -839,47 +844,51 @@ static int max77693_muic_chg_handler(struct max77693_muic_info *info) case MAX77693_MUIC_ADC_AV_CABLE_NOLOAD: /* Dock-Audio */ /* * Dock-Audio device with USB/TA cable - * - Dock device include two port(Dock-Audio and micro-usb - * port). When the target connect Dock-Audio device, extcon - * driver check whether USB/TA cable is connected. If USB/TA - * cable is connected, extcon driver notify state to notifiee - * for charging battery. + * - Dock device include two port(Dock-Audio and micro- + * usb port). When the target connect Dock-Audio device, + * extcon driver check whether USB/TA cable is connected + * or not. If USB/TA cable is connected, extcon driver + * notify state to notifiee for charging battery. * * Features of 'USB/TA cable with Dock-Audio device' * - Support external output feature of audio. - * - Support charging through micro-usb port without data - * connection. + * - Support charging through micro-usb port without + * data connection. */ extcon_set_cable_state(info->edev, "USB", attached); if (!cable_attached) - extcon_set_cable_state(info->edev, "Dock-Audio", cable_attached); + extcon_set_cable_state(info->edev, "Dock-Audio", + cable_attached); break; case MAX77693_MUIC_ADC_RESERVED_ACC_3: /* Dock-Smart */ /* * Dock-Smart device with USB/TA cable * - Dock-Desk device include three type of cable which * are HDMI, USB for mouse/keyboard and micro-usb port - * for USB/TA cable. Dock-Smart device need always exteranl - * power supply(USB/TA cable through micro-usb cable). Dock- - * Smart device support screen output of target to separate - * monitor and mouse/keyboard for desktop mode. + * for USB/TA cable. Dock-Smart device need always + * exteranl power supply(USB/TA cable through micro-usb + * cable). Dock-Smart device support screen output of + * target to separate monitor and mouse/keyboard for + * desktop mode. * * Features of 'USB/TA cable with Dock-Smart device' * - Support MHL * - Support external output feature of audio - * - Support charging through micro-usb port without data - * connection if TA cable is connected to target. - * - Support charging and data connection through micro-usb port - * if USB cable is connected between target and host - * device. + * - Support charging through micro-usb port without + * data connection if TA cable is connected to target. + * - Support charging and data connection through micro- + * usb port if USB cable is connected between target + * and host device * - Support OTG device (Mouse/Keyboard) */ - ret = max77693_muic_set_path(info, info->path_usb, attached); + ret = max77693_muic_set_path(info, info->path_usb, + attached); if (ret < 0) return ret; - extcon_set_cable_state(info->edev, "Dock-Smart", attached); + extcon_set_cable_state(info->edev, "Dock-Smart", + attached); extcon_set_cable_state(info->edev, "MHL", attached); break; @@ -889,25 +898,28 @@ static int max77693_muic_chg_handler(struct max77693_muic_info *info) switch (chg_type) { case MAX77693_CHARGER_TYPE_NONE: /* - * When MHL(with USB/TA cable) or Dock-Audio with USB/TA cable - * is attached, muic device happen below two interrupt. - * - 'MAX77693_MUIC_IRQ_INT1_ADC' for detecting MHL/Dock-Audio. - * - 'MAX77693_MUIC_IRQ_INT2_CHGTYP' for detecting USB/TA cable - * connected to MHL or Dock-Audio. - * Always, happen eariler MAX77693_MUIC_IRQ_INT1_ADC interrupt - * than MAX77693_MUIC_IRQ_INT2_CHGTYP interrupt. + * When MHL(with USB/TA cable) or Dock-Audio with USB/TA + * cable is attached, muic device happen below two irq. + * - 'MAX77693_MUIC_IRQ_INT1_ADC' for detecting + * MHL/Dock-Audio. + * - 'MAX77693_MUIC_IRQ_INT2_CHGTYP' for detecting + * USB/TA cable connected to MHL or Dock-Audio. + * Always, happen eariler MAX77693_MUIC_IRQ_INT1_ADC + * irq than MAX77693_MUIC_IRQ_INT2_CHGTYP irq. * - * If user attach MHL (with USB/TA cable and immediately detach - * MHL with USB/TA cable before MAX77693_MUIC_IRQ_INT2_CHGTYP - * interrupt is happened, USB/TA cable remain connected state to - * target. But USB/TA cable isn't connected to target. The user - * be face with unusual action. So, driver should check this - * situation in spite of, that previous charger type is N/A. + * If user attach MHL (with USB/TA cable and immediately + * detach MHL with USB/TA cable before MAX77693_MUIC_IRQ + * _INT2_CHGTYP irq is happened, USB/TA cable remain + * connected state to target. But USB/TA cable isn't + * connected to target. The user be face with unusual + * action. So, driver should check this situation in + * spite of, that previous charger type is N/A. */ break; case MAX77693_CHARGER_TYPE_USB: /* Only USB cable, PATH:AP_USB */ - ret = max77693_muic_set_path(info, info->path_usb, attached); + ret = max77693_muic_set_path(info, info->path_usb, + attached); if (ret < 0) return ret; @@ -1214,7 +1226,8 @@ static int max77693_muic_probe(struct platform_device *pdev) } if (pdata->muic_data) { - struct max77693_muic_platform_data *muic_pdata = pdata->muic_data; + struct max77693_muic_platform_data *muic_pdata + = pdata->muic_data; /* * Default usb/uart path whether UART/USB or AUX_UART/AUX_USB diff --git a/drivers/extcon/extcon-max8997.c b/drivers/extcon/extcon-max8997.c index 67d6738..b9e726d 100644 --- a/drivers/extcon/extcon-max8997.c +++ b/drivers/extcon/extcon-max8997.c @@ -426,7 +426,8 @@ static int max8997_muic_adc_handler(struct max8997_muic_info *info) break; case MAX8997_MUIC_ADC_FACTORY_MODE_USB_OFF: case MAX8997_MUIC_ADC_FACTORY_MODE_USB_ON: - ret = max8997_muic_handle_usb(info, MAX8997_USB_DEVICE, attached); + ret = max8997_muic_handle_usb(info, + MAX8997_USB_DEVICE, attached); if (ret < 0) return ret; break; @@ -504,7 +505,8 @@ static int max8997_muic_chg_handler(struct max8997_muic_info *info) } break; case MAX8997_CHARGER_TYPE_DOWNSTREAM_PORT: - extcon_set_cable_state(info->edev, "Charge-downstream", attached); + extcon_set_cable_state(info->edev, + "Charge-downstream", attached); break; case MAX8997_CHARGER_TYPE_DEDICATED_CHG: extcon_set_cable_state(info->edev, "TA", attached); -- cgit v0.10.2 From 2cc7e4d4b8086a8ad2299b30b610624aa911d1f8 Mon Sep 17 00:00:00 2001 From: Chanwoo Choi Date: Fri, 23 Aug 2013 10:21:38 +0900 Subject: extcon: class: Remove unnecessary extern declaration This patch remove unnecessary extern declaration (extcon_set_state). checkpatch found this coding style issue. Signed-off-by: Chanwoo Choi Signed-off-by: Myungjoo Ham Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/extcon/extcon-class.c b/drivers/extcon/extcon-class.c index 1754432..b6a3d37 100644 --- a/drivers/extcon/extcon-class.c +++ b/drivers/extcon/extcon-class.c @@ -129,7 +129,6 @@ static ssize_t state_show(struct device *dev, struct device_attribute *attr, return count; } -int extcon_set_state(struct extcon_dev *edev, u32 state); static ssize_t state_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { -- cgit v0.10.2 From d6675667540a19427099cfd7eb80fcd4320a951d Mon Sep 17 00:00:00 2001 From: Chanwoo Choi Date: Fri, 23 Aug 2013 10:21:39 +0900 Subject: extcon: arizona: Fix up minor coding style to remove unnecessary braces This fixes up braces coding style issue by using checkpatch script. Cc: Charles Keepax Cc: Mark Brown Signed-off-by: Chanwoo Choi Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/extcon/extcon-arizona.c b/drivers/extcon/extcon-arizona.c index 9431092..91a702b 100644 --- a/drivers/extcon/extcon-arizona.c +++ b/drivers/extcon/extcon-arizona.c @@ -564,11 +564,10 @@ static irqreturn_t arizona_hpdet_irq(int irq, void *data) } ret = arizona_hpdet_read(info); - if (ret == -EAGAIN) { + if (ret == -EAGAIN) goto out; - } else if (ret < 0) { + else if (ret < 0) goto done; - } reading = ret; /* Reset back to starting range */ @@ -578,11 +577,10 @@ static irqreturn_t arizona_hpdet_irq(int irq, void *data) 0); ret = arizona_hpdet_do_id(info, &reading, &mic); - if (ret == -EAGAIN) { + if (ret == -EAGAIN) goto out; - } else if (ret < 0) { + else if (ret < 0) goto done; - } /* Report high impedence cables as line outputs */ if (reading >= 5000) -- cgit v0.10.2 From 58125210ab3b202917c04fca014317944d464ea0 Mon Sep 17 00:00:00 2001 From: Olaf Hering Date: Wed, 7 Aug 2013 19:14:37 +0200 Subject: Tools: hv: cache FQDN in kvp_daemon to avoid timeouts kvp_daemon does some operations which take an unpredicable amount of time. In addition the kernel driver gives the kvp_daemon a 5 second timeout to respond to message from the host. If an operation such as getaddrinfo takes a long time and the timeout triggers then netlink errors occour. As a result of such errors the daemon just terminates and the service becomes unavailable. Idendifying and fixing these shortcomings in the kernel-userland communication protocol will be done in separate patches. This change fixes just one obvious timeout bug. Update kvp_get_domain_name to not return a value, better diagnostic for the consumer of the hostname string, remove trailing newline in error case, use snprintf to not overrun output buffer, get hostname only once and return the cached result. Signed-off-by: Olaf Hering Signed-off-by: Greg Kroah-Hartman diff --git a/tools/hv/hv_kvp_daemon.c b/tools/hv/hv_kvp_daemon.c index 8fd9ec6..21bd7d4 100644 --- a/tools/hv/hv_kvp_daemon.c +++ b/tools/hv/hv_kvp_daemon.c @@ -89,6 +89,7 @@ static char *processor_arch; static char *os_build; static char *os_version; static char *lic_version = "Unknown version"; +static char full_domain_name[HV_KVP_EXCHANGE_MAX_VALUE_SIZE]; static struct utsname uts_buf; /* @@ -1367,7 +1368,7 @@ setval_error: } -static int +static void kvp_get_domain_name(char *buffer, int length) { struct addrinfo hints, *info ; @@ -1381,12 +1382,12 @@ kvp_get_domain_name(char *buffer, int length) error = getaddrinfo(buffer, NULL, &hints, &info); if (error != 0) { - strcpy(buffer, "getaddrinfo failed\n"); - return error; + snprintf(buffer, length, "getaddrinfo failed: 0x%x %s", + error, gai_strerror(error)); + return; } - strcpy(buffer, info->ai_canonname); + snprintf(buffer, length, "%s", info->ai_canonname); freeaddrinfo(info); - return error; } static int @@ -1453,6 +1454,11 @@ int main(void) * Retrieve OS release information. */ kvp_get_os_info(); + /* + * Cache Fully Qualified Domain Name because getaddrinfo takes an + * unpredictable amount of time to finish. + */ + kvp_get_domain_name(full_domain_name, sizeof(full_domain_name)); if (kvp_file_init()) { syslog(LOG_ERR, "Failed to initialize the pools"); @@ -1671,8 +1677,7 @@ int main(void) switch (hv_msg->body.kvp_enum_data.index) { case FullyQualifiedDomainName: - kvp_get_domain_name(key_value, - HV_KVP_EXCHANGE_MAX_VALUE_SIZE); + strcpy(key_value, full_domain_name); strcpy(key_name, "FullyQualifiedDomainName"); break; case IntegrationServicesVersion: -- cgit v0.10.2 From 269ce62bbc00c4e80bf3ca2aa21823f20625bcf6 Mon Sep 17 00:00:00 2001 From: Olaf Hering Date: Tue, 6 Aug 2013 20:55:38 +0200 Subject: Tools: hv: use single send+recv buffer send_buffer is used only once during registration. To reduce runtime memory usage reuse the recv_buffer for registration. Also use NLMSG_LENGTH instead of NLMSG_HDRLEN to take alignment into account. Signed-off-by: Olaf Hering Signed-off-by: Greg Kroah-Hartman diff --git a/tools/hv/hv_kvp_daemon.c b/tools/hv/hv_kvp_daemon.c index 21bd7d4..b8d6d54 100644 --- a/tools/hv/hv_kvp_daemon.c +++ b/tools/hv/hv_kvp_daemon.c @@ -1434,7 +1434,6 @@ int main(void) int pool; char *if_name; struct hv_kvp_ipaddr_value *kvp_ip_val; - char *kvp_send_buffer; char *kvp_recv_buffer; size_t kvp_recv_buffer_len; @@ -1443,11 +1442,10 @@ int main(void) openlog("KVP", 0, LOG_USER); syslog(LOG_INFO, "KVP starting; pid is:%d", getpid()); - kvp_recv_buffer_len = NLMSG_HDRLEN + sizeof(struct cn_msg) + sizeof(struct hv_kvp_msg); - kvp_send_buffer = calloc(1, kvp_recv_buffer_len); + kvp_recv_buffer_len = NLMSG_LENGTH(0) + sizeof(struct cn_msg) + sizeof(struct hv_kvp_msg); kvp_recv_buffer = calloc(1, kvp_recv_buffer_len); - if (!(kvp_send_buffer && kvp_recv_buffer)) { - syslog(LOG_ERR, "Failed to allocate netlink buffers"); + if (!kvp_recv_buffer) { + syslog(LOG_ERR, "Failed to allocate netlink buffer"); exit(EXIT_FAILURE); } /* @@ -1494,7 +1492,7 @@ int main(void) /* * Register ourselves with the kernel. */ - message = (struct cn_msg *)kvp_send_buffer; + message = (struct cn_msg *)kvp_recv_buffer; message->id.idx = CN_KVP_IDX; message->id.val = CN_KVP_VAL; diff --git a/tools/hv/hv_vss_daemon.c b/tools/hv/hv_vss_daemon.c index 8611962..8bcb040 100644 --- a/tools/hv/hv_vss_daemon.c +++ b/tools/hv/hv_vss_daemon.c @@ -140,7 +140,6 @@ int main(void) struct cn_msg *incoming_cn_msg; int op; struct hv_vss_msg *vss_msg; - char *vss_send_buffer; char *vss_recv_buffer; size_t vss_recv_buffer_len; @@ -150,10 +149,9 @@ int main(void) openlog("Hyper-V VSS", 0, LOG_USER); syslog(LOG_INFO, "VSS starting; pid is:%d", getpid()); - vss_recv_buffer_len = NLMSG_HDRLEN + sizeof(struct cn_msg) + sizeof(struct hv_vss_msg); - vss_send_buffer = calloc(1, vss_recv_buffer_len); + vss_recv_buffer_len = NLMSG_LENGTH(0) + sizeof(struct cn_msg) + sizeof(struct hv_vss_msg); vss_recv_buffer = calloc(1, vss_recv_buffer_len); - if (!(vss_send_buffer && vss_recv_buffer)) { + if (!vss_recv_buffer) { syslog(LOG_ERR, "Failed to allocate netlink buffers"); exit(EXIT_FAILURE); } @@ -185,7 +183,7 @@ int main(void) /* * Register ourselves with the kernel. */ - message = (struct cn_msg *)vss_send_buffer; + message = (struct cn_msg *)vss_recv_buffer; message->id.idx = CN_VSS_IDX; message->id.val = CN_VSS_VAL; message->ack = 0; -- cgit v0.10.2 From 9fd379e929a2808208b1d2d4cd9697067e83a242 Mon Sep 17 00:00:00 2001 From: Oliver Schinagl Date: Tue, 3 Sep 2013 12:33:27 +0200 Subject: ARM: sunxi: Initial support for Allwinner's Security ID fuses Allwinner has electric fuses (efuse) on their line of chips. This driver reads those fuses, seeds the kernel entropy and exports them as a sysfs node. These fuses are most likely to be programmed at the factory, encoding things like Chip ID, some sort of serial number, etc. and appear to be reasonably unique. While in theory, these should be writeable by the user, it will probably be inconvenient to do so. Allwinner recommends that a certain input pin, labeled 'efuse_vddq', be connected to GND. To write these fuses however, a 2.5 V programming voltage needs to be applied to this pin. Even so, they can still be used to generate a board-unique mac from, board unique RSA key and seed the kernel RNG. On sun7i additional storage is available, this is initially used for an UEFI BOOT key, Secure JTAG key, HDMI-HDCP key and vendor specific keys. Currently supported are the following known chips: Allwinner sun4i (A10) Allwinner sun5i (A10s, A13) Allwinner sun7i (A20) Signed-off-by: Oliver Schinagl Acked-by: Maxime Ripard Signed-off-by: Greg Kroah-Hartman diff --git a/Documentation/ABI/testing/sysfs-driver-sunxi-sid b/Documentation/ABI/testing/sysfs-driver-sunxi-sid new file mode 100644 index 0000000..ffb9536 --- /dev/null +++ b/Documentation/ABI/testing/sysfs-driver-sunxi-sid @@ -0,0 +1,22 @@ +What: /sys/devices/*//eeprom +Date: August 2013 +Contact: Oliver Schinagl +Description: read-only access to the SID (Security-ID) on current + A-series SoC's from Allwinner. Currently supports A10, A10s, A13 + and A20 CPU's. The earlier A1x series of SoCs exports 16 bytes, + whereas the newer A20 SoC exposes 512 bytes split into sections. + Besides the 16 bytes of SID, there's also an SJTAG area, + HDMI-HDCP key and some custom keys. Below a quick overview, for + details see the user manual: + 0x000 128 bit root-key (sun[457]i) + 0x010 128 bit boot-key (sun7i) + 0x020 64 bit security-jtag-key (sun7i) + 0x028 16 bit key configuration (sun7i) + 0x02b 16 bit custom-vendor-key (sun7i) + 0x02c 320 bit low general key (sun7i) + 0x040 32 bit read-control access (sun7i) + 0x064 224 bit low general key (sun7i) + 0x080 2304 bit HDCP-key (sun7i) + 0x1a0 768 bit high general key (sun7i) +Users: any user space application which wants to read the SID on + Allwinner's A-series of CPU's. diff --git a/Documentation/devicetree/bindings/misc/allwinner,sunxi-sid.txt b/Documentation/devicetree/bindings/misc/allwinner,sunxi-sid.txt new file mode 100644 index 0000000..68ba372 --- /dev/null +++ b/Documentation/devicetree/bindings/misc/allwinner,sunxi-sid.txt @@ -0,0 +1,17 @@ +Allwinner sunxi-sid + +Required properties: +- compatible: "allwinner,sun4i-sid" or "allwinner,sun7i-a20-sid". +- reg: Should contain registers location and length + +Example for sun4i: + sid@01c23800 { + compatible = "allwinner,sun4i-sid"; + reg = <0x01c23800 0x10> + }; + +Example for sun7i: + sid@01c23800 { + compatible = "allwinner,sun7i-a20-sid"; + reg = <0x01c23800 0x200> + }; diff --git a/drivers/misc/eeprom/Kconfig b/drivers/misc/eeprom/Kconfig index 04f2e1f..9536852f 100644 --- a/drivers/misc/eeprom/Kconfig +++ b/drivers/misc/eeprom/Kconfig @@ -96,4 +96,17 @@ config EEPROM_DIGSY_MTC_CFG If unsure, say N. +config EEPROM_SUNXI_SID + tristate "Allwinner sunxi security ID support" + depends on ARCH_SUNXI && SYSFS + help + This is a driver for the 'security ID' available on various Allwinner + devices. + + Due to the potential risks involved with changing e-fuses, + this driver is read-only. + + This driver can also be built as a module. If so, the module + will be called sunxi_sid. + endmenu diff --git a/drivers/misc/eeprom/Makefile b/drivers/misc/eeprom/Makefile index fc1e81d..9507aec 100644 --- a/drivers/misc/eeprom/Makefile +++ b/drivers/misc/eeprom/Makefile @@ -4,4 +4,5 @@ obj-$(CONFIG_EEPROM_LEGACY) += eeprom.o obj-$(CONFIG_EEPROM_MAX6875) += max6875.o obj-$(CONFIG_EEPROM_93CX6) += eeprom_93cx6.o obj-$(CONFIG_EEPROM_93XX46) += eeprom_93xx46.o +obj-$(CONFIG_EEPROM_SUNXI_SID) += sunxi_sid.o obj-$(CONFIG_EEPROM_DIGSY_MTC_CFG) += digsy_mtc_eeprom.o diff --git a/drivers/misc/eeprom/sunxi_sid.c b/drivers/misc/eeprom/sunxi_sid.c new file mode 100644 index 0000000..9c34e57 --- /dev/null +++ b/drivers/misc/eeprom/sunxi_sid.c @@ -0,0 +1,158 @@ +/* + * Copyright (c) 2013 Oliver Schinagl + * http://www.linux-sunxi.org + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * This driver exposes the Allwinner security ID, efuses exported in byte- + * sized chunks. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DRV_NAME "sunxi-sid" + +struct sunxi_sid_data { + void __iomem *reg_base; + unsigned int keysize; +}; + +/* We read the entire key, due to a 32 bit read alignment requirement. Since we + * want to return the requested byte, this results in somewhat slower code and + * uses 4 times more reads as needed but keeps code simpler. Since the SID is + * only very rarely probed, this is not really an issue. + */ +static u8 sunxi_sid_read_byte(const struct sunxi_sid_data *sid_data, + const unsigned int offset) +{ + u32 sid_key; + + if (offset >= sid_data->keysize) + return 0; + + sid_key = ioread32be(sid_data->reg_base + round_down(offset, 4)); + sid_key >>= (offset % 4) * 8; + + return sid_key; /* Only return the last byte */ +} + +static ssize_t sid_read(struct file *fd, struct kobject *kobj, + struct bin_attribute *attr, char *buf, + loff_t pos, size_t size) +{ + struct platform_device *pdev; + struct sunxi_sid_data *sid_data; + int i; + + pdev = to_platform_device(kobj_to_dev(kobj)); + sid_data = platform_get_drvdata(pdev); + + if (pos < 0 || pos >= sid_data->keysize) + return 0; + if (size > sid_data->keysize - pos) + size = sid_data->keysize - pos; + + for (i = 0; i < size; i++) + buf[i] = sunxi_sid_read_byte(sid_data, pos + i); + + return i; +} + +static struct bin_attribute sid_bin_attr = { + .attr = { .name = "eeprom", .mode = S_IRUGO, }, + .read = sid_read, +}; + +static int sunxi_sid_remove(struct platform_device *pdev) +{ + device_remove_bin_file(&pdev->dev, &sid_bin_attr); + dev_dbg(&pdev->dev, "driver unloaded\n"); + + return 0; +} + +static const struct of_device_id sunxi_sid_of_match[] = { + { .compatible = "allwinner,sun4i-sid", .data = (void *)16}, + { .compatible = "allwinner,sun7i-a20-sid", .data = (void *)512}, + {/* sentinel */}, +}; +MODULE_DEVICE_TABLE(of, sunxi_sid_of_match); + +static int sunxi_sid_probe(struct platform_device *pdev) +{ + struct sunxi_sid_data *sid_data; + struct resource *res; + const struct of_device_id *of_dev_id; + u8 *entropy; + unsigned int i; + + sid_data = devm_kzalloc(&pdev->dev, sizeof(struct sunxi_sid_data), + GFP_KERNEL); + if (!sid_data) + return -ENOMEM; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + sid_data->reg_base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(sid_data->reg_base)) + return PTR_ERR(sid_data->reg_base); + + of_dev_id = of_match_device(sunxi_sid_of_match, &pdev->dev); + if (!of_dev_id) + return -ENODEV; + sid_data->keysize = (int)of_dev_id->data; + + platform_set_drvdata(pdev, sid_data); + + sid_bin_attr.size = sid_data->keysize; + if (device_create_bin_file(&pdev->dev, &sid_bin_attr)) + return -ENODEV; + + entropy = kzalloc(sizeof(u8) * sid_data->keysize, GFP_KERNEL); + for (i = 0; i < sid_data->keysize; i++) + entropy[i] = sunxi_sid_read_byte(sid_data, i); + add_device_randomness(entropy, sid_data->keysize); + kfree(entropy); + + dev_dbg(&pdev->dev, "loaded\n"); + + return 0; +} + +static struct platform_driver sunxi_sid_driver = { + .probe = sunxi_sid_probe, + .remove = sunxi_sid_remove, + .driver = { + .name = DRV_NAME, + .owner = THIS_MODULE, + .of_match_table = sunxi_sid_of_match, + }, +}; +module_platform_driver(sunxi_sid_driver); + +MODULE_AUTHOR("Oliver Schinagl "); +MODULE_DESCRIPTION("Allwinner sunxi security id driver"); +MODULE_LICENSE("GPL"); -- cgit v0.10.2 From 9089e3be60b13a1114e8afdc37c605e75cfc4a26 Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Fri, 6 Sep 2013 14:39:28 +0800 Subject: VMCI: fix to pass correct device identity to free_irq() free_irq() expects the same device identity that was passed to corresponding request_irq(), otherwise the IRQ is not freed. Signed-off-by: Wei Yongjun Acked-by: Dmitry Torokhov Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/misc/vmw_vmci/vmci_guest.c b/drivers/misc/vmw_vmci/vmci_guest.c index b3a2b76..c98b03b 100644 --- a/drivers/misc/vmw_vmci/vmci_guest.c +++ b/drivers/misc/vmw_vmci/vmci_guest.c @@ -649,7 +649,7 @@ static int vmci_guest_probe_device(struct pci_dev *pdev, return 0; err_free_irq: - free_irq(vmci_dev->irq, &vmci_dev); + free_irq(vmci_dev->irq, vmci_dev); tasklet_kill(&vmci_dev->datagram_tasklet); tasklet_kill(&vmci_dev->bm_tasklet); -- cgit v0.10.2 From b170d8ce3f81bd97e85756e9184779a56a5f55a7 Mon Sep 17 00:00:00 2001 From: Sudeep Dutt Date: Thu, 5 Sep 2013 16:41:31 -0700 Subject: Intel MIC Host Driver for X100 family. This patch enables the following: a) Initializes the Intel MIC X100 PCIe devices. b) Provides sysfs entries for family and stepping information. Co-author: Dasaratharaman Chandramouli Signed-off-by: Ashutosh Dixit Signed-off-by: Caz Yokoyama Signed-off-by: Dasaratharaman Chandramouli Signed-off-by: Harshavardhan R Kharche Signed-off-by: Nikhil Rao Signed-off-by: Sudeep Dutt Acked-by: Yaozu (Eddie) Dong Reviewed-by: Peter P Waskiewicz Jr Signed-off-by: Greg Kroah-Hartman diff --git a/Documentation/ABI/testing/sysfs-class-mic.txt b/Documentation/ABI/testing/sysfs-class-mic.txt new file mode 100644 index 0000000..09eb3c6 --- /dev/null +++ b/Documentation/ABI/testing/sysfs-class-mic.txt @@ -0,0 +1,34 @@ +What: /sys/class/mic/ +Date: August 2013 +KernelVersion: 3.11 +Contact: Sudeep Dutt +Description: + The mic class directory belongs to Intel MIC devices and + provides information per MIC device. An Intel MIC device is a + PCIe form factor add-in Coprocessor card based on the Intel Many + Integrated Core (MIC) architecture that runs a Linux OS. + +What: /sys/class/mic/mic(x) +Date: August 2013 +KernelVersion: 3.11 +Contact: Sudeep Dutt +Description: + The directories /sys/class/mic/mic0, /sys/class/mic/mic1 etc., + represent MIC devices (0,1,..etc). Each directory has + information specific to that MIC device. + +What: /sys/class/mic/mic(x)/family +Date: August 2013 +KernelVersion: 3.11 +Contact: Sudeep Dutt +Description: + Provides information about the Coprocessor family for an Intel + MIC device. For example - "x100" + +What: /sys/class/mic/mic(x)/stepping +Date: August 2013 +KernelVersion: 3.11 +Contact: Sudeep Dutt +Description: + Provides information about the silicon stepping for an Intel + MIC device. For example - "A0" or "B0" diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index 8dacd4c..e760715 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -537,4 +537,5 @@ source "drivers/misc/carma/Kconfig" source "drivers/misc/altera-stapl/Kconfig" source "drivers/misc/mei/Kconfig" source "drivers/misc/vmw_vmci/Kconfig" +source "drivers/misc/mic/Kconfig" endmenu diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index c235d5b..0b7ea3e 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -53,3 +53,4 @@ obj-$(CONFIG_INTEL_MEI) += mei/ obj-$(CONFIG_VMWARE_VMCI) += vmw_vmci/ obj-$(CONFIG_LATTICE_ECP3_CONFIG) += lattice-ecp3-config.o obj-$(CONFIG_SRAM) += sram.o +obj-y += mic/ diff --git a/drivers/misc/mic/Kconfig b/drivers/misc/mic/Kconfig new file mode 100644 index 0000000..aaefd0c --- /dev/null +++ b/drivers/misc/mic/Kconfig @@ -0,0 +1,19 @@ +comment "Intel MIC Host Driver" + +config INTEL_MIC_HOST + tristate "Intel MIC Host Driver" + depends on 64BIT && PCI + default N + help + This enables Host Driver support for the Intel Many Integrated + Core (MIC) family of PCIe form factor coprocessor devices that + run a 64 bit Linux OS. The driver manages card OS state and + enables communication between host and card. Intel MIC X100 + devices are currently supported. + + If you are building a host kernel with an Intel MIC device then + say M (recommended) or Y, else say N. If unsure say N. + + More information about the Intel MIC family as well as the Linux + OS and tools for MIC to use with this driver are available from + . diff --git a/drivers/misc/mic/Makefile b/drivers/misc/mic/Makefile new file mode 100644 index 0000000..8e72421 --- /dev/null +++ b/drivers/misc/mic/Makefile @@ -0,0 +1,5 @@ +# +# Makefile - Intel MIC Linux driver. +# Copyright(c) 2013, Intel Corporation. +# +obj-$(CONFIG_INTEL_MIC_HOST) += host/ diff --git a/drivers/misc/mic/common/mic_device.h b/drivers/misc/mic/common/mic_device.h new file mode 100644 index 0000000..f02262e --- /dev/null +++ b/drivers/misc/mic/common/mic_device.h @@ -0,0 +1,37 @@ +/* + * Intel MIC Platform Software Stack (MPSS) + * + * Copyright(c) 2013 Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, version 2, as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * The full GNU General Public License is included in this distribution in + * the file called "COPYING". + * + * Intel MIC driver. + * + */ +#ifndef __MIC_COMMON_DEVICE_H_ +#define __MIC_COMMON_DEVICE_H_ + +/** + * struct mic_mw - MIC memory window + * + * @pa: Base physical address. + * @va: Base ioremap'd virtual address. + * @len: Size of the memory window. + */ +struct mic_mw { + phys_addr_t pa; + void __iomem *va; + resource_size_t len; +}; + +#endif diff --git a/drivers/misc/mic/host/Makefile b/drivers/misc/mic/host/Makefile new file mode 100644 index 0000000..93b9d25 --- /dev/null +++ b/drivers/misc/mic/host/Makefile @@ -0,0 +1,8 @@ +# +# Makefile - Intel MIC Linux driver. +# Copyright(c) 2013, Intel Corporation. +# +obj-$(CONFIG_INTEL_MIC_HOST) += mic_host.o +mic_host-objs := mic_main.o +mic_host-objs += mic_x100.o +mic_host-objs += mic_sysfs.o diff --git a/drivers/misc/mic/host/mic_device.h b/drivers/misc/mic/host/mic_device.h new file mode 100644 index 0000000..6cd904c --- /dev/null +++ b/drivers/misc/mic/host/mic_device.h @@ -0,0 +1,109 @@ +/* + * Intel MIC Platform Software Stack (MPSS) + * + * Copyright(c) 2013 Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, version 2, as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * The full GNU General Public License is included in this distribution in + * the file called "COPYING". + * + * Intel MIC Host driver. + * + */ +#ifndef _MIC_DEVICE_H_ +#define _MIC_DEVICE_H_ + +/* The maximum number of MIC devices supported in a single host system. */ +#define MIC_MAX_NUM_DEVS 256 + +/** + * enum mic_hw_family - The hardware family to which a device belongs. + */ +enum mic_hw_family { + MIC_FAMILY_X100 = 0, + MIC_FAMILY_UNKNOWN +}; + +/** + * enum mic_stepping - MIC stepping ids. + */ +enum mic_stepping { + MIC_A0_STEP = 0x0, + MIC_B0_STEP = 0x10, + MIC_B1_STEP = 0x11, + MIC_C0_STEP = 0x20, +}; + +/** + * struct mic_device - MIC device information for each card. + * + * @mmio: MMIO bar information. + * @aper: Aperture bar information. + * @family: The MIC family to which this device belongs. + * @ops: MIC HW specific operations. + * @id: The unique device id for this MIC device. + * @stepping: Stepping ID. + * @attr_group: Pointer to list of sysfs attribute groups. + * @sdev: Device for sysfs entries. + */ +struct mic_device { + struct mic_mw mmio; + struct mic_mw aper; + enum mic_hw_family family; + struct mic_hw_ops *ops; + int id; + enum mic_stepping stepping; + const struct attribute_group **attr_group; + struct device *sdev; +}; + +/** + * struct mic_hw_ops - MIC HW specific operations. + * @aper_bar: Aperture bar resource number. + * @mmio_bar: MMIO bar resource number. + * @read_spad: Read from scratch pad register. + * @write_spad: Write to scratch pad register. + */ +struct mic_hw_ops { + u8 aper_bar; + u8 mmio_bar; + u32 (*read_spad)(struct mic_device *mdev, unsigned int idx); + void (*write_spad)(struct mic_device *mdev, unsigned int idx, u32 val); +}; + +/** + * mic_mmio_read - read from an MMIO register. + * @mw: MMIO register base virtual address. + * @offset: register offset. + * + * RETURNS: register value. + */ +static inline u32 mic_mmio_read(struct mic_mw *mw, u32 offset) +{ + return ioread32(mw->va + offset); +} + +/** + * mic_mmio_write - write to an MMIO register. + * @mw: MMIO register base virtual address. + * @val: the data value to put into the register + * @offset: register offset. + * + * RETURNS: none. + */ +static inline void +mic_mmio_write(struct mic_mw *mw, u32 val, u32 offset) +{ + iowrite32(val, mw->va + offset); +} + +void mic_sysfs_init(struct mic_device *mdev); +#endif diff --git a/drivers/misc/mic/host/mic_main.c b/drivers/misc/mic/host/mic_main.c new file mode 100644 index 0000000..228c96c --- /dev/null +++ b/drivers/misc/mic/host/mic_main.c @@ -0,0 +1,309 @@ +/* + * Intel MIC Platform Software Stack (MPSS) + * + * Copyright(c) 2013 Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, version 2, as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * The full GNU General Public License is included in this distribution in + * the file called "COPYING". + * + * Intel MIC Host driver. + * + * Global TODO's across the driver to be added after initial base + * patches are accepted upstream: + * 1) Enable DMA support. + * 2) Enable per vring interrupt support. + */ +#include +#include +#include +#include + +#include "../common/mic_device.h" +#include "mic_device.h" +#include "mic_x100.h" + +static const char mic_driver_name[] = "mic"; + +static DEFINE_PCI_DEVICE_TABLE(mic_pci_tbl) = { + {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MIC_X100_PCI_DEVICE_2250)}, + {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MIC_X100_PCI_DEVICE_2251)}, + {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MIC_X100_PCI_DEVICE_2252)}, + {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MIC_X100_PCI_DEVICE_2253)}, + {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MIC_X100_PCI_DEVICE_2254)}, + {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MIC_X100_PCI_DEVICE_2255)}, + {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MIC_X100_PCI_DEVICE_2256)}, + {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MIC_X100_PCI_DEVICE_2257)}, + {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MIC_X100_PCI_DEVICE_2258)}, + {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MIC_X100_PCI_DEVICE_2259)}, + {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MIC_X100_PCI_DEVICE_225a)}, + {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MIC_X100_PCI_DEVICE_225b)}, + {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MIC_X100_PCI_DEVICE_225c)}, + {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MIC_X100_PCI_DEVICE_225d)}, + {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MIC_X100_PCI_DEVICE_225e)}, + + /* required last entry */ + { 0, } +}; + +MODULE_DEVICE_TABLE(pci, mic_pci_tbl); + +/* ID allocator for MIC devices */ +static struct ida g_mic_ida; +/* Class of MIC devices for sysfs accessibility. */ +static struct class *g_mic_class; +/* Base device node number for MIC devices */ +static dev_t g_mic_devno; + +/** + * mic_ops_init: Initialize HW specific operation tables. + * + * @mdev: pointer to mic_device instance + * + * returns none. + */ +static void mic_ops_init(struct mic_device *mdev) +{ + switch (mdev->family) { + case MIC_FAMILY_X100: + mdev->ops = &mic_x100_ops; + break; + default: + break; + } +} + +/** + * mic_get_family - Determine hardware family to which this MIC belongs. + * + * @pdev: The pci device structure + * + * returns family. + */ +static enum mic_hw_family mic_get_family(struct pci_dev *pdev) +{ + enum mic_hw_family family; + + switch (pdev->device) { + case MIC_X100_PCI_DEVICE_2250: + case MIC_X100_PCI_DEVICE_2251: + case MIC_X100_PCI_DEVICE_2252: + case MIC_X100_PCI_DEVICE_2253: + case MIC_X100_PCI_DEVICE_2254: + case MIC_X100_PCI_DEVICE_2255: + case MIC_X100_PCI_DEVICE_2256: + case MIC_X100_PCI_DEVICE_2257: + case MIC_X100_PCI_DEVICE_2258: + case MIC_X100_PCI_DEVICE_2259: + case MIC_X100_PCI_DEVICE_225a: + case MIC_X100_PCI_DEVICE_225b: + case MIC_X100_PCI_DEVICE_225c: + case MIC_X100_PCI_DEVICE_225d: + case MIC_X100_PCI_DEVICE_225e: + family = MIC_FAMILY_X100; + break; + default: + family = MIC_FAMILY_UNKNOWN; + break; + } + return family; +} + +/** + * mic_device_init - Allocates and initializes the MIC device structure + * + * @mdev: pointer to mic_device instance + * @pdev: The pci device structure + * + * returns none. + */ +static void +mic_device_init(struct mic_device *mdev, struct pci_dev *pdev) +{ + mdev->family = mic_get_family(pdev); + mdev->stepping = pdev->revision; + mic_ops_init(mdev); + mic_sysfs_init(mdev); +} + +/** + * mic_probe - Device Initialization Routine + * + * @pdev: PCI device structure + * @ent: entry in mic_pci_tbl + * + * returns 0 on success, < 0 on failure. + */ +static int mic_probe(struct pci_dev *pdev, + const struct pci_device_id *ent) +{ + int rc; + struct mic_device *mdev; + + mdev = kzalloc(sizeof(*mdev), GFP_KERNEL); + if (!mdev) { + rc = -ENOMEM; + dev_err(&pdev->dev, "mdev kmalloc failed rc %d\n", rc); + goto mdev_alloc_fail; + } + mdev->id = ida_simple_get(&g_mic_ida, 0, MIC_MAX_NUM_DEVS, GFP_KERNEL); + if (mdev->id < 0) { + rc = mdev->id; + dev_err(&pdev->dev, "ida_simple_get failed rc %d\n", rc); + goto ida_fail; + } + + mic_device_init(mdev, pdev); + + rc = pci_enable_device(pdev); + if (rc) { + dev_err(&pdev->dev, "failed to enable pci device.\n"); + goto ida_remove; + } + + pci_set_master(pdev); + + rc = pci_request_regions(pdev, mic_driver_name); + if (rc) { + dev_err(&pdev->dev, "failed to get pci regions.\n"); + goto disable_device; + } + + rc = pci_set_dma_mask(pdev, DMA_BIT_MASK(64)); + if (rc) { + dev_err(&pdev->dev, "Cannot set DMA mask\n"); + goto release_regions; + } + + mdev->mmio.pa = pci_resource_start(pdev, mdev->ops->mmio_bar); + mdev->mmio.len = pci_resource_len(pdev, mdev->ops->mmio_bar); + mdev->mmio.va = pci_ioremap_bar(pdev, mdev->ops->mmio_bar); + if (!mdev->mmio.va) { + dev_err(&pdev->dev, "Cannot remap MMIO BAR\n"); + rc = -EIO; + goto release_regions; + } + + mdev->aper.pa = pci_resource_start(pdev, mdev->ops->aper_bar); + mdev->aper.len = pci_resource_len(pdev, mdev->ops->aper_bar); + mdev->aper.va = ioremap_wc(mdev->aper.pa, mdev->aper.len); + if (!mdev->aper.va) { + dev_err(&pdev->dev, "Cannot remap Aperture BAR\n"); + rc = -EIO; + goto unmap_mmio; + } + + pci_set_drvdata(pdev, mdev); + + mdev->sdev = device_create_with_groups(g_mic_class, &pdev->dev, + MKDEV(MAJOR(g_mic_devno), mdev->id), NULL, + mdev->attr_group, "mic%d", mdev->id); + if (IS_ERR(mdev->sdev)) { + rc = PTR_ERR(mdev->sdev); + dev_err(&pdev->dev, + "device_create_with_groups failed rc %d\n", rc); + goto unmap_aper; + } + return 0; +unmap_aper: + iounmap(mdev->aper.va); +unmap_mmio: + iounmap(mdev->mmio.va); +release_regions: + pci_release_regions(pdev); +disable_device: + pci_disable_device(pdev); +ida_remove: + ida_simple_remove(&g_mic_ida, mdev->id); +ida_fail: + kfree(mdev); +mdev_alloc_fail: + dev_err(&pdev->dev, "Probe failed rc %d\n", rc); + return rc; +} + +/** + * mic_remove - Device Removal Routine + * mic_remove is called by the PCI subsystem to alert the driver + * that it should release a PCI device. + * + * @pdev: PCI device structure + */ +static void mic_remove(struct pci_dev *pdev) +{ + struct mic_device *mdev; + + mdev = pci_get_drvdata(pdev); + if (!mdev) + return; + + device_destroy(g_mic_class, MKDEV(MAJOR(g_mic_devno), mdev->id)); + iounmap(mdev->mmio.va); + iounmap(mdev->aper.va); + pci_release_regions(pdev); + pci_disable_device(pdev); + ida_simple_remove(&g_mic_ida, mdev->id); + kfree(mdev); +} +static struct pci_driver mic_driver = { + .name = mic_driver_name, + .id_table = mic_pci_tbl, + .probe = mic_probe, + .remove = mic_remove +}; + +static int __init mic_init(void) +{ + int ret; + + ret = alloc_chrdev_region(&g_mic_devno, 0, + MIC_MAX_NUM_DEVS, mic_driver_name); + if (ret) { + pr_err("alloc_chrdev_region failed ret %d\n", ret); + goto error; + } + + g_mic_class = class_create(THIS_MODULE, mic_driver_name); + if (IS_ERR(g_mic_class)) { + ret = PTR_ERR(g_mic_class); + pr_err("class_create failed ret %d\n", ret); + goto cleanup_chrdev; + } + + ida_init(&g_mic_ida); + ret = pci_register_driver(&mic_driver); + if (ret) { + pr_err("pci_register_driver failed ret %d\n", ret); + goto class_destroy; + } + return ret; +class_destroy: + class_destroy(g_mic_class); +cleanup_chrdev: + unregister_chrdev_region(g_mic_devno, MIC_MAX_NUM_DEVS); +error: + return ret; +} + +static void __exit mic_exit(void) +{ + pci_unregister_driver(&mic_driver); + ida_destroy(&g_mic_ida); + class_destroy(g_mic_class); + unregister_chrdev_region(g_mic_devno, MIC_MAX_NUM_DEVS); +} + +module_init(mic_init); +module_exit(mic_exit); + +MODULE_AUTHOR("Intel Corporation"); +MODULE_DESCRIPTION("Intel(R) MIC X100 Host driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/misc/mic/host/mic_sysfs.c b/drivers/misc/mic/host/mic_sysfs.c new file mode 100644 index 0000000..972c182 --- /dev/null +++ b/drivers/misc/mic/host/mic_sysfs.c @@ -0,0 +1,97 @@ +/* + * Intel MIC Platform Software Stack (MPSS) + * + * Copyright(c) 2013 Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, version 2, as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * The full GNU General Public License is included in this distribution in + * the file called "COPYING". + * + * Intel MIC Host driver. + * + */ +#include + +#include "../common/mic_device.h" +#include "mic_device.h" + +static ssize_t +mic_show_family(struct device *dev, struct device_attribute *attr, char *buf) +{ + static const char x100[] = "x100"; + static const char unknown[] = "Unknown"; + const char *card = NULL; + struct mic_device *mdev = dev_get_drvdata(dev->parent); + + if (!mdev) + return -EINVAL; + + switch (mdev->family) { + case MIC_FAMILY_X100: + card = x100; + break; + default: + card = unknown; + break; + } + return scnprintf(buf, PAGE_SIZE, "%s\n", card); +} +static DEVICE_ATTR(family, S_IRUGO, mic_show_family, NULL); + +static ssize_t +mic_show_stepping(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct mic_device *mdev = dev_get_drvdata(dev->parent); + char *string = "??"; + + if (!mdev) + return -EINVAL; + + switch (mdev->stepping) { + case MIC_A0_STEP: + string = "A0"; + break; + case MIC_B0_STEP: + string = "B0"; + break; + case MIC_B1_STEP: + string = "B1"; + break; + case MIC_C0_STEP: + string = "C0"; + break; + default: + break; + } + return scnprintf(buf, PAGE_SIZE, "%s\n", string); +} +static DEVICE_ATTR(stepping, S_IRUGO, mic_show_stepping, NULL); + +static struct attribute *mic_default_attrs[] = { + &dev_attr_family.attr, + &dev_attr_stepping.attr, + + NULL +}; + +static struct attribute_group mic_attr_group = { + .attrs = mic_default_attrs, +}; + +static const struct attribute_group *__mic_attr_group[] = { + &mic_attr_group, + NULL +}; + +void mic_sysfs_init(struct mic_device *mdev) +{ + mdev->attr_group = __mic_attr_group; +} diff --git a/drivers/misc/mic/host/mic_x100.c b/drivers/misc/mic/host/mic_x100.c new file mode 100644 index 0000000..da481b1 --- /dev/null +++ b/drivers/misc/mic/host/mic_x100.c @@ -0,0 +1,75 @@ +/* + * Intel MIC Platform Software Stack (MPSS) + * + * Copyright(c) 2013 Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, version 2, as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * The full GNU General Public License is included in this distribution in + * the file called "COPYING". + * + * Intel MIC Host driver. + * + */ +#include +#include + +#include "../common/mic_device.h" +#include "mic_device.h" +#include "mic_x100.h" + +/** + * mic_x100_write_spad - write to the scratchpad register + * @mdev: pointer to mic_device instance + * @idx: index to the scratchpad register, 0 based + * @val: the data value to put into the register + * + * This function allows writing of a 32bit value to the indexed scratchpad + * register. + * + * RETURNS: none. + */ +static void +mic_x100_write_spad(struct mic_device *mdev, unsigned int idx, u32 val) +{ + dev_dbg(mdev->sdev->parent, "Writing 0x%x to scratch pad index %d\n", + val, idx); + mic_mmio_write(&mdev->mmio, val, + MIC_X100_SBOX_BASE_ADDRESS + + MIC_X100_SBOX_SPAD0 + idx * 4); +} + +/** + * mic_x100_read_spad - read from the scratchpad register + * @mdev: pointer to mic_device instance + * @idx: index to scratchpad register, 0 based + * + * This function allows reading of the 32bit scratchpad register. + * + * RETURNS: An appropriate -ERRNO error value on error, or zero for success. + */ +static u32 +mic_x100_read_spad(struct mic_device *mdev, unsigned int idx) +{ + u32 val = mic_mmio_read(&mdev->mmio, + MIC_X100_SBOX_BASE_ADDRESS + + MIC_X100_SBOX_SPAD0 + idx * 4); + + dev_dbg(mdev->sdev->parent, + "Reading 0x%x from scratch pad index %d\n", val, idx); + return val; +} + +struct mic_hw_ops mic_x100_ops = { + .aper_bar = MIC_X100_APER_BAR, + .mmio_bar = MIC_X100_MMIO_BAR, + .read_spad = mic_x100_read_spad, + .write_spad = mic_x100_write_spad, +}; diff --git a/drivers/misc/mic/host/mic_x100.h b/drivers/misc/mic/host/mic_x100.h new file mode 100644 index 0000000..1f4e630 --- /dev/null +++ b/drivers/misc/mic/host/mic_x100.h @@ -0,0 +1,47 @@ +/* + * Intel MIC Platform Software Stack (MPSS) + * + * Copyright(c) 2013 Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, version 2, as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * The full GNU General Public License is included in this distribution in + * the file called "COPYING". + * + * Intel MIC Host driver. + * + */ +#ifndef _MIC_X100_HW_H_ +#define _MIC_X100_HW_H_ + +#define MIC_X100_PCI_DEVICE_2250 0x2250 +#define MIC_X100_PCI_DEVICE_2251 0x2251 +#define MIC_X100_PCI_DEVICE_2252 0x2252 +#define MIC_X100_PCI_DEVICE_2253 0x2253 +#define MIC_X100_PCI_DEVICE_2254 0x2254 +#define MIC_X100_PCI_DEVICE_2255 0x2255 +#define MIC_X100_PCI_DEVICE_2256 0x2256 +#define MIC_X100_PCI_DEVICE_2257 0x2257 +#define MIC_X100_PCI_DEVICE_2258 0x2258 +#define MIC_X100_PCI_DEVICE_2259 0x2259 +#define MIC_X100_PCI_DEVICE_225a 0x225a +#define MIC_X100_PCI_DEVICE_225b 0x225b +#define MIC_X100_PCI_DEVICE_225c 0x225c +#define MIC_X100_PCI_DEVICE_225d 0x225d +#define MIC_X100_PCI_DEVICE_225e 0x225e + +#define MIC_X100_APER_BAR 0 +#define MIC_X100_MMIO_BAR 4 + +#define MIC_X100_SBOX_BASE_ADDRESS 0x00010000 +#define MIC_X100_SBOX_SPAD0 0x0000AB20 +extern struct mic_hw_ops mic_x100_ops; + +#endif -- cgit v0.10.2 From a01e28f692088e9789ebb0c374fdac83de59899b Mon Sep 17 00:00:00 2001 From: Dasaratharaman Chandramouli Date: Thu, 5 Sep 2013 16:41:41 -0700 Subject: Intel MIC Host Driver Interrupt/SMPT support. This patch enables the following features: a) MSIx, MSI and legacy interrupt support. b) System Memory Page Table(SMPT) support. SMPT enables system memory access from the card. On X100 devices the host can program 32 SMPT registers each capable of accessing 16GB of system memory address space from X100 devices. The registers can thereby be used to access a cumulative 512GB of system memory address space from X100 devices at any point in time. Co-author: Sudeep Dutt Signed-off-by: Ashutosh Dixit Signed-off-by: Caz Yokoyama Signed-off-by: Dasaratharaman Chandramouli Signed-off-by: Nikhil Rao Signed-off-by: Harshavardhan R Kharche Signed-off-by: Sudeep Dutt Acked-by: Yaozu (Eddie) Dong Reviewed-by: Peter P Waskiewicz Jr Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/misc/mic/host/Makefile b/drivers/misc/mic/host/Makefile index 93b9d25..6ff5550 100644 --- a/drivers/misc/mic/host/Makefile +++ b/drivers/misc/mic/host/Makefile @@ -6,3 +6,5 @@ obj-$(CONFIG_INTEL_MIC_HOST) += mic_host.o mic_host-objs := mic_main.o mic_host-objs += mic_x100.o mic_host-objs += mic_sysfs.o +mic_host-objs += mic_smpt.o +mic_host-objs += mic_intr.o diff --git a/drivers/misc/mic/host/mic_device.h b/drivers/misc/mic/host/mic_device.h index 6cd904c..dd34b65 100644 --- a/drivers/misc/mic/host/mic_device.h +++ b/drivers/misc/mic/host/mic_device.h @@ -21,6 +21,10 @@ #ifndef _MIC_DEVICE_H_ #define _MIC_DEVICE_H_ +#include + +#include "mic_intr.h" + /* The maximum number of MIC devices supported in a single host system. */ #define MIC_MAX_NUM_DEVS 256 @@ -53,6 +57,12 @@ enum mic_stepping { * @stepping: Stepping ID. * @attr_group: Pointer to list of sysfs attribute groups. * @sdev: Device for sysfs entries. + * @mic_mutex: Mutex for synchronizing access to mic_device. + * @intr_ops: HW specific interrupt operations. + * @smpt_ops: Hardware specific SMPT operations. + * @smpt: MIC SMPT information. + * @intr_info: H/W specific interrupt information. + * @irq_info: The OS specific irq information */ struct mic_device { struct mic_mw mmio; @@ -63,6 +73,12 @@ struct mic_device { enum mic_stepping stepping; const struct attribute_group **attr_group; struct device *sdev; + struct mutex mic_mutex; + struct mic_hw_intr_ops *intr_ops; + struct mic_smpt_ops *smpt_ops; + struct mic_smpt_info *smpt; + struct mic_intr_info *intr_info; + struct mic_irq_info irq_info; }; /** @@ -71,12 +87,17 @@ struct mic_device { * @mmio_bar: MMIO bar resource number. * @read_spad: Read from scratch pad register. * @write_spad: Write to scratch pad register. + * @send_intr: Send an interrupt for a particular doorbell on the card. + * @ack_interrupt: Hardware specific operations to ack the h/w on + * receipt of an interrupt. */ struct mic_hw_ops { u8 aper_bar; u8 mmio_bar; u32 (*read_spad)(struct mic_device *mdev, unsigned int idx); void (*write_spad)(struct mic_device *mdev, unsigned int idx, u32 val); + void (*send_intr)(struct mic_device *mdev, int doorbell); + u32 (*ack_interrupt)(struct mic_device *mdev); }; /** diff --git a/drivers/misc/mic/host/mic_intr.c b/drivers/misc/mic/host/mic_intr.c new file mode 100644 index 0000000..cdae314 --- /dev/null +++ b/drivers/misc/mic/host/mic_intr.c @@ -0,0 +1,628 @@ +/* + * Intel MIC Platform Software Stack (MPSS) + * + * Copyright(c) 2013 Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, version 2, as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * The full GNU General Public License is included in this distribution in + * the file called "COPYING". + * + * Intel MIC Host driver. + * + */ +#include +#include + +#include "../common/mic_device.h" +#include "mic_device.h" + +/* + * mic_invoke_callback - Invoke callback functions registered for + * the corresponding source id. + * + * @mdev: pointer to the mic_device instance + * @idx: The interrupt source id. + * + * Returns none. + */ +static inline void mic_invoke_callback(struct mic_device *mdev, int idx) +{ + struct mic_intr_cb *intr_cb; + struct pci_dev *pdev = container_of(mdev->sdev->parent, + struct pci_dev, dev); + + spin_lock(&mdev->irq_info.mic_intr_lock); + list_for_each_entry(intr_cb, &mdev->irq_info.cb_list[idx], list) + if (intr_cb->func) + intr_cb->func(pdev->irq, intr_cb->data); + spin_unlock(&mdev->irq_info.mic_intr_lock); +} + +/** + * mic_interrupt - Generic interrupt handler for + * MSI and INTx based interrupts. + */ +static irqreturn_t mic_interrupt(int irq, void *dev) +{ + struct mic_device *mdev = dev; + struct mic_intr_info *info = mdev->intr_info; + u32 mask; + int i; + + mask = mdev->ops->ack_interrupt(mdev); + if (!mask) + return IRQ_NONE; + + for (i = info->intr_start_idx[MIC_INTR_DB]; + i < info->intr_len[MIC_INTR_DB]; i++) + if (mask & BIT(i)) + mic_invoke_callback(mdev, i); + + return IRQ_HANDLED; +} + +/* Return the interrupt offset from the index. Index is 0 based. */ +static u16 mic_map_src_to_offset(struct mic_device *mdev, + int intr_src, enum mic_intr_type type) { + + if (type >= MIC_NUM_INTR_TYPES) + return MIC_NUM_OFFSETS; + if (intr_src >= mdev->intr_info->intr_len[type]) + return MIC_NUM_OFFSETS; + + return mdev->intr_info->intr_start_idx[type] + intr_src; +} + +/* Return next available msix_entry. */ +static struct msix_entry *mic_get_available_vector(struct mic_device *mdev) +{ + int i; + struct mic_irq_info *info = &mdev->irq_info; + + for (i = 0; i < info->num_vectors; i++) + if (!info->mic_msi_map[i]) + return &info->msix_entries[i]; + return NULL; +} + +/** + * mic_register_intr_callback - Register a callback handler for the + * given source id. + * + * @mdev: pointer to the mic_device instance + * @idx: The source id to be registered. + * @func: The function to be called when the source id receives + * the interrupt. + * @data: Private data of the requester. + * Return the callback structure that was registered or an + * appropriate error on failure. + */ +static struct mic_intr_cb *mic_register_intr_callback(struct mic_device *mdev, + u8 idx, irqreturn_t (*func) (int irq, void *dev), + void *data) +{ + struct mic_intr_cb *intr_cb; + unsigned long flags; + int rc; + intr_cb = kmalloc(sizeof(struct mic_intr_cb), GFP_KERNEL); + + if (!intr_cb) + return ERR_PTR(-ENOMEM); + + intr_cb->func = func; + intr_cb->data = data; + intr_cb->cb_id = ida_simple_get(&mdev->irq_info.cb_ida, + 0, 0, GFP_KERNEL); + if (intr_cb->cb_id < 0) { + rc = intr_cb->cb_id; + goto ida_fail; + } + + spin_lock_irqsave(&mdev->irq_info.mic_intr_lock, flags); + list_add_tail(&intr_cb->list, &mdev->irq_info.cb_list[idx]); + spin_unlock_irqrestore(&mdev->irq_info.mic_intr_lock, flags); + + return intr_cb; +ida_fail: + kfree(intr_cb); + return ERR_PTR(rc); +} + +/** + * mic_unregister_intr_callback - Unregister the callback handler + * identified by its callback id. + * + * @mdev: pointer to the mic_device instance + * @idx: The callback structure id to be unregistered. + * Return the source id that was unregistered or MIC_NUM_OFFSETS if no + * such callback handler was found. + */ +static u8 mic_unregister_intr_callback(struct mic_device *mdev, u32 idx) +{ + struct list_head *pos, *tmp; + struct mic_intr_cb *intr_cb; + unsigned long flags; + int i; + + for (i = 0; i < MIC_NUM_OFFSETS; i++) { + spin_lock_irqsave(&mdev->irq_info.mic_intr_lock, flags); + list_for_each_safe(pos, tmp, &mdev->irq_info.cb_list[i]) { + intr_cb = list_entry(pos, struct mic_intr_cb, list); + if (intr_cb->cb_id == idx) { + list_del(pos); + ida_simple_remove(&mdev->irq_info.cb_ida, + intr_cb->cb_id); + kfree(intr_cb); + spin_unlock_irqrestore( + &mdev->irq_info.mic_intr_lock, flags); + return i; + } + } + spin_unlock_irqrestore(&mdev->irq_info.mic_intr_lock, flags); + } + return MIC_NUM_OFFSETS; +} + +/** + * mic_setup_msix - Initializes MSIx interrupts. + * + * @mdev: pointer to mic_device instance + * + * + * RETURNS: An appropriate -ERRNO error value on error, or zero for success. + */ +static int mic_setup_msix(struct mic_device *mdev, struct pci_dev *pdev) +{ + int rc, i; + + mdev->irq_info.msix_entries = kmalloc(sizeof(struct msix_entry) * + MIC_MIN_MSIX, GFP_KERNEL); + if (!mdev->irq_info.msix_entries) { + rc = -ENOMEM; + goto err_nomem1; + } + + for (i = 0; i < MIC_MIN_MSIX; i++) + mdev->irq_info.msix_entries[i].entry = i; + + rc = pci_enable_msix(pdev, mdev->irq_info.msix_entries, + MIC_MIN_MSIX); + if (rc) { + dev_dbg(&pdev->dev, "Error enabling MSIx. rc = %d\n", rc); + goto err_enable_msix; + } + + mdev->irq_info.num_vectors = MIC_MIN_MSIX; + mdev->irq_info.mic_msi_map = kzalloc((sizeof(u32) * + mdev->irq_info.num_vectors), GFP_KERNEL); + + if (!mdev->irq_info.mic_msi_map) { + rc = -ENOMEM; + goto err_nomem2; + } + + dev_dbg(mdev->sdev->parent, + "%d MSIx irqs setup\n", mdev->irq_info.num_vectors); + return 0; +err_nomem2: + pci_disable_msix(pdev); +err_enable_msix: + kfree(mdev->irq_info.msix_entries); +err_nomem1: + mdev->irq_info.num_vectors = 0; + return rc; +} + +/** + * mic_setup_callbacks - Initialize data structures needed + * to handle callbacks. + * + * @mdev: pointer to mic_device instance + */ +static int mic_setup_callbacks(struct mic_device *mdev) +{ + int i; + + mdev->irq_info.cb_list = kmalloc(sizeof(struct list_head) * + MIC_NUM_OFFSETS, GFP_KERNEL); + if (!mdev->irq_info.cb_list) + return -ENOMEM; + + for (i = 0; i < MIC_NUM_OFFSETS; i++) + INIT_LIST_HEAD(&mdev->irq_info.cb_list[i]); + ida_init(&mdev->irq_info.cb_ida); + spin_lock_init(&mdev->irq_info.mic_intr_lock); + return 0; +} + +/** + * mic_release_callbacks - Uninitialize data structures needed + * to handle callbacks. + * + * @mdev: pointer to mic_device instance + */ +static void mic_release_callbacks(struct mic_device *mdev) +{ + unsigned long flags; + struct list_head *pos, *tmp; + struct mic_intr_cb *intr_cb; + int i; + + for (i = 0; i < MIC_NUM_OFFSETS; i++) { + spin_lock_irqsave(&mdev->irq_info.mic_intr_lock, flags); + + if (list_empty(&mdev->irq_info.cb_list[i])) { + spin_unlock_irqrestore(&mdev->irq_info.mic_intr_lock, + flags); + break; + } + + list_for_each_safe(pos, tmp, &mdev->irq_info.cb_list[i]) { + intr_cb = list_entry(pos, struct mic_intr_cb, list); + list_del(pos); + ida_simple_remove(&mdev->irq_info.cb_ida, + intr_cb->cb_id); + kfree(intr_cb); + } + spin_unlock_irqrestore(&mdev->irq_info.mic_intr_lock, flags); + } + ida_destroy(&mdev->irq_info.cb_ida); + kfree(mdev->irq_info.cb_list); +} + +/** + * mic_setup_msi - Initializes MSI interrupts. + * + * @mdev: pointer to mic_device instance + * @pdev: PCI device structure + * + * RETURNS: An appropriate -ERRNO error value on error, or zero for success. + */ +static int mic_setup_msi(struct mic_device *mdev, struct pci_dev *pdev) +{ + int rc; + + rc = pci_enable_msi(pdev); + if (rc) { + dev_dbg(&pdev->dev, "Error enabling MSI. rc = %d\n", rc); + return rc; + } + + mdev->irq_info.num_vectors = 1; + mdev->irq_info.mic_msi_map = kzalloc((sizeof(u32) * + mdev->irq_info.num_vectors), GFP_KERNEL); + + if (!mdev->irq_info.mic_msi_map) { + rc = -ENOMEM; + goto err_nomem1; + } + + rc = mic_setup_callbacks(mdev); + if (rc) { + dev_err(&pdev->dev, "Error setting up callbacks\n"); + goto err_nomem2; + } + + rc = request_irq(pdev->irq, mic_interrupt, 0 , "mic-msi", mdev); + if (rc) { + dev_err(&pdev->dev, "Error allocating MSI interrupt\n"); + goto err_irq_req_fail; + } + + dev_dbg(&pdev->dev, "%d MSI irqs setup\n", mdev->irq_info.num_vectors); + return 0; +err_irq_req_fail: + mic_release_callbacks(mdev); +err_nomem2: + kfree(mdev->irq_info.mic_msi_map); +err_nomem1: + pci_disable_msi(pdev); + mdev->irq_info.num_vectors = 0; + return rc; +} + +/** + * mic_setup_intx - Initializes legacy interrupts. + * + * @mdev: pointer to mic_device instance + * @pdev: PCI device structure + * + * RETURNS: An appropriate -ERRNO error value on error, or zero for success. + */ +static int mic_setup_intx(struct mic_device *mdev, struct pci_dev *pdev) +{ + int rc; + + pci_msi_off(pdev); + + /* Enable intx */ + pci_intx(pdev, 1); + rc = mic_setup_callbacks(mdev); + if (rc) { + dev_err(&pdev->dev, "Error setting up callbacks\n"); + goto err_nomem; + } + + rc = request_irq(pdev->irq, mic_interrupt, + IRQF_SHARED, "mic-intx", mdev); + if (rc) + goto err; + + dev_dbg(&pdev->dev, "intx irq setup\n"); + return 0; +err: + mic_release_callbacks(mdev); +err_nomem: + return rc; +} + +/** + * mic_next_db - Retrieve the next doorbell interrupt source id. + * The id is picked sequentially from the available pool of + * doorlbell ids. + * + * @mdev: pointer to the mic_device instance. + * + * Returns the next doorbell interrupt source. + */ +int mic_next_db(struct mic_device *mdev) +{ + int next_db; + + next_db = mdev->irq_info.next_avail_src % + mdev->intr_info->intr_len[MIC_INTR_DB]; + mdev->irq_info.next_avail_src++; + return next_db; +} + +#define COOKIE_ID_SHIFT 16 +#define GET_ENTRY(cookie) ((cookie) & 0xFFFF) +#define GET_OFFSET(cookie) ((cookie) >> COOKIE_ID_SHIFT) +#define MK_COOKIE(x, y) ((x) | (y) << COOKIE_ID_SHIFT) + +/** + * mic_request_irq - request an irq. mic_mutex needs + * to be held before calling this function. + * + * @mdev: pointer to mic_device instance + * @func: The callback function that handles the interrupt. + * The function needs to call ack_interrupts + * (mdev->ops->ack_interrupt(mdev)) when handling the interrupts. + * @name: The ASCII name of the callee requesting the irq. + * @data: private data that is returned back when calling the + * function handler. + * @intr_src: The source id of the requester. Its the doorbell id + * for Doorbell interrupts and DMA channel id for DMA interrupts. + * @type: The type of interrupt. Values defined in mic_intr_type + * + * returns: The cookie that is transparent to the caller. Passed + * back when calling mic_free_irq. An appropriate error code + * is returned on failure. Caller needs to use IS_ERR(return_val) + * to check for failure and PTR_ERR(return_val) to obtained the + * error code. + * + */ +struct mic_irq *mic_request_irq(struct mic_device *mdev, + irqreturn_t (*func)(int irq, void *dev), + const char *name, void *data, int intr_src, + enum mic_intr_type type) +{ + u16 offset; + int rc = 0; + struct msix_entry *msix = NULL; + unsigned long cookie = 0; + u16 entry; + struct mic_intr_cb *intr_cb; + struct pci_dev *pdev = container_of(mdev->sdev->parent, + struct pci_dev, dev); + + offset = mic_map_src_to_offset(mdev, intr_src, type); + if (offset >= MIC_NUM_OFFSETS) { + dev_err(mdev->sdev->parent, + "Error mapping index %d to a valid source id.\n", + intr_src); + rc = -EINVAL; + goto err; + } + + if (mdev->irq_info.num_vectors > 1) { + msix = mic_get_available_vector(mdev); + if (!msix) { + dev_err(mdev->sdev->parent, + "No MSIx vectors available for use.\n"); + rc = -ENOSPC; + goto err; + } + + rc = request_irq(msix->vector, func, 0, name, data); + if (rc) { + dev_dbg(mdev->sdev->parent, + "request irq failed rc = %d\n", rc); + goto err; + } + entry = msix->entry; + mdev->irq_info.mic_msi_map[entry] |= BIT(offset); + mdev->intr_ops->program_msi_to_src_map(mdev, + entry, offset, true); + cookie = MK_COOKIE(entry, offset); + dev_dbg(mdev->sdev->parent, "irq: %d assigned for src: %d\n", + msix->vector, intr_src); + } else { + intr_cb = mic_register_intr_callback(mdev, + offset, func, data); + if (IS_ERR(intr_cb)) { + dev_err(mdev->sdev->parent, + "No available callback entries for use\n"); + rc = PTR_ERR(intr_cb); + goto err; + } + + entry = 0; + if (pci_dev_msi_enabled(pdev)) { + mdev->irq_info.mic_msi_map[entry] |= (1 << offset); + mdev->intr_ops->program_msi_to_src_map(mdev, + entry, offset, true); + } + cookie = MK_COOKIE(entry, intr_cb->cb_id); + dev_dbg(mdev->sdev->parent, "callback %d registered for src: %d\n", + intr_cb->cb_id, intr_src); + } + return (struct mic_irq *)cookie; +err: + return ERR_PTR(rc); +} + +/** + * mic_free_irq - free irq. mic_mutex + * needs to be held before calling this function. + * + * @mdev: pointer to mic_device instance + * @cookie: cookie obtained during a successful call to mic_request_irq + * @data: private data specified by the calling function during the + * mic_request_irq + * + * returns: none. + */ +void mic_free_irq(struct mic_device *mdev, + struct mic_irq *cookie, void *data) +{ + u32 offset; + u32 entry; + u8 src_id; + unsigned int irq; + struct pci_dev *pdev = container_of(mdev->sdev->parent, + struct pci_dev, dev); + + entry = GET_ENTRY((unsigned long)cookie); + offset = GET_OFFSET((unsigned long)cookie); + if (mdev->irq_info.num_vectors > 1) { + if (entry >= mdev->irq_info.num_vectors) { + dev_warn(mdev->sdev->parent, + "entry %d should be < num_irq %d\n", + entry, mdev->irq_info.num_vectors); + return; + } + irq = mdev->irq_info.msix_entries[entry].vector; + free_irq(irq, data); + mdev->irq_info.mic_msi_map[entry] &= ~(BIT(offset)); + mdev->intr_ops->program_msi_to_src_map(mdev, + entry, offset, false); + + dev_dbg(mdev->sdev->parent, "irq: %d freed\n", irq); + } else { + irq = pdev->irq; + src_id = mic_unregister_intr_callback(mdev, offset); + if (src_id >= MIC_NUM_OFFSETS) { + dev_warn(mdev->sdev->parent, "Error unregistering callback\n"); + return; + } + if (pci_dev_msi_enabled(pdev)) { + mdev->irq_info.mic_msi_map[entry] &= ~(BIT(src_id)); + mdev->intr_ops->program_msi_to_src_map(mdev, + entry, src_id, false); + } + dev_dbg(mdev->sdev->parent, "callback %d unregistered for src: %d\n", + offset, src_id); + } +} + +/** + * mic_setup_interrupts - Initializes interrupts. + * + * @mdev: pointer to mic_device instance + * @pdev: PCI device structure + * + * RETURNS: An appropriate -ERRNO error value on error, or zero for success. + */ +int mic_setup_interrupts(struct mic_device *mdev, struct pci_dev *pdev) +{ + int rc; + + rc = mic_setup_msix(mdev, pdev); + if (!rc) + goto done; + + rc = mic_setup_msi(mdev, pdev); + if (!rc) + goto done; + + rc = mic_setup_intx(mdev, pdev); + if (rc) { + dev_err(mdev->sdev->parent, "no usable interrupts\n"); + return rc; + } +done: + mdev->intr_ops->enable_interrupts(mdev); + return 0; +} + +/** + * mic_free_interrupts - Frees interrupts setup by mic_setup_interrupts + * + * @mdev: pointer to mic_device instance + * @pdev: PCI device structure + * + * returns none. + */ +void mic_free_interrupts(struct mic_device *mdev, struct pci_dev *pdev) +{ + int i; + + mdev->intr_ops->disable_interrupts(mdev); + if (mdev->irq_info.num_vectors > 1) { + for (i = 0; i < mdev->irq_info.num_vectors; i++) { + if (mdev->irq_info.mic_msi_map[i]) + dev_warn(&pdev->dev, "irq %d may still be in use.\n", + mdev->irq_info.msix_entries[i].vector); + } + kfree(mdev->irq_info.mic_msi_map); + kfree(mdev->irq_info.msix_entries); + pci_disable_msix(pdev); + } else { + if (pci_dev_msi_enabled(pdev)) { + free_irq(pdev->irq, mdev); + kfree(mdev->irq_info.mic_msi_map); + pci_disable_msi(pdev); + } else { + free_irq(pdev->irq, mdev); + } + mic_release_callbacks(mdev); + } +} + +/** + * mic_intr_restore - Restore MIC interrupt registers. + * + * @mdev: pointer to mic_device instance. + * + * Restore the interrupt registers to values previously + * stored in the SW data structures. mic_mutex needs to + * be held before calling this function. + * + * returns None. + */ +void mic_intr_restore(struct mic_device *mdev) +{ + int entry, offset; + struct pci_dev *pdev = container_of(mdev->sdev->parent, + struct pci_dev, dev); + + if (!pci_dev_msi_enabled(pdev)) + return; + + for (entry = 0; entry < mdev->irq_info.num_vectors; entry++) { + for (offset = 0; offset < MIC_NUM_OFFSETS; offset++) { + if (mdev->irq_info.mic_msi_map[entry] & BIT(offset)) + mdev->intr_ops->program_msi_to_src_map(mdev, + entry, offset, true); + } + } +} diff --git a/drivers/misc/mic/host/mic_intr.h b/drivers/misc/mic/host/mic_intr.h new file mode 100644 index 0000000..6091aa9 --- /dev/null +++ b/drivers/misc/mic/host/mic_intr.h @@ -0,0 +1,137 @@ +/* + * Intel MIC Platform Software Stack (MPSS) + * + * Copyright(c) 2013 Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, version 2, as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * The full GNU General Public License is included in this distribution in + * the file called "COPYING". + * + * Intel MIC Host driver. + * + */ +#ifndef _MIC_INTR_H_ +#define _MIC_INTR_H_ + +/* + * The minimum number of msix vectors required for normal operation. + * 3 for virtio network, console and block devices. + * 1 for card shutdown notifications. + */ +#define MIC_MIN_MSIX 4 +#define MIC_NUM_OFFSETS 32 + +/** + * mic_intr_source - The type of source that will generate + * the interrupt.The number of types needs to be in sync with + * MIC_NUM_INTR_TYPES + * + * MIC_INTR_DB: The source is a doorbell + * MIC_INTR_DMA: The source is a DMA channel + * MIC_INTR_ERR: The source is an error interrupt e.g. SBOX ERR + * MIC_NUM_INTR_TYPES: Total number of interrupt sources. + */ +enum mic_intr_type { + MIC_INTR_DB = 0, + MIC_INTR_DMA, + MIC_INTR_ERR, + MIC_NUM_INTR_TYPES +}; + +/** + * struct mic_intr_info - Contains h/w specific interrupt sources + * information. + * + * @intr_start_idx: Contains the starting indexes of the + * interrupt types. + * @intr_len: Contains the length of the interrupt types. + */ +struct mic_intr_info { + u16 intr_start_idx[MIC_NUM_INTR_TYPES]; + u16 intr_len[MIC_NUM_INTR_TYPES]; +}; + +/** + * struct mic_irq_info - OS specific irq information + * + * @next_avail_src: next available doorbell that can be assigned. + * @msix_entries: msix entries allocated while setting up MSI-x + * @mic_msi_map: The MSI/MSI-x mapping information. + * @num_vectors: The number of MSI/MSI-x vectors that have been allocated. + * @cb_ida: callback ID allocator to track the callbacks registered. + * @mic_intr_lock: spinlock to protect the interrupt callback list. + * @cb_list: Array of callback lists one for each source. + */ +struct mic_irq_info { + int next_avail_src; + struct msix_entry *msix_entries; + u32 *mic_msi_map; + u16 num_vectors; + struct ida cb_ida; + spinlock_t mic_intr_lock; + struct list_head *cb_list; +}; + +/** + * struct mic_intr_cb - Interrupt callback structure. + * + * @func: The callback function + * @data: Private data of the requester. + * @cb_id: The callback id. Identifies this callback. + * @list: list head pointing to the next callback structure. + */ +struct mic_intr_cb { + irqreturn_t (*func) (int irq, void *data); + void *data; + int cb_id; + struct list_head list; +}; + +/** + * struct mic_irq - opaque pointer used as cookie + */ +struct mic_irq; + +/* Forward declaration */ +struct mic_device; + +/** + * struct mic_hw_intr_ops: MIC HW specific interrupt operations + * @intr_init: Initialize H/W specific interrupt information. + * @enable_interrupts: Enable interrupts from the hardware. + * @disable_interrupts: Disable interrupts from the hardware. + * @program_msi_to_src_map: Update MSI mapping registers with + * irq information. + * @read_msi_to_src_map: Read MSI mapping registers containing + * irq information. + */ +struct mic_hw_intr_ops { + void (*intr_init)(struct mic_device *mdev); + void (*enable_interrupts)(struct mic_device *mdev); + void (*disable_interrupts)(struct mic_device *mdev); + void (*program_msi_to_src_map) (struct mic_device *mdev, + int idx, int intr_src, bool set); + u32 (*read_msi_to_src_map) (struct mic_device *mdev, + int idx); +}; + +int mic_next_db(struct mic_device *mdev); +struct mic_irq *mic_request_irq(struct mic_device *mdev, + irqreturn_t (*func)(int irq, void *data), + const char *name, void *data, int intr_src, + enum mic_intr_type type); + +void mic_free_irq(struct mic_device *mdev, + struct mic_irq *cookie, void *data); +int mic_setup_interrupts(struct mic_device *mdev, struct pci_dev *pdev); +void mic_free_interrupts(struct mic_device *mdev, struct pci_dev *pdev); +void mic_intr_restore(struct mic_device *mdev); +#endif diff --git a/drivers/misc/mic/host/mic_main.c b/drivers/misc/mic/host/mic_main.c index 228c96c..332a15e 100644 --- a/drivers/misc/mic/host/mic_main.c +++ b/drivers/misc/mic/host/mic_main.c @@ -23,13 +23,13 @@ * 2) Enable per vring interrupt support. */ #include -#include #include #include #include "../common/mic_device.h" #include "mic_device.h" #include "mic_x100.h" +#include "mic_smpt.h" static const char mic_driver_name[] = "mic"; @@ -75,6 +75,8 @@ static void mic_ops_init(struct mic_device *mdev) switch (mdev->family) { case MIC_FAMILY_X100: mdev->ops = &mic_x100_ops; + mdev->intr_ops = &mic_x100_intr_ops; + mdev->smpt_ops = &mic_x100_smpt_ops; break; default: break; @@ -132,6 +134,8 @@ mic_device_init(struct mic_device *mdev, struct pci_dev *pdev) mdev->stepping = pdev->revision; mic_ops_init(mdev); mic_sysfs_init(mdev); + mutex_init(&mdev->mic_mutex); + mdev->irq_info.next_avail_src = 0; } /** @@ -201,6 +205,18 @@ static int mic_probe(struct pci_dev *pdev, goto unmap_mmio; } + mdev->intr_ops->intr_init(mdev); + rc = mic_setup_interrupts(mdev, pdev); + if (rc) { + dev_err(&pdev->dev, "mic_setup_interrupts failed %d\n", rc); + goto unmap_aper; + } + rc = mic_smpt_init(mdev); + if (rc) { + dev_err(&pdev->dev, "smpt_init failed %d\n", rc); + goto free_interrupts; + } + pci_set_drvdata(pdev, mdev); mdev->sdev = device_create_with_groups(g_mic_class, &pdev->dev, @@ -210,9 +226,13 @@ static int mic_probe(struct pci_dev *pdev, rc = PTR_ERR(mdev->sdev); dev_err(&pdev->dev, "device_create_with_groups failed rc %d\n", rc); - goto unmap_aper; + goto smpt_uninit; } return 0; +smpt_uninit: + mic_smpt_uninit(mdev); +free_interrupts: + mic_free_interrupts(mdev, pdev); unmap_aper: iounmap(mdev->aper.va); unmap_mmio: @@ -246,6 +266,8 @@ static void mic_remove(struct pci_dev *pdev) return; device_destroy(g_mic_class, MKDEV(MAJOR(g_mic_devno), mdev->id)); + mic_smpt_uninit(mdev); + mic_free_interrupts(mdev, pdev); iounmap(mdev->mmio.va); iounmap(mdev->aper.va); pci_release_regions(pdev); diff --git a/drivers/misc/mic/host/mic_smpt.c b/drivers/misc/mic/host/mic_smpt.c new file mode 100644 index 0000000..a346207 --- /dev/null +++ b/drivers/misc/mic/host/mic_smpt.c @@ -0,0 +1,442 @@ +/* + * Intel MIC Platform Software Stack (MPSS) + * + * Copyright(c) 2013 Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, version 2, as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * The full GNU General Public License is included in this distribution in + * the file called "COPYING". + * + * Intel MIC Host driver. + * + */ +#include + +#include "../common/mic_device.h" +#include "mic_device.h" +#include "mic_smpt.h" + +static inline u64 mic_system_page_mask(struct mic_device *mdev) +{ + return (1ULL << mdev->smpt->info.page_shift) - 1ULL; +} + +static inline u8 mic_sys_addr_to_smpt(struct mic_device *mdev, dma_addr_t pa) +{ + return (pa - mdev->smpt->info.base) >> mdev->smpt->info.page_shift; +} + +static inline u64 mic_smpt_to_pa(struct mic_device *mdev, u8 index) +{ + return mdev->smpt->info.base + (index * mdev->smpt->info.page_size); +} + +static inline u64 mic_smpt_offset(struct mic_device *mdev, dma_addr_t pa) +{ + return pa & mic_system_page_mask(mdev); +} + +static inline u64 mic_smpt_align_low(struct mic_device *mdev, dma_addr_t pa) +{ + return ALIGN(pa - mic_system_page_mask(mdev), + mdev->smpt->info.page_size); +} + +static inline u64 mic_smpt_align_high(struct mic_device *mdev, dma_addr_t pa) +{ + return ALIGN(pa, mdev->smpt->info.page_size); +} + +/* Total Cumulative system memory accessible by MIC across all SMPT entries */ +static inline u64 mic_max_system_memory(struct mic_device *mdev) +{ + return mdev->smpt->info.num_reg * mdev->smpt->info.page_size; +} + +/* Maximum system memory address accessible by MIC */ +static inline u64 mic_max_system_addr(struct mic_device *mdev) +{ + return mdev->smpt->info.base + mic_max_system_memory(mdev) - 1ULL; +} + +/* Check if the DMA address is a MIC system memory address */ +static inline bool +mic_is_system_addr(struct mic_device *mdev, dma_addr_t pa) +{ + return pa >= mdev->smpt->info.base && pa <= mic_max_system_addr(mdev); +} + +/* Populate an SMPT entry and update the reference counts. */ +static void mic_add_smpt_entry(int spt, s64 *ref, u64 addr, + int entries, struct mic_device *mdev) +{ + struct mic_smpt_info *smpt_info = mdev->smpt; + int i; + + for (i = spt; i < spt + entries; i++, + addr += smpt_info->info.page_size) { + if (!smpt_info->entry[i].ref_count && + (smpt_info->entry[i].dma_addr != addr)) { + mdev->smpt_ops->set(mdev, addr, i); + smpt_info->entry[i].dma_addr = addr; + } + smpt_info->entry[i].ref_count += ref[i - spt]; + } +} + +/* + * Find an available MIC address in MIC SMPT address space + * for a given DMA address and size. + */ +static dma_addr_t mic_smpt_op(struct mic_device *mdev, u64 dma_addr, + int entries, s64 *ref, size_t size) +{ + int spt; + int ae = 0; + int i; + unsigned long flags; + dma_addr_t mic_addr = 0; + dma_addr_t addr = dma_addr; + struct mic_smpt_info *smpt_info = mdev->smpt; + + spin_lock_irqsave(&smpt_info->smpt_lock, flags); + + /* find existing entries */ + for (i = 0; i < smpt_info->info.num_reg; i++) { + if (smpt_info->entry[i].dma_addr == addr) { + ae++; + addr += smpt_info->info.page_size; + } else if (ae) /* cannot find contiguous entries */ + goto not_found; + + if (ae == entries) + goto found; + } + + /* find free entry */ + for (ae = 0, i = 0; i < smpt_info->info.num_reg; i++) { + ae = (smpt_info->entry[i].ref_count == 0) ? ae + 1 : 0; + if (ae == entries) + goto found; + } + +not_found: + spin_unlock_irqrestore(&smpt_info->smpt_lock, flags); + return mic_addr; + +found: + spt = i - entries + 1; + mic_addr = mic_smpt_to_pa(mdev, spt); + mic_add_smpt_entry(spt, ref, dma_addr, entries, mdev); + smpt_info->map_count++; + smpt_info->ref_count += (s64)size; + spin_unlock_irqrestore(&smpt_info->smpt_lock, flags); + return mic_addr; +} + +/* + * Returns number of smpt entries needed for dma_addr to dma_addr + size + * also returns the reference count array for each of those entries + * and the starting smpt address + */ +static int mic_get_smpt_ref_count(struct mic_device *mdev, dma_addr_t dma_addr, + size_t size, s64 *ref, u64 *smpt_start) +{ + u64 start = dma_addr; + u64 end = dma_addr + size; + int i = 0; + + while (start < end) { + ref[i++] = min(mic_smpt_align_high(mdev, start + 1), + end) - start; + start = mic_smpt_align_high(mdev, start + 1); + } + + if (smpt_start) + *smpt_start = mic_smpt_align_low(mdev, dma_addr); + + return i; +} + +/* + * mic_to_dma_addr - Converts a MIC address to a DMA address. + * + * @mdev: pointer to mic_device instance. + * @mic_addr: MIC address. + * + * returns a DMA address. + */ +static dma_addr_t +mic_to_dma_addr(struct mic_device *mdev, dma_addr_t mic_addr) +{ + struct mic_smpt_info *smpt_info = mdev->smpt; + int spt; + dma_addr_t dma_addr; + + if (!mic_is_system_addr(mdev, mic_addr)) { + dev_err(mdev->sdev->parent, + "mic_addr is invalid. mic_addr = 0x%llx\n", mic_addr); + return -EINVAL; + } + spt = mic_sys_addr_to_smpt(mdev, mic_addr); + dma_addr = smpt_info->entry[spt].dma_addr + + mic_smpt_offset(mdev, mic_addr); + return dma_addr; +} + +/** + * mic_map - Maps a DMA address to a MIC physical address. + * + * @mdev: pointer to mic_device instance. + * @dma_addr: DMA address. + * @size: Size of the region to be mapped. + * + * This API converts the DMA address provided to a DMA address understood + * by MIC. Caller should check for errors by calling mic_map_error(..). + * + * returns DMA address as required by MIC. + */ +dma_addr_t mic_map(struct mic_device *mdev, dma_addr_t dma_addr, size_t size) +{ + dma_addr_t mic_addr = 0; + int num_entries; + s64 *ref; + u64 smpt_start; + + if (!size || size > mic_max_system_memory(mdev)) + return mic_addr; + + ref = kmalloc(mdev->smpt->info.num_reg * sizeof(s64), GFP_KERNEL); + if (!ref) + return mic_addr; + + num_entries = mic_get_smpt_ref_count(mdev, dma_addr, size, + ref, &smpt_start); + + /* Set the smpt table appropriately and get 16G aligned mic address */ + mic_addr = mic_smpt_op(mdev, smpt_start, num_entries, ref, size); + + kfree(ref); + + /* + * If mic_addr is zero then its an error case + * since mic_addr can never be zero. + * else generate mic_addr by adding the 16G offset in dma_addr + */ + if (!mic_addr && MIC_FAMILY_X100 == mdev->family) { + dev_err(mdev->sdev->parent, + "mic_map failed dma_addr 0x%llx size 0x%lx\n", + dma_addr, size); + return mic_addr; + } else { + return mic_addr + mic_smpt_offset(mdev, dma_addr); + } +} + +/** + * mic_unmap - Unmaps a MIC physical address. + * + * @mdev: pointer to mic_device instance. + * @mic_addr: MIC physical address. + * @size: Size of the region to be unmapped. + * + * This API unmaps the mappings created by mic_map(..). + * + * returns None. + */ +void mic_unmap(struct mic_device *mdev, dma_addr_t mic_addr, size_t size) +{ + struct mic_smpt_info *smpt_info = mdev->smpt; + s64 *ref; + int num_smpt; + int spt; + int i; + unsigned long flags; + + if (!size) + return; + + if (!mic_is_system_addr(mdev, mic_addr)) { + dev_err(mdev->sdev->parent, + "invalid address: 0x%llx\n", mic_addr); + return; + } + + spt = mic_sys_addr_to_smpt(mdev, mic_addr); + ref = kmalloc(mdev->smpt->info.num_reg * sizeof(s64), GFP_KERNEL); + if (!ref) + return; + + /* Get number of smpt entries to be mapped, ref count array */ + num_smpt = mic_get_smpt_ref_count(mdev, mic_addr, size, ref, NULL); + + spin_lock_irqsave(&smpt_info->smpt_lock, flags); + smpt_info->unmap_count++; + smpt_info->ref_count -= (s64)size; + + for (i = spt; i < spt + num_smpt; i++) { + smpt_info->entry[i].ref_count -= ref[i - spt]; + if (smpt_info->entry[i].ref_count < 0) + dev_warn(mdev->sdev->parent, + "ref count for entry %d is negative\n", i); + } + spin_unlock_irqrestore(&smpt_info->smpt_lock, flags); + kfree(ref); +} + +/** + * mic_map_single - Maps a virtual address to a MIC physical address. + * + * @mdev: pointer to mic_device instance. + * @va: Kernel direct mapped virtual address. + * @size: Size of the region to be mapped. + * + * This API calls pci_map_single(..) for the direct mapped virtual address + * and then converts the DMA address provided to a DMA address understood + * by MIC. Caller should check for errors by calling mic_map_error(..). + * + * returns DMA address as required by MIC. + */ +dma_addr_t mic_map_single(struct mic_device *mdev, void *va, size_t size) +{ + dma_addr_t mic_addr = 0; + struct pci_dev *pdev = container_of(mdev->sdev->parent, + struct pci_dev, dev); + dma_addr_t dma_addr = + pci_map_single(pdev, va, size, PCI_DMA_BIDIRECTIONAL); + + if (!pci_dma_mapping_error(pdev, dma_addr)) { + mic_addr = mic_map(mdev, dma_addr, size); + if (!mic_addr) { + dev_err(mdev->sdev->parent, + "mic_map failed dma_addr 0x%llx size 0x%lx\n", + dma_addr, size); + pci_unmap_single(pdev, dma_addr, + size, PCI_DMA_BIDIRECTIONAL); + } + } + return mic_addr; +} + +/** + * mic_unmap_single - Unmaps a MIC physical address. + * + * @mdev: pointer to mic_device instance. + * @mic_addr: MIC physical address. + * @size: Size of the region to be unmapped. + * + * This API unmaps the mappings created by mic_map_single(..). + * + * returns None. + */ +void +mic_unmap_single(struct mic_device *mdev, dma_addr_t mic_addr, size_t size) +{ + struct pci_dev *pdev = container_of(mdev->sdev->parent, + struct pci_dev, dev); + dma_addr_t dma_addr = mic_to_dma_addr(mdev, mic_addr); + mic_unmap(mdev, mic_addr, size); + pci_unmap_single(pdev, dma_addr, size, PCI_DMA_BIDIRECTIONAL); +} + +/** + * mic_smpt_init - Initialize MIC System Memory Page Tables. + * + * @mdev: pointer to mic_device instance. + * + * returns 0 for success and -errno for error. + */ +int mic_smpt_init(struct mic_device *mdev) +{ + int i, err = 0; + dma_addr_t dma_addr; + struct mic_smpt_info *smpt_info; + + mdev->smpt = kmalloc(sizeof(*mdev->smpt), GFP_KERNEL); + if (!mdev->smpt) + return -ENOMEM; + + smpt_info = mdev->smpt; + mdev->smpt_ops->init(mdev); + smpt_info->entry = kmalloc(sizeof(struct mic_smpt) + * smpt_info->info.num_reg, GFP_KERNEL); + if (!smpt_info->entry) { + err = -ENOMEM; + goto free_smpt; + } + spin_lock_init(&smpt_info->smpt_lock); + for (i = 0; i < smpt_info->info.num_reg; i++) { + dma_addr = i * smpt_info->info.page_size; + smpt_info->entry[i].dma_addr = dma_addr; + smpt_info->entry[i].ref_count = 0; + mdev->smpt_ops->set(mdev, dma_addr, i); + } + smpt_info->ref_count = 0; + smpt_info->map_count = 0; + smpt_info->unmap_count = 0; + return 0; +free_smpt: + kfree(smpt_info); + return err; +} + +/** + * mic_smpt_uninit - UnInitialize MIC System Memory Page Tables. + * + * @mdev: pointer to mic_device instance. + * + * returns None. + */ +void mic_smpt_uninit(struct mic_device *mdev) +{ + struct mic_smpt_info *smpt_info = mdev->smpt; + int i; + + dev_dbg(mdev->sdev->parent, + "nodeid %d SMPT ref count %lld map %lld unmap %lld\n", + mdev->id, smpt_info->ref_count, + smpt_info->map_count, smpt_info->unmap_count); + + for (i = 0; i < smpt_info->info.num_reg; i++) { + dev_dbg(mdev->sdev->parent, + "SMPT entry[%d] dma_addr = 0x%llx ref_count = %lld\n", + i, smpt_info->entry[i].dma_addr, + smpt_info->entry[i].ref_count); + if (smpt_info->entry[i].ref_count) + dev_warn(mdev->sdev->parent, + "ref count for entry %d is not zero\n", i); + } + kfree(smpt_info->entry); + kfree(smpt_info); +} + +/** + * mic_smpt_restore - Restore MIC System Memory Page Tables. + * + * @mdev: pointer to mic_device instance. + * + * Restore the SMPT registers to values previously stored in the + * SW data structures. Some MIC steppings lose register state + * across resets and this API should be called for performing + * a restore operation if required. + * + * returns None. + */ +void mic_smpt_restore(struct mic_device *mdev) +{ + int i; + dma_addr_t dma_addr; + + for (i = 0; i < mdev->smpt->info.num_reg; i++) { + dma_addr = mdev->smpt->entry[i].dma_addr; + mdev->smpt_ops->set(mdev, dma_addr, i); + } +} diff --git a/drivers/misc/mic/host/mic_smpt.h b/drivers/misc/mic/host/mic_smpt.h new file mode 100644 index 0000000..51970ab --- /dev/null +++ b/drivers/misc/mic/host/mic_smpt.h @@ -0,0 +1,98 @@ +/* + * Intel MIC Platform Software Stack (MPSS) + * + * Copyright(c) 2013 Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, version 2, as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * The full GNU General Public License is included in this distribution in + * the file called "COPYING". + * + * Intel MIC Host driver. + * + */ +#ifndef MIC_SMPT_H +#define MIC_SMPT_H +/** + * struct mic_smpt_ops - MIC HW specific SMPT operations. + * @init: Initialize hardware specific SMPT information in mic_smpt_hw_info. + * @set: Set the value for a particular SMPT entry. + */ +struct mic_smpt_ops { + void (*init)(struct mic_device *mdev); + void (*set)(struct mic_device *mdev, dma_addr_t dma_addr, u8 index); +}; + +/** + * struct mic_smpt - MIC SMPT entry information. + * @dma_addr: Base DMA address for this SMPT entry. + * @ref_count: Number of active mappings for this SMPT entry in bytes. + */ +struct mic_smpt { + dma_addr_t dma_addr; + s64 ref_count; +}; + +/** + * struct mic_smpt_hw_info - MIC SMPT hardware specific information. + * @num_reg: Number of SMPT registers. + * @page_shift: System memory page shift. + * @page_size: System memory page size. + * @base: System address base. + */ +struct mic_smpt_hw_info { + u8 num_reg; + u8 page_shift; + u64 page_size; + u64 base; +}; + +/** + * struct mic_smpt_info - MIC SMPT information. + * @entry: Array of SMPT entries. + * @smpt_lock: Spin lock protecting access to SMPT data structures. + * @info: Hardware specific SMPT information. + * @ref_count: Number of active SMPT mappings (for debug). + * @map_count: Number of SMPT mappings created (for debug). + * @unmap_count: Number of SMPT mappings destroyed (for debug). + */ +struct mic_smpt_info { + struct mic_smpt *entry; + spinlock_t smpt_lock; + struct mic_smpt_hw_info info; + s64 ref_count; + s64 map_count; + s64 unmap_count; +}; + +dma_addr_t mic_map_single(struct mic_device *mdev, void *va, size_t size); +void mic_unmap_single(struct mic_device *mdev, + dma_addr_t mic_addr, size_t size); +dma_addr_t mic_map(struct mic_device *mdev, + dma_addr_t dma_addr, size_t size); +void mic_unmap(struct mic_device *mdev, dma_addr_t mic_addr, size_t size); + +/** + * mic_map_error - Check a MIC address for errors. + * + * @mdev: pointer to mic_device instance. + * + * returns Whether there was an error during mic_map..(..) APIs. + */ +static inline bool mic_map_error(dma_addr_t mic_addr) +{ + return !mic_addr; +} + +int mic_smpt_init(struct mic_device *mdev); +void mic_smpt_uninit(struct mic_device *mdev); +void mic_smpt_restore(struct mic_device *mdev); + +#endif diff --git a/drivers/misc/mic/host/mic_x100.c b/drivers/misc/mic/host/mic_x100.c index da481b1..b637316 100644 --- a/drivers/misc/mic/host/mic_x100.c +++ b/drivers/misc/mic/host/mic_x100.c @@ -24,6 +24,7 @@ #include "../common/mic_device.h" #include "mic_device.h" #include "mic_x100.h" +#include "mic_smpt.h" /** * mic_x100_write_spad - write to the scratchpad register @@ -67,9 +68,255 @@ mic_x100_read_spad(struct mic_device *mdev, unsigned int idx) return val; } +/** + * mic_x100_enable_interrupts - Enable interrupts. + * @mdev: pointer to mic_device instance + */ +static void mic_x100_enable_interrupts(struct mic_device *mdev) +{ + u32 reg; + struct mic_mw *mw = &mdev->mmio; + u32 sice0 = MIC_X100_SBOX_BASE_ADDRESS + MIC_X100_SBOX_SICE0; + u32 siac0 = MIC_X100_SBOX_BASE_ADDRESS + MIC_X100_SBOX_SIAC0; + + reg = mic_mmio_read(mw, sice0); + reg |= MIC_X100_SBOX_DBR_BITS(0xf) | MIC_X100_SBOX_DMA_BITS(0xff); + mic_mmio_write(mw, reg, sice0); + + /* + * Enable auto-clear when enabling interrupts. Applicable only for + * MSI-x. Legacy and MSI mode cannot have auto-clear enabled. + */ + if (mdev->irq_info.num_vectors > 1) { + reg = mic_mmio_read(mw, siac0); + reg |= MIC_X100_SBOX_DBR_BITS(0xf) | + MIC_X100_SBOX_DMA_BITS(0xff); + mic_mmio_write(mw, reg, siac0); + } +} + +/** + * mic_x100_disable_interrupts - Disable interrupts. + * @mdev: pointer to mic_device instance + */ +static void mic_x100_disable_interrupts(struct mic_device *mdev) +{ + u32 reg; + struct mic_mw *mw = &mdev->mmio; + u32 sice0 = MIC_X100_SBOX_BASE_ADDRESS + MIC_X100_SBOX_SICE0; + u32 siac0 = MIC_X100_SBOX_BASE_ADDRESS + MIC_X100_SBOX_SIAC0; + u32 sicc0 = MIC_X100_SBOX_BASE_ADDRESS + MIC_X100_SBOX_SICC0; + + reg = mic_mmio_read(mw, sice0); + mic_mmio_write(mw, reg, sicc0); + + if (mdev->irq_info.num_vectors > 1) { + reg = mic_mmio_read(mw, siac0); + reg &= ~(MIC_X100_SBOX_DBR_BITS(0xf) | + MIC_X100_SBOX_DMA_BITS(0xff)); + mic_mmio_write(mw, reg, siac0); + } +} + +/** + * mic_x100_send_sbox_intr - Send an MIC_X100_SBOX interrupt to MIC. + * @mdev: pointer to mic_device instance + */ +static void mic_x100_send_sbox_intr(struct mic_device *mdev, + int doorbell) +{ + struct mic_mw *mw = &mdev->mmio; + u64 apic_icr_offset = MIC_X100_SBOX_APICICR0 + doorbell * 8; + u32 apicicr_low = mic_mmio_read(mw, + MIC_X100_SBOX_BASE_ADDRESS + apic_icr_offset); + + /* for MIC we need to make sure we "hit" the send_icr bit (13) */ + apicicr_low = (apicicr_low | (1 << 13)); + + /* Ensure that the interrupt is ordered w.r.t. previous stores. */ + wmb(); + mic_mmio_write(mw, apicicr_low, + MIC_X100_SBOX_BASE_ADDRESS + apic_icr_offset); +} + +/** + * mic_x100_send_rdmasr_intr - Send an RDMASR interrupt to MIC. + * @mdev: pointer to mic_device instance + */ +static void mic_x100_send_rdmasr_intr(struct mic_device *mdev, + int doorbell) +{ + int rdmasr_offset = MIC_X100_SBOX_RDMASR0 + (doorbell << 2); + /* Ensure that the interrupt is ordered w.r.t. previous stores. */ + wmb(); + mic_mmio_write(&mdev->mmio, 0, + MIC_X100_SBOX_BASE_ADDRESS + rdmasr_offset); +} + +/** + * __mic_x100_send_intr - Send interrupt to MIC. + * @mdev: pointer to mic_device instance + * @doorbell: doorbell number. + */ +static void mic_x100_send_intr(struct mic_device *mdev, int doorbell) +{ + int rdmasr_db; + if (doorbell < MIC_X100_NUM_SBOX_IRQ) { + mic_x100_send_sbox_intr(mdev, doorbell); + } else { + rdmasr_db = doorbell - MIC_X100_NUM_SBOX_IRQ + + MIC_X100_RDMASR_IRQ_BASE; + mic_x100_send_rdmasr_intr(mdev, rdmasr_db); + } +} + +/** + * mic_ack_interrupt - Device specific interrupt handling. + * @mdev: pointer to mic_device instance + * + * Returns: bitmask of doorbell events triggered. + */ +static u32 mic_x100_ack_interrupt(struct mic_device *mdev) +{ + u32 reg = 0; + struct mic_mw *mw = &mdev->mmio; + u32 sicr0 = MIC_X100_SBOX_BASE_ADDRESS + MIC_X100_SBOX_SICR0; + + /* Clear pending bit array. */ + if (MIC_A0_STEP == mdev->stepping) + mic_mmio_write(mw, 1, MIC_X100_SBOX_BASE_ADDRESS + + MIC_X100_SBOX_MSIXPBACR); + + if (mdev->irq_info.num_vectors <= 1) { + reg = mic_mmio_read(mw, sicr0); + + if (unlikely(!reg)) + goto done; + + mic_mmio_write(mw, reg, sicr0); + } + + if (mdev->stepping >= MIC_B0_STEP) + mdev->intr_ops->enable_interrupts(mdev); +done: + return reg; +} + +/** + * mic_x100_hw_intr_init - Initialize h/w specific interrupt + * information. + * @mdev: pointer to mic_device instance + */ +static void mic_x100_hw_intr_init(struct mic_device *mdev) +{ + mdev->intr_info = (struct mic_intr_info *) mic_x100_intr_init; +} + +/** + * mic_x100_read_msi_to_src_map - read from the MSI mapping registers + * @mdev: pointer to mic_device instance + * @idx: index to the mapping register, 0 based + * + * This function allows reading of the 32bit MSI mapping register. + * + * RETURNS: The value in the register. + */ +static u32 +mic_x100_read_msi_to_src_map(struct mic_device *mdev, int idx) +{ + return mic_mmio_read(&mdev->mmio, + MIC_X100_SBOX_BASE_ADDRESS + + MIC_X100_SBOX_MXAR0 + idx * 4); +} + +/** + * mic_x100_program_msi_to_src_map - program the MSI mapping registers + * @mdev: pointer to mic_device instance + * @idx: index to the mapping register, 0 based + * @offset: The bit offset in the register that needs to be updated. + * @set: boolean specifying if the bit in the specified offset needs + * to be set or cleared. + * + * RETURNS: None. + */ +static void +mic_x100_program_msi_to_src_map(struct mic_device *mdev, + int idx, int offset, bool set) +{ + unsigned long reg; + struct mic_mw *mw = &mdev->mmio; + u32 mxar = MIC_X100_SBOX_BASE_ADDRESS + + MIC_X100_SBOX_MXAR0 + idx * 4; + + reg = mic_mmio_read(mw, mxar); + if (set) + __set_bit(offset, ®); + else + __clear_bit(offset, ®); + mic_mmio_write(mw, reg, mxar); +} + +/** + * mic_x100_smpt_set - Update an SMPT entry with a DMA address. + * @mdev: pointer to mic_device instance + * + * RETURNS: none. + */ +static void +mic_x100_smpt_set(struct mic_device *mdev, dma_addr_t dma_addr, u8 index) +{ +#define SNOOP_ON (0 << 0) +#define SNOOP_OFF (1 << 0) +/* + * Sbox Smpt Reg Bits: + * Bits 31:2 Host address + * Bits 1 RSVD + * Bits 0 No snoop + */ +#define BUILD_SMPT(NO_SNOOP, HOST_ADDR) \ + (u32)(((HOST_ADDR) << 2) | ((NO_SNOOP) & 0x01)) + + uint32_t smpt_reg_val = BUILD_SMPT(SNOOP_ON, + dma_addr >> mdev->smpt->info.page_shift); + mic_mmio_write(&mdev->mmio, smpt_reg_val, + MIC_X100_SBOX_BASE_ADDRESS + + MIC_X100_SBOX_SMPT00 + (4 * index)); +} + +/** + * mic_x100_smpt_hw_init - Initialize SMPT X100 specific fields. + * @mdev: pointer to mic_device instance + * + * RETURNS: none. + */ +static void mic_x100_smpt_hw_init(struct mic_device *mdev) +{ + struct mic_smpt_hw_info *info = &mdev->smpt->info; + + info->num_reg = 32; + info->page_shift = 34; + info->page_size = (1ULL << info->page_shift); + info->base = 0x8000000000ULL; +} + +struct mic_smpt_ops mic_x100_smpt_ops = { + .init = mic_x100_smpt_hw_init, + .set = mic_x100_smpt_set, +}; + struct mic_hw_ops mic_x100_ops = { .aper_bar = MIC_X100_APER_BAR, .mmio_bar = MIC_X100_MMIO_BAR, .read_spad = mic_x100_read_spad, .write_spad = mic_x100_write_spad, + .send_intr = mic_x100_send_intr, + .ack_interrupt = mic_x100_ack_interrupt, +}; + +struct mic_hw_intr_ops mic_x100_intr_ops = { + .intr_init = mic_x100_hw_intr_init, + .enable_interrupts = mic_x100_enable_interrupts, + .disable_interrupts = mic_x100_disable_interrupts, + .program_msi_to_src_map = mic_x100_program_msi_to_src_map, + .read_msi_to_src_map = mic_x100_read_msi_to_src_map, }; diff --git a/drivers/misc/mic/host/mic_x100.h b/drivers/misc/mic/host/mic_x100.h index 1f4e630..642cae9 100644 --- a/drivers/misc/mic/host/mic_x100.h +++ b/drivers/misc/mic/host/mic_x100.h @@ -42,6 +42,45 @@ #define MIC_X100_SBOX_BASE_ADDRESS 0x00010000 #define MIC_X100_SBOX_SPAD0 0x0000AB20 +#define MIC_X100_SBOX_SICR0_DBR(x) ((x) & 0xf) +#define MIC_X100_SBOX_SICR0_DMA(x) (((x) >> 8) & 0xff) +#define MIC_X100_SBOX_SICE0_DBR(x) ((x) & 0xf) +#define MIC_X100_SBOX_DBR_BITS(x) ((x) & 0xf) +#define MIC_X100_SBOX_SICE0_DMA(x) (((x) >> 8) & 0xff) +#define MIC_X100_SBOX_DMA_BITS(x) (((x) & 0xff) << 8) + +#define MIC_X100_SBOX_APICICR0 0x0000A9D0 +#define MIC_X100_SBOX_SICR0 0x00009004 +#define MIC_X100_SBOX_SICE0 0x0000900C +#define MIC_X100_SBOX_SICC0 0x00009010 +#define MIC_X100_SBOX_SIAC0 0x00009014 +#define MIC_X100_SBOX_MSIXPBACR 0x00009084 +#define MIC_X100_SBOX_MXAR0 0x00009044 +#define MIC_X100_SBOX_SMPT00 0x00003100 +#define MIC_X100_SBOX_RDMASR0 0x0000B180 + +#define MIC_X100_DOORBELL_IDX_START 0 +#define MIC_X100_NUM_DOORBELL 4 +#define MIC_X100_DMA_IDX_START 8 +#define MIC_X100_NUM_DMA 8 +#define MIC_X100_ERR_IDX_START 30 +#define MIC_X100_NUM_ERR 1 + +#define MIC_X100_NUM_SBOX_IRQ 8 +#define MIC_X100_NUM_RDMASR_IRQ 8 +#define MIC_X100_RDMASR_IRQ_BASE 17 + +static const u16 mic_x100_intr_init[] = { + MIC_X100_DOORBELL_IDX_START, + MIC_X100_DMA_IDX_START, + MIC_X100_ERR_IDX_START, + MIC_X100_NUM_DOORBELL, + MIC_X100_NUM_DMA, + MIC_X100_NUM_ERR, +}; + extern struct mic_hw_ops mic_x100_ops; +extern struct mic_smpt_ops mic_x100_smpt_ops; +extern struct mic_hw_intr_ops mic_x100_intr_ops; #endif -- cgit v0.10.2 From 3a6a9201897c6482573ad07ee880574147761006 Mon Sep 17 00:00:00 2001 From: Sudeep Dutt Date: Thu, 5 Sep 2013 16:41:55 -0700 Subject: Intel MIC Host Driver, card OS state management. This patch enables the following features: a) Boots and shuts down the card via sysfs entries. b) Allocates and maps a device page for communication with the card driver and updates the device page address via scratchpad registers. c) Provides sysfs entries for shutdown status, kernel command line, ramdisk and log buffer information. Co-author: Dasaratharaman Chandramouli Signed-off-by: Ashutosh Dixit Signed-off-by: Caz Yokoyama Signed-off-by: Dasaratharaman Chandramouli Signed-off-by: Harshavardhan R Kharche Signed-off-by: Nikhil Rao Signed-off-by: Sudeep Dutt Acked-by: Yaozu (Eddie) Dong Reviewed-by: Peter P Waskiewicz Jr Signed-off-by: Greg Kroah-Hartman diff --git a/Documentation/ABI/testing/sysfs-class-mic.txt b/Documentation/ABI/testing/sysfs-class-mic.txt index 09eb3c6..82cdad3 100644 --- a/Documentation/ABI/testing/sysfs-class-mic.txt +++ b/Documentation/ABI/testing/sysfs-class-mic.txt @@ -32,3 +32,116 @@ Contact: Sudeep Dutt Description: Provides information about the silicon stepping for an Intel MIC device. For example - "A0" or "B0" + +What: /sys/class/mic/mic(x)/state +Date: August 2013 +KernelVersion: 3.11 +Contact: Sudeep Dutt +Description: + When read, this entry provides the current state of an Intel + MIC device in the context of the card OS. Possible values that + will be read are: + "offline" - The MIC device is ready to boot the card OS. + "online" - The MIC device has initiated booting a card OS. + "shutting_down" - The card OS is shutting down. + "reset_failed" - The MIC device has failed to reset. + + When written, this sysfs entry triggers different state change + operations depending upon the current state of the card OS. + Acceptable values are: + "boot" - Boot the card OS image specified by the combination + of firmware, ramdisk, cmdline and bootmode + sysfs entries. + "reset" - Initiates device reset. + "shutdown" - Initiates card OS shutdown. + +What: /sys/class/mic/mic(x)/shutdown_status +Date: August 2013 +KernelVersion: 3.11 +Contact: Sudeep Dutt +Description: + An Intel MIC device runs a Linux OS during its operation. This + OS can shutdown because of various reasons. When read, this + entry provides the status on why the card OS was shutdown. + Possible values are: + "nop" - shutdown status is not applicable, when the card OS is + "online" + "crashed" - Shutdown because of a HW or SW crash. + "halted" - Shutdown because of a halt command. + "poweroff" - Shutdown because of a poweroff command. + "restart" - Shutdown because of a restart command. + +What: /sys/class/mic/mic(x)/cmdline +Date: August 2013 +KernelVersion: 3.11 +Contact: Sudeep Dutt +Description: + An Intel MIC device runs a Linux OS during its operation. Before + booting this card OS, it is possible to pass kernel command line + options to configure various features in it, similar to + self-bootable machines. When read, this entry provides + information about the current kernel command line options set to + boot the card OS. This entry can be written to change the + existing kernel command line options. Typically, the user would + want to read the current command line options, append new ones + or modify existing ones and then write the whole kernel command + line back to this entry. + +What: /sys/class/mic/mic(x)/firmware +Date: August 2013 +KernelVersion: 3.11 +Contact: Sudeep Dutt +Description: + When read, this sysfs entry provides the path name under + /lib/firmware/ where the firmware image to be booted on the + card can be found. The entry can be written to change the + firmware image location under /lib/firmware/. + +What: /sys/class/mic/mic(x)/ramdisk +Date: August 2013 +KernelVersion: 3.11 +Contact: Sudeep Dutt +Description: + When read, this sysfs entry provides the path name under + /lib/firmware/ where the ramdisk image to be used during card + OS boot can be found. The entry can be written to change + the ramdisk image location under /lib/firmware/. + +What: /sys/class/mic/mic(x)/bootmode +Date: August 2013 +KernelVersion: 3.11 +Contact: Sudeep Dutt +Description: + When read, this sysfs entry provides the current bootmode for + the card. This sysfs entry can be written with the following + valid strings: + a) linux - Boot a Linux image. + b) elf - Boot an elf image for flash updates. + +What: /sys/class/mic/mic(x)/log_buf_addr +Date: August 2013 +KernelVersion: 3.11 +Contact: Sudeep Dutt +Description: + An Intel MIC device runs a Linux OS during its operation. For + debugging purpose and early kernel boot messages, the user can + access the card OS log buffer via debugfs. When read, this entry + provides the kernel virtual address of the buffer where the card + OS log buffer can be read. This entry is written by the host + configuration daemon to set the log buffer address. The correct + log buffer address to be written can be found in the System.map + file of the card OS. + +What: /sys/class/mic/mic(x)/log_buf_len +Date: August 2013 +KernelVersion: 3.11 +Contact: Sudeep Dutt +Description: + An Intel MIC device runs a Linux OS during its operation. For + debugging purpose and early kernel boot messages, the user can + access the card OS log buffer via debugfs. When read, this entry + provides the kernel virtual address where the card OS log buffer + length can be read. This entry is written by host configuration + daemon to set the log buffer length address. The correct log + buffer length address to be written can be found in the + System.map file of the card OS. diff --git a/drivers/misc/mic/common/mic_device.h b/drivers/misc/mic/common/mic_device.h index f02262e..6440e9d 100644 --- a/drivers/misc/mic/common/mic_device.h +++ b/drivers/misc/mic/common/mic_device.h @@ -34,4 +34,11 @@ struct mic_mw { resource_size_t len; }; +/* + * Scratch pad register offsets used by the host to communicate + * device page DMA address to the card. + */ +#define MIC_DPLO_SPAD 14 +#define MIC_DPHI_SPAD 15 + #endif diff --git a/drivers/misc/mic/host/Makefile b/drivers/misc/mic/host/Makefile index 6ff5550..a375dd3 100644 --- a/drivers/misc/mic/host/Makefile +++ b/drivers/misc/mic/host/Makefile @@ -8,3 +8,5 @@ mic_host-objs += mic_x100.o mic_host-objs += mic_sysfs.o mic_host-objs += mic_smpt.o mic_host-objs += mic_intr.o +mic_host-objs += mic_boot.o +mic_host-objs += mic_debugfs.o diff --git a/drivers/misc/mic/host/mic_boot.c b/drivers/misc/mic/host/mic_boot.c new file mode 100644 index 0000000..936fc58 --- /dev/null +++ b/drivers/misc/mic/host/mic_boot.c @@ -0,0 +1,184 @@ +/* + * Intel MIC Platform Software Stack (MPSS) + * + * Copyright(c) 2013 Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, version 2, as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * The full GNU General Public License is included in this distribution in + * the file called "COPYING". + * + * Intel MIC Host driver. + * + */ +#include +#include +#include + +#include +#include "../common/mic_device.h" +#include "mic_device.h" +#include "mic_smpt.h" + +/** + * mic_reset - Reset the MIC device. + * @mdev: pointer to mic_device instance + */ +static void mic_reset(struct mic_device *mdev) +{ + int i; + +#define MIC_RESET_TO (45) + + mdev->ops->reset_fw_ready(mdev); + mdev->ops->reset(mdev); + + for (i = 0; i < MIC_RESET_TO; i++) { + if (mdev->ops->is_fw_ready(mdev)) + return; + /* + * Resets typically take 10s of seconds to complete. + * Since an MMIO read is required to check if the + * firmware is ready or not, a 1 second delay works nicely. + */ + msleep(1000); + } + mic_set_state(mdev, MIC_RESET_FAILED); +} + +/* Initialize the MIC bootparams */ +void mic_bootparam_init(struct mic_device *mdev) +{ + struct mic_bootparam *bootparam = mdev->dp; + + bootparam->magic = MIC_MAGIC; + bootparam->c2h_shutdown_db = mdev->shutdown_db; + bootparam->h2c_shutdown_db = -1; + bootparam->h2c_config_db = -1; + bootparam->shutdown_status = 0; + bootparam->shutdown_card = 0; +} + +/** + * mic_start - Start the MIC. + * @mdev: pointer to mic_device instance + * @buf: buffer containing boot string including firmware/ramdisk path. + * + * This function prepares an MIC for boot and initiates boot. + * RETURNS: An appropriate -ERRNO error value on error, or zero for success. + */ +int mic_start(struct mic_device *mdev, const char *buf) +{ + int rc; + mutex_lock(&mdev->mic_mutex); +retry: + if (MIC_OFFLINE != mdev->state) { + rc = -EINVAL; + goto unlock_ret; + } + if (!mdev->ops->is_fw_ready(mdev)) { + mic_reset(mdev); + /* + * The state will either be MIC_OFFLINE if the reset succeeded + * or MIC_RESET_FAILED if the firmware reset failed. + */ + goto retry; + } + rc = mdev->ops->load_mic_fw(mdev, buf); + if (rc) + goto unlock_ret; + mic_smpt_restore(mdev); + mic_intr_restore(mdev); + mdev->intr_ops->enable_interrupts(mdev); + mdev->ops->write_spad(mdev, MIC_DPLO_SPAD, mdev->dp_dma_addr); + mdev->ops->write_spad(mdev, MIC_DPHI_SPAD, mdev->dp_dma_addr >> 32); + mdev->ops->send_firmware_intr(mdev); + mic_set_state(mdev, MIC_ONLINE); +unlock_ret: + mutex_unlock(&mdev->mic_mutex); + return rc; +} + +/** + * mic_stop - Prepare the MIC for reset and trigger reset. + * @mdev: pointer to mic_device instance + * @force: force a MIC to reset even if it is already offline. + * + * RETURNS: None. + */ +void mic_stop(struct mic_device *mdev, bool force) +{ + mutex_lock(&mdev->mic_mutex); + if (MIC_OFFLINE != mdev->state || force) { + mic_bootparam_init(mdev); + mic_reset(mdev); + if (MIC_RESET_FAILED == mdev->state) + goto unlock; + mic_set_shutdown_status(mdev, MIC_NOP); + mic_set_state(mdev, MIC_OFFLINE); + } +unlock: + mutex_unlock(&mdev->mic_mutex); +} + +/** + * mic_shutdown - Initiate MIC shutdown. + * @mdev: pointer to mic_device instance + * + * RETURNS: None. + */ +void mic_shutdown(struct mic_device *mdev) +{ + struct mic_bootparam *bootparam = mdev->dp; + s8 db = bootparam->h2c_shutdown_db; + + mutex_lock(&mdev->mic_mutex); + if (MIC_ONLINE == mdev->state && db != -1) { + bootparam->shutdown_card = 1; + mdev->ops->send_intr(mdev, db); + mic_set_state(mdev, MIC_SHUTTING_DOWN); + } + mutex_unlock(&mdev->mic_mutex); +} + +/** + * mic_shutdown_work - Handle shutdown interrupt from MIC. + * @work: The work structure. + * + * This work is scheduled whenever the host has received a shutdown + * interrupt from the MIC. + */ +void mic_shutdown_work(struct work_struct *work) +{ + struct mic_device *mdev = container_of(work, struct mic_device, + shutdown_work); + struct mic_bootparam *bootparam = mdev->dp; + + mutex_lock(&mdev->mic_mutex); + mic_set_shutdown_status(mdev, bootparam->shutdown_status); + bootparam->shutdown_status = 0; + if (MIC_SHUTTING_DOWN != mdev->state) + mic_set_state(mdev, MIC_SHUTTING_DOWN); + mutex_unlock(&mdev->mic_mutex); +} + +/** + * mic_reset_trigger_work - Trigger MIC reset. + * @work: The work structure. + * + * This work is scheduled whenever the host wants to reset the MIC. + */ +void mic_reset_trigger_work(struct work_struct *work) +{ + struct mic_device *mdev = container_of(work, struct mic_device, + reset_trigger_work); + + mic_stop(mdev, false); +} diff --git a/drivers/misc/mic/host/mic_debugfs.c b/drivers/misc/mic/host/mic_debugfs.c new file mode 100644 index 0000000..78541d4 --- /dev/null +++ b/drivers/misc/mic/host/mic_debugfs.c @@ -0,0 +1,355 @@ +/* + * Intel MIC Platform Software Stack (MPSS) + * + * Copyright(c) 2013 Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, version 2, as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * The full GNU General Public License is included in this distribution in + * the file called "COPYING". + * + * Intel MIC Host driver. + * + */ +#include +#include +#include + +#include +#include "../common/mic_device.h" +#include "mic_device.h" +#include "mic_smpt.h" + +/* Debugfs parent dir */ +static struct dentry *mic_dbg; + +/** + * mic_log_buf_show - Display MIC kernel log buffer. + * + * log_buf addr/len is read from System.map by user space + * and populated in sysfs entries. + */ +static int mic_log_buf_show(struct seq_file *s, void *unused) +{ + void __iomem *log_buf_va; + int __iomem *log_buf_len_va; + struct mic_device *mdev = s->private; + void *kva; + int size; + unsigned long aper_offset; + + if (!mdev || !mdev->log_buf_addr || !mdev->log_buf_len) + goto done; + /* + * Card kernel will never be relocated and any kernel text/data mapping + * can be translated to phys address by subtracting __START_KERNEL_map. + */ + aper_offset = (unsigned long)mdev->log_buf_len - __START_KERNEL_map; + log_buf_len_va = mdev->aper.va + aper_offset; + aper_offset = (unsigned long)mdev->log_buf_addr - __START_KERNEL_map; + log_buf_va = mdev->aper.va + aper_offset; + size = ioread32(log_buf_len_va); + + kva = kmalloc(size, GFP_KERNEL); + if (!kva) + goto done; + mutex_lock(&mdev->mic_mutex); + memcpy_fromio(kva, log_buf_va, size); + switch (mdev->state) { + case MIC_ONLINE: + /* Fall through */ + case MIC_SHUTTING_DOWN: + seq_write(s, kva, size); + break; + default: + break; + } + mutex_unlock(&mdev->mic_mutex); + kfree(kva); +done: + return 0; +} + +static int mic_log_buf_open(struct inode *inode, struct file *file) +{ + return single_open(file, mic_log_buf_show, inode->i_private); +} + +static int mic_log_buf_release(struct inode *inode, struct file *file) +{ + return single_release(inode, file); +} + +static const struct file_operations log_buf_ops = { + .owner = THIS_MODULE, + .open = mic_log_buf_open, + .read = seq_read, + .llseek = seq_lseek, + .release = mic_log_buf_release +}; + +static int mic_smpt_show(struct seq_file *s, void *pos) +{ + int i; + struct mic_device *mdev = s->private; + unsigned long flags; + + seq_printf(s, "MIC %-2d |%-10s| %-14s %-10s\n", + mdev->id, "SMPT entry", "SW DMA addr", "RefCount"); + seq_puts(s, "====================================================\n"); + + if (mdev->smpt) { + struct mic_smpt_info *smpt_info = mdev->smpt; + spin_lock_irqsave(&smpt_info->smpt_lock, flags); + for (i = 0; i < smpt_info->info.num_reg; i++) { + seq_printf(s, "%9s|%-10d| %-#14llx %-10lld\n", + " ", i, smpt_info->entry[i].dma_addr, + smpt_info->entry[i].ref_count); + } + spin_unlock_irqrestore(&smpt_info->smpt_lock, flags); + } + seq_puts(s, "====================================================\n"); + return 0; +} + +static int mic_smpt_debug_open(struct inode *inode, struct file *file) +{ + return single_open(file, mic_smpt_show, inode->i_private); +} + +static int mic_smpt_debug_release(struct inode *inode, struct file *file) +{ + return single_release(inode, file); +} + +static const struct file_operations smpt_file_ops = { + .owner = THIS_MODULE, + .open = mic_smpt_debug_open, + .read = seq_read, + .llseek = seq_lseek, + .release = mic_smpt_debug_release +}; + +static int mic_soft_reset_show(struct seq_file *s, void *pos) +{ + struct mic_device *mdev = s->private; + + mic_stop(mdev, true); + return 0; +} + +static int mic_soft_reset_debug_open(struct inode *inode, struct file *file) +{ + return single_open(file, mic_soft_reset_show, inode->i_private); +} + +static int mic_soft_reset_debug_release(struct inode *inode, struct file *file) +{ + return single_release(inode, file); +} + +static const struct file_operations soft_reset_ops = { + .owner = THIS_MODULE, + .open = mic_soft_reset_debug_open, + .read = seq_read, + .llseek = seq_lseek, + .release = mic_soft_reset_debug_release +}; + +static int mic_post_code_show(struct seq_file *s, void *pos) +{ + struct mic_device *mdev = s->private; + u32 reg = mdev->ops->get_postcode(mdev); + + seq_printf(s, "%c%c", reg & 0xff, (reg >> 8) & 0xff); + return 0; +} + +static int mic_post_code_debug_open(struct inode *inode, struct file *file) +{ + return single_open(file, mic_post_code_show, inode->i_private); +} + +static int mic_post_code_debug_release(struct inode *inode, struct file *file) +{ + return single_release(inode, file); +} + +static const struct file_operations post_code_ops = { + .owner = THIS_MODULE, + .open = mic_post_code_debug_open, + .read = seq_read, + .llseek = seq_lseek, + .release = mic_post_code_debug_release +}; + +static int mic_dp_show(struct seq_file *s, void *pos) +{ + struct mic_device *mdev = s->private; + struct mic_bootparam *bootparam = mdev->dp; + + seq_printf(s, "Bootparam: magic 0x%x\n", + bootparam->magic); + seq_printf(s, "Bootparam: h2c_shutdown_db %d\n", + bootparam->h2c_shutdown_db); + seq_printf(s, "Bootparam: h2c_config_db %d\n", + bootparam->h2c_config_db); + seq_printf(s, "Bootparam: c2h_shutdown_db %d\n", + bootparam->c2h_shutdown_db); + seq_printf(s, "Bootparam: shutdown_status %d\n", + bootparam->shutdown_status); + seq_printf(s, "Bootparam: shutdown_card %d\n", + bootparam->shutdown_card); + + return 0; +} + +static int mic_dp_debug_open(struct inode *inode, struct file *file) +{ + return single_open(file, mic_dp_show, inode->i_private); +} + +static int mic_dp_debug_release(struct inode *inode, struct file *file) +{ + return single_release(inode, file); +} + +static const struct file_operations dp_ops = { + .owner = THIS_MODULE, + .open = mic_dp_debug_open, + .read = seq_read, + .llseek = seq_lseek, + .release = mic_dp_debug_release +}; + +static int mic_msi_irq_info_show(struct seq_file *s, void *pos) +{ + struct mic_device *mdev = s->private; + int reg; + int i, j; + u16 entry; + u16 vector; + struct pci_dev *pdev = container_of(mdev->sdev->parent, + struct pci_dev, dev); + + if (pci_dev_msi_enabled(pdev)) { + for (i = 0; i < mdev->irq_info.num_vectors; i++) { + if (pdev->msix_enabled) { + entry = mdev->irq_info.msix_entries[i].entry; + vector = mdev->irq_info.msix_entries[i].vector; + } else { + entry = 0; + vector = pdev->irq; + } + + reg = mdev->intr_ops->read_msi_to_src_map(mdev, entry); + + seq_printf(s, "%s %-10d %s %-10d MXAR[%d]: %08X\n", + "IRQ:", vector, "Entry:", entry, i, reg); + + seq_printf(s, "%-10s", "offset:"); + for (j = (MIC_NUM_OFFSETS - 1); j >= 0; j--) + seq_printf(s, "%4d ", j); + seq_puts(s, "\n"); + + + seq_printf(s, "%-10s", "count:"); + for (j = (MIC_NUM_OFFSETS - 1); j >= 0; j--) + seq_printf(s, "%4d ", + (mdev->irq_info.mic_msi_map[i] & BIT(j)) ? + 1 : 0); + seq_puts(s, "\n\n"); + } + } else { + seq_puts(s, "MSI/MSIx interrupts not enabled\n"); + } + + return 0; + +} + +static int mic_msi_irq_info_debug_open(struct inode *inode, struct file *file) +{ + return single_open(file, mic_msi_irq_info_show, inode->i_private); +} + +static int +mic_msi_irq_info_debug_release(struct inode *inode, struct file *file) +{ + return single_release(inode, file); +} + +static const struct file_operations msi_irq_info_ops = { + .owner = THIS_MODULE, + .open = mic_msi_irq_info_debug_open, + .read = seq_read, + .llseek = seq_lseek, + .release = mic_msi_irq_info_debug_release +}; + +/** + * mic_create_debug_dir - Initialize MIC debugfs entries. + */ +void mic_create_debug_dir(struct mic_device *mdev) +{ + if (!mic_dbg) + return; + + mdev->dbg_dir = debugfs_create_dir(dev_name(mdev->sdev), mic_dbg); + if (!mdev->dbg_dir) + return; + + debugfs_create_file("log_buf", 0444, mdev->dbg_dir, + mdev, &log_buf_ops); + + debugfs_create_file("smpt", 0444, mdev->dbg_dir, + mdev, &smpt_file_ops); + + debugfs_create_file("soft_reset", 0444, mdev->dbg_dir, + mdev, &soft_reset_ops); + + debugfs_create_file("post_code", 0444, mdev->dbg_dir, + mdev, &post_code_ops); + + debugfs_create_file("dp", 0444, mdev->dbg_dir, + mdev, &dp_ops); + + debugfs_create_file("msi_irq_info", 0444, mdev->dbg_dir, + mdev, &msi_irq_info_ops); +} + +/** + * mic_delete_debug_dir - Uninitialize MIC debugfs entries. + */ +void mic_delete_debug_dir(struct mic_device *mdev) +{ + if (!mdev->dbg_dir) + return; + + debugfs_remove_recursive(mdev->dbg_dir); +} + +/** + * mic_init_debugfs - Initialize global debugfs entry. + */ +void __init mic_init_debugfs(void) +{ + mic_dbg = debugfs_create_dir(KBUILD_MODNAME, NULL); + if (!mic_dbg) + pr_err("can't create debugfs dir\n"); +} + +/** + * mic_exit_debugfs - Uninitialize global debugfs entry + */ +void mic_exit_debugfs(void) +{ + debugfs_remove(mic_dbg); +} diff --git a/drivers/misc/mic/host/mic_device.h b/drivers/misc/mic/host/mic_device.h index dd34b65..50b8b88 100644 --- a/drivers/misc/mic/host/mic_device.h +++ b/drivers/misc/mic/host/mic_device.h @@ -63,6 +63,23 @@ enum mic_stepping { * @smpt: MIC SMPT information. * @intr_info: H/W specific interrupt information. * @irq_info: The OS specific irq information + * @dbg_dir: debugfs directory of this MIC device. + * @cmdline: Kernel command line. + * @firmware: Firmware file name. + * @ramdisk: Ramdisk file name. + * @bootmode: Boot mode i.e. "linux" or "elf" for flash updates. + * @bootaddr: MIC boot address. + * @reset_trigger_work: Work for triggering reset requests. + * @shutdown_work: Work for handling shutdown interrupts. + * @state: MIC state. + * @shutdown_status: MIC status reported by card for shutdown/crashes. + * @state_sysfs: Sysfs dirent for notifying ring 3 about MIC state changes. + * @log_buf_addr: Log buffer address for MIC. + * @log_buf_len: Log buffer length address for MIC. + * @dp: virtio device page + * @dp_dma_addr: virtio device page DMA address. + * @shutdown_db: shutdown doorbell. + * @shutdown_cookie: shutdown cookie. */ struct mic_device { struct mic_mw mmio; @@ -79,6 +96,23 @@ struct mic_device { struct mic_smpt_info *smpt; struct mic_intr_info *intr_info; struct mic_irq_info irq_info; + struct dentry *dbg_dir; + char *cmdline; + char *firmware; + char *ramdisk; + char *bootmode; + u32 bootaddr; + struct work_struct reset_trigger_work; + struct work_struct shutdown_work; + u8 state; + u8 shutdown_status; + struct sysfs_dirent *state_sysfs; + void *log_buf_addr; + int *log_buf_len; + void *dp; + dma_addr_t dp_dma_addr; + int shutdown_db; + struct mic_irq *shutdown_cookie; }; /** @@ -90,6 +124,13 @@ struct mic_device { * @send_intr: Send an interrupt for a particular doorbell on the card. * @ack_interrupt: Hardware specific operations to ack the h/w on * receipt of an interrupt. + * @reset: Reset the remote processor. + * @reset_fw_ready: Reset firmware ready field. + * @is_fw_ready: Check if firmware is ready for OS download. + * @send_firmware_intr: Send an interrupt to the card firmware. + * @load_mic_fw: Load firmware segments required to boot the card + * into card memory. This includes the kernel, command line, ramdisk etc. + * @get_postcode: Get post code status from firmware. */ struct mic_hw_ops { u8 aper_bar; @@ -98,6 +139,12 @@ struct mic_hw_ops { void (*write_spad)(struct mic_device *mdev, unsigned int idx, u32 val); void (*send_intr)(struct mic_device *mdev, int doorbell); u32 (*ack_interrupt)(struct mic_device *mdev); + void (*reset)(struct mic_device *mdev); + void (*reset_fw_ready)(struct mic_device *mdev); + bool (*is_fw_ready)(struct mic_device *mdev); + void (*send_firmware_intr)(struct mic_device *mdev); + int (*load_mic_fw)(struct mic_device *mdev, const char *buf); + u32 (*get_postcode)(struct mic_device *mdev); }; /** @@ -127,4 +174,17 @@ mic_mmio_write(struct mic_mw *mw, u32 val, u32 offset) } void mic_sysfs_init(struct mic_device *mdev); +int mic_start(struct mic_device *mdev, const char *buf); +void mic_stop(struct mic_device *mdev, bool force); +void mic_shutdown(struct mic_device *mdev); +void mic_reset_delayed_work(struct work_struct *work); +void mic_reset_trigger_work(struct work_struct *work); +void mic_shutdown_work(struct work_struct *work); +void mic_bootparam_init(struct mic_device *mdev); +void mic_set_state(struct mic_device *mdev, u8 state); +void mic_set_shutdown_status(struct mic_device *mdev, u8 status); +void mic_create_debug_dir(struct mic_device *dev); +void mic_delete_debug_dir(struct mic_device *dev); +void __init mic_init_debugfs(void); +void mic_exit_debugfs(void); #endif diff --git a/drivers/misc/mic/host/mic_main.c b/drivers/misc/mic/host/mic_main.c index 332a15e..998a20a 100644 --- a/drivers/misc/mic/host/mic_main.c +++ b/drivers/misc/mic/host/mic_main.c @@ -26,6 +26,7 @@ #include #include +#include #include "../common/mic_device.h" #include "mic_device.h" #include "mic_x100.h" @@ -63,6 +64,60 @@ static struct class *g_mic_class; /* Base device node number for MIC devices */ static dev_t g_mic_devno; +/* Initialize the device page */ +static int mic_dp_init(struct mic_device *mdev) +{ + mdev->dp = kzalloc(MIC_DP_SIZE, GFP_KERNEL); + if (!mdev->dp) { + dev_err(mdev->sdev->parent, "%s %d err %d\n", + __func__, __LINE__, -ENOMEM); + return -ENOMEM; + } + + mdev->dp_dma_addr = mic_map_single(mdev, + mdev->dp, MIC_DP_SIZE); + if (mic_map_error(mdev->dp_dma_addr)) { + kfree(mdev->dp); + dev_err(mdev->sdev->parent, "%s %d err %d\n", + __func__, __LINE__, -ENOMEM); + return -ENOMEM; + } + mdev->ops->write_spad(mdev, MIC_DPLO_SPAD, mdev->dp_dma_addr); + mdev->ops->write_spad(mdev, MIC_DPHI_SPAD, mdev->dp_dma_addr >> 32); + return 0; +} + +/* Uninitialize the device page */ +static void mic_dp_uninit(struct mic_device *mdev) +{ + mic_unmap_single(mdev, mdev->dp_dma_addr, MIC_DP_SIZE); + kfree(mdev->dp); +} + +/** + * mic_shutdown_db - Shutdown doorbell interrupt handler. + */ +static irqreturn_t mic_shutdown_db(int irq, void *data) +{ + struct mic_device *mdev = data; + struct mic_bootparam *bootparam = mdev->dp; + + mdev->ops->ack_interrupt(mdev); + + switch (bootparam->shutdown_status) { + case MIC_HALTED: + case MIC_POWER_OFF: + case MIC_RESTART: + /* Fall through */ + case MIC_CRASHED: + schedule_work(&mdev->shutdown_work); + break; + default: + break; + }; + return IRQ_HANDLED; +} + /** * mic_ops_init: Initialize HW specific operation tables. * @@ -136,6 +191,26 @@ mic_device_init(struct mic_device *mdev, struct pci_dev *pdev) mic_sysfs_init(mdev); mutex_init(&mdev->mic_mutex); mdev->irq_info.next_avail_src = 0; + INIT_WORK(&mdev->reset_trigger_work, mic_reset_trigger_work); + INIT_WORK(&mdev->shutdown_work, mic_shutdown_work); +} + +/** + * mic_device_uninit - Frees resources allocated during mic_device_init(..) + * + * @mdev: pointer to mic_device instance + * + * returns none + */ +static void mic_device_uninit(struct mic_device *mdev) +{ + /* The cmdline sysfs entry might have allocated cmdline */ + kfree(mdev->cmdline); + kfree(mdev->firmware); + kfree(mdev->ramdisk); + kfree(mdev->bootmode); + flush_work(&mdev->reset_trigger_work); + flush_work(&mdev->shutdown_work); } /** @@ -170,7 +245,7 @@ static int mic_probe(struct pci_dev *pdev, rc = pci_enable_device(pdev); if (rc) { dev_err(&pdev->dev, "failed to enable pci device.\n"); - goto ida_remove; + goto uninit_device; } pci_set_master(pdev); @@ -228,7 +303,40 @@ static int mic_probe(struct pci_dev *pdev, "device_create_with_groups failed rc %d\n", rc); goto smpt_uninit; } + mdev->state_sysfs = sysfs_get_dirent(mdev->sdev->kobj.sd, + NULL, "state"); + if (!mdev->state_sysfs) { + rc = -ENODEV; + dev_err(&pdev->dev, "sysfs_get_dirent failed rc %d\n", rc); + goto destroy_device; + } + + rc = mic_dp_init(mdev); + if (rc) { + dev_err(&pdev->dev, "mic_dp_init failed rc %d\n", rc); + goto sysfs_put; + } + mutex_lock(&mdev->mic_mutex); + + mdev->shutdown_db = mic_next_db(mdev); + mdev->shutdown_cookie = mic_request_irq(mdev, mic_shutdown_db, + "shutdown-interrupt", mdev, mdev->shutdown_db, MIC_INTR_DB); + if (IS_ERR(mdev->shutdown_cookie)) { + rc = PTR_ERR(mdev->shutdown_cookie); + mutex_unlock(&mdev->mic_mutex); + goto dp_uninit; + } + mutex_unlock(&mdev->mic_mutex); + mic_bootparam_init(mdev); + + mic_create_debug_dir(mdev); return 0; +dp_uninit: + mic_dp_uninit(mdev); +sysfs_put: + sysfs_put(mdev->state_sysfs); +destroy_device: + device_destroy(g_mic_class, MKDEV(MAJOR(g_mic_devno), mdev->id)); smpt_uninit: mic_smpt_uninit(mdev); free_interrupts: @@ -241,7 +349,8 @@ release_regions: pci_release_regions(pdev); disable_device: pci_disable_device(pdev); -ida_remove: +uninit_device: + mic_device_uninit(mdev); ida_simple_remove(&g_mic_ida, mdev->id); ida_fail: kfree(mdev); @@ -265,11 +374,20 @@ static void mic_remove(struct pci_dev *pdev) if (!mdev) return; + mic_stop(mdev, false); + mic_delete_debug_dir(mdev); + mutex_lock(&mdev->mic_mutex); + mic_free_irq(mdev, mdev->shutdown_cookie, mdev); + mutex_unlock(&mdev->mic_mutex); + flush_work(&mdev->shutdown_work); + mic_dp_uninit(mdev); + sysfs_put(mdev->state_sysfs); device_destroy(g_mic_class, MKDEV(MAJOR(g_mic_devno), mdev->id)); mic_smpt_uninit(mdev); mic_free_interrupts(mdev, pdev); iounmap(mdev->mmio.va); iounmap(mdev->aper.va); + mic_device_uninit(mdev); pci_release_regions(pdev); pci_disable_device(pdev); ida_simple_remove(&g_mic_ida, mdev->id); @@ -300,14 +418,16 @@ static int __init mic_init(void) goto cleanup_chrdev; } + mic_init_debugfs(); ida_init(&g_mic_ida); ret = pci_register_driver(&mic_driver); if (ret) { pr_err("pci_register_driver failed ret %d\n", ret); - goto class_destroy; + goto cleanup_debugfs; } return ret; -class_destroy: +cleanup_debugfs: + mic_exit_debugfs(); class_destroy(g_mic_class); cleanup_chrdev: unregister_chrdev_region(g_mic_devno, MIC_MAX_NUM_DEVS); @@ -319,6 +439,7 @@ static void __exit mic_exit(void) { pci_unregister_driver(&mic_driver); ida_destroy(&g_mic_ida); + mic_exit_debugfs(); class_destroy(g_mic_class); unregister_chrdev_region(g_mic_devno, MIC_MAX_NUM_DEVS); } diff --git a/drivers/misc/mic/host/mic_sysfs.c b/drivers/misc/mic/host/mic_sysfs.c index 972c182..aaf8499 100644 --- a/drivers/misc/mic/host/mic_sysfs.c +++ b/drivers/misc/mic/host/mic_sysfs.c @@ -20,9 +20,50 @@ */ #include +#include #include "../common/mic_device.h" #include "mic_device.h" +/* + * A state-to-string lookup table, for exposing a human readable state + * via sysfs. Always keep in sync with enum mic_states + */ +static const char * const mic_state_string[] = { + [MIC_OFFLINE] = "offline", + [MIC_ONLINE] = "online", + [MIC_SHUTTING_DOWN] = "shutting_down", + [MIC_RESET_FAILED] = "reset_failed", +}; + +/* + * A shutdown-status-to-string lookup table, for exposing a human + * readable state via sysfs. Always keep in sync with enum mic_shutdown_status + */ +static const char * const mic_shutdown_status_string[] = { + [MIC_NOP] = "nop", + [MIC_CRASHED] = "crashed", + [MIC_HALTED] = "halted", + [MIC_POWER_OFF] = "poweroff", + [MIC_RESTART] = "restart", +}; + +void mic_set_shutdown_status(struct mic_device *mdev, u8 shutdown_status) +{ + dev_dbg(mdev->sdev->parent, "Shutdown Status %s -> %s\n", + mic_shutdown_status_string[mdev->shutdown_status], + mic_shutdown_status_string[shutdown_status]); + mdev->shutdown_status = shutdown_status; +} + +void mic_set_state(struct mic_device *mdev, u8 state) +{ + dev_dbg(mdev->sdev->parent, "State %s -> %s\n", + mic_state_string[mdev->state], + mic_state_string[state]); + mdev->state = state; + sysfs_notify_dirent(mdev->state_sysfs); +} + static ssize_t mic_show_family(struct device *dev, struct device_attribute *attr, char *buf) { @@ -75,9 +116,337 @@ mic_show_stepping(struct device *dev, struct device_attribute *attr, char *buf) } static DEVICE_ATTR(stepping, S_IRUGO, mic_show_stepping, NULL); +static ssize_t +mic_show_state(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct mic_device *mdev = dev_get_drvdata(dev->parent); + + if (!mdev || mdev->state >= MIC_LAST) + return -EINVAL; + + return scnprintf(buf, PAGE_SIZE, "%s\n", + mic_state_string[mdev->state]); +} + +static ssize_t +mic_store_state(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + int rc = 0; + struct mic_device *mdev = dev_get_drvdata(dev->parent); + if (!mdev) + return -EINVAL; + if (sysfs_streq(buf, "boot")) { + rc = mic_start(mdev, buf); + if (rc) { + dev_err(mdev->sdev->parent, + "mic_boot failed rc %d\n", rc); + count = rc; + } + goto done; + } + + if (sysfs_streq(buf, "reset")) { + schedule_work(&mdev->reset_trigger_work); + goto done; + } + + if (sysfs_streq(buf, "shutdown")) { + mic_shutdown(mdev); + goto done; + } + + count = -EINVAL; +done: + return count; +} +static DEVICE_ATTR(state, S_IRUGO|S_IWUSR, mic_show_state, mic_store_state); + +static ssize_t mic_show_shutdown_status(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct mic_device *mdev = dev_get_drvdata(dev->parent); + + if (!mdev || mdev->shutdown_status >= MIC_STATUS_LAST) + return -EINVAL; + + return scnprintf(buf, PAGE_SIZE, "%s\n", + mic_shutdown_status_string[mdev->shutdown_status]); +} +static DEVICE_ATTR(shutdown_status, S_IRUGO|S_IWUSR, + mic_show_shutdown_status, NULL); + +static ssize_t +mic_show_cmdline(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct mic_device *mdev = dev_get_drvdata(dev->parent); + char *cmdline; + + if (!mdev) + return -EINVAL; + + cmdline = mdev->cmdline; + + if (cmdline) + return scnprintf(buf, PAGE_SIZE, "%s\n", cmdline); + return 0; +} + +static ssize_t +mic_store_cmdline(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct mic_device *mdev = dev_get_drvdata(dev->parent); + + if (!mdev) + return -EINVAL; + + mutex_lock(&mdev->mic_mutex); + kfree(mdev->cmdline); + + mdev->cmdline = kmalloc(count + 1, GFP_KERNEL); + if (!mdev->cmdline) { + count = -ENOMEM; + goto unlock; + } + + strncpy(mdev->cmdline, buf, count); + + if (mdev->cmdline[count - 1] == '\n') + mdev->cmdline[count - 1] = '\0'; + else + mdev->cmdline[count] = '\0'; +unlock: + mutex_unlock(&mdev->mic_mutex); + return count; +} +static DEVICE_ATTR(cmdline, S_IRUGO | S_IWUSR, + mic_show_cmdline, mic_store_cmdline); + +static ssize_t +mic_show_firmware(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct mic_device *mdev = dev_get_drvdata(dev->parent); + char *firmware; + + if (!mdev) + return -EINVAL; + + firmware = mdev->firmware; + + if (firmware) + return scnprintf(buf, PAGE_SIZE, "%s\n", firmware); + return 0; +} + +static ssize_t +mic_store_firmware(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct mic_device *mdev = dev_get_drvdata(dev->parent); + + if (!mdev) + return -EINVAL; + + mutex_lock(&mdev->mic_mutex); + kfree(mdev->firmware); + + mdev->firmware = kmalloc(count + 1, GFP_KERNEL); + if (!mdev->firmware) { + count = -ENOMEM; + goto unlock; + } + strncpy(mdev->firmware, buf, count); + + if (mdev->firmware[count - 1] == '\n') + mdev->firmware[count - 1] = '\0'; + else + mdev->firmware[count] = '\0'; +unlock: + mutex_unlock(&mdev->mic_mutex); + return count; +} +static DEVICE_ATTR(firmware, S_IRUGO | S_IWUSR, + mic_show_firmware, mic_store_firmware); + +static ssize_t +mic_show_ramdisk(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct mic_device *mdev = dev_get_drvdata(dev->parent); + char *ramdisk; + + if (!mdev) + return -EINVAL; + + ramdisk = mdev->ramdisk; + + if (ramdisk) + return scnprintf(buf, PAGE_SIZE, "%s\n", ramdisk); + return 0; +} + +static ssize_t +mic_store_ramdisk(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct mic_device *mdev = dev_get_drvdata(dev->parent); + + if (!mdev) + return -EINVAL; + + mutex_lock(&mdev->mic_mutex); + kfree(mdev->ramdisk); + + mdev->ramdisk = kmalloc(count + 1, GFP_KERNEL); + if (!mdev->ramdisk) { + count = -ENOMEM; + goto unlock; + } + + strncpy(mdev->ramdisk, buf, count); + + if (mdev->ramdisk[count - 1] == '\n') + mdev->ramdisk[count - 1] = '\0'; + else + mdev->ramdisk[count] = '\0'; +unlock: + mutex_unlock(&mdev->mic_mutex); + return count; +} +static DEVICE_ATTR(ramdisk, S_IRUGO | S_IWUSR, + mic_show_ramdisk, mic_store_ramdisk); + +static ssize_t +mic_show_bootmode(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct mic_device *mdev = dev_get_drvdata(dev->parent); + char *bootmode; + + if (!mdev) + return -EINVAL; + + bootmode = mdev->bootmode; + + if (bootmode) + return scnprintf(buf, PAGE_SIZE, "%s\n", bootmode); + return 0; +} + +static ssize_t +mic_store_bootmode(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct mic_device *mdev = dev_get_drvdata(dev->parent); + + if (!mdev) + return -EINVAL; + + if (!sysfs_streq(buf, "linux") && !sysfs_streq(buf, "elf")) + return -EINVAL; + + mutex_lock(&mdev->mic_mutex); + kfree(mdev->bootmode); + + mdev->bootmode = kmalloc(count + 1, GFP_KERNEL); + if (!mdev->bootmode) { + count = -ENOMEM; + goto unlock; + } + + strncpy(mdev->bootmode, buf, count); + + if (mdev->bootmode[count - 1] == '\n') + mdev->bootmode[count - 1] = '\0'; + else + mdev->bootmode[count] = '\0'; +unlock: + mutex_unlock(&mdev->mic_mutex); + return count; +} +static DEVICE_ATTR(bootmode, S_IRUGO | S_IWUSR, + mic_show_bootmode, mic_store_bootmode); + +static ssize_t +mic_show_log_buf_addr(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct mic_device *mdev = dev_get_drvdata(dev->parent); + + if (!mdev) + return -EINVAL; + + return scnprintf(buf, PAGE_SIZE, "%p\n", mdev->log_buf_addr); +} + +static ssize_t +mic_store_log_buf_addr(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct mic_device *mdev = dev_get_drvdata(dev->parent); + int ret; + unsigned long addr; + + if (!mdev) + return -EINVAL; + + ret = kstrtoul(buf, 16, &addr); + if (ret) + goto exit; + + mdev->log_buf_addr = (void *)addr; + ret = count; +exit: + return ret; +} +static DEVICE_ATTR(log_buf_addr, S_IRUGO | S_IWUSR, + mic_show_log_buf_addr, mic_store_log_buf_addr); + +static ssize_t +mic_show_log_buf_len(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct mic_device *mdev = dev_get_drvdata(dev->parent); + + if (!mdev) + return -EINVAL; + + return scnprintf(buf, PAGE_SIZE, "%p\n", mdev->log_buf_len); +} + +static ssize_t +mic_store_log_buf_len(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct mic_device *mdev = dev_get_drvdata(dev->parent); + int ret; + unsigned long addr; + + if (!mdev) + return -EINVAL; + + ret = kstrtoul(buf, 16, &addr); + if (ret) + goto exit; + + mdev->log_buf_len = (int *)addr; + ret = count; +exit: + return ret; +} +static DEVICE_ATTR(log_buf_len, S_IRUGO | S_IWUSR, + mic_show_log_buf_len, mic_store_log_buf_len); + static struct attribute *mic_default_attrs[] = { &dev_attr_family.attr, &dev_attr_stepping.attr, + &dev_attr_state.attr, + &dev_attr_shutdown_status.attr, + &dev_attr_cmdline.attr, + &dev_attr_firmware.attr, + &dev_attr_ramdisk.attr, + &dev_attr_bootmode.attr, + &dev_attr_log_buf_addr.attr, + &dev_attr_log_buf_len.attr, NULL }; diff --git a/drivers/misc/mic/host/mic_x100.c b/drivers/misc/mic/host/mic_x100.c index b637316..a12ae5c 100644 --- a/drivers/misc/mic/host/mic_x100.c +++ b/drivers/misc/mic/host/mic_x100.c @@ -20,6 +20,9 @@ */ #include #include +#include +#include +#include #include "../common/mic_device.h" #include "mic_device.h" @@ -256,6 +259,248 @@ mic_x100_program_msi_to_src_map(struct mic_device *mdev, mic_mmio_write(mw, reg, mxar); } +/* + * mic_x100_reset_fw_ready - Reset Firmware ready status field. + * @mdev: pointer to mic_device instance + */ +static void mic_x100_reset_fw_ready(struct mic_device *mdev) +{ + mdev->ops->write_spad(mdev, MIC_X100_DOWNLOAD_INFO, 0); +} + +/* + * mic_x100_is_fw_ready - Check if firmware is ready. + * @mdev: pointer to mic_device instance + */ +static bool mic_x100_is_fw_ready(struct mic_device *mdev) +{ + u32 scratch2 = mdev->ops->read_spad(mdev, MIC_X100_DOWNLOAD_INFO); + return MIC_X100_SPAD2_DOWNLOAD_STATUS(scratch2) ? true : false; +} + +/** + * mic_x100_get_apic_id - Get bootstrap APIC ID. + * @mdev: pointer to mic_device instance + */ +static u32 mic_x100_get_apic_id(struct mic_device *mdev) +{ + u32 scratch2 = 0; + + scratch2 = mdev->ops->read_spad(mdev, MIC_X100_DOWNLOAD_INFO); + return MIC_X100_SPAD2_APIC_ID(scratch2); +} + +/** + * mic_x100_send_firmware_intr - Send an interrupt to the firmware on MIC. + * @mdev: pointer to mic_device instance + */ +static void mic_x100_send_firmware_intr(struct mic_device *mdev) +{ + u32 apicicr_low; + u64 apic_icr_offset = MIC_X100_SBOX_APICICR7; + int vector = MIC_X100_BSP_INTERRUPT_VECTOR; + struct mic_mw *mw = &mdev->mmio; + + /* + * For MIC we need to make sure we "hit" + * the send_icr bit (13). + */ + apicicr_low = (vector | (1 << 13)); + + mic_mmio_write(mw, mic_x100_get_apic_id(mdev), + MIC_X100_SBOX_BASE_ADDRESS + apic_icr_offset + 4); + + /* Ensure that the interrupt is ordered w.r.t. previous stores. */ + wmb(); + mic_mmio_write(mw, apicicr_low, + MIC_X100_SBOX_BASE_ADDRESS + apic_icr_offset); +} + +/** + * mic_x100_hw_reset - Reset the MIC device. + * @mdev: pointer to mic_device instance + */ +static void mic_x100_hw_reset(struct mic_device *mdev) +{ + u32 reset_reg; + u32 rgcr = MIC_X100_SBOX_BASE_ADDRESS + MIC_X100_SBOX_RGCR; + struct mic_mw *mw = &mdev->mmio; + + /* Ensure that the reset is ordered w.r.t. previous loads and stores */ + mb(); + /* Trigger reset */ + reset_reg = mic_mmio_read(mw, rgcr); + reset_reg |= 0x1; + mic_mmio_write(mw, reset_reg, rgcr); + /* + * It seems we really want to delay at least 1 second + * after touching reset to prevent a lot of problems. + */ + msleep(1000); +} + +/** + * mic_x100_load_command_line - Load command line to MIC. + * @mdev: pointer to mic_device instance + * @fw: the firmware image + * + * RETURNS: An appropriate -ERRNO error value on error, or zero for success. + */ +static int +mic_x100_load_command_line(struct mic_device *mdev, const struct firmware *fw) +{ + u32 len = 0; + u32 boot_mem; + char *buf; + void __iomem *cmd_line_va = mdev->aper.va + mdev->bootaddr + fw->size; +#define CMDLINE_SIZE 2048 + + boot_mem = mdev->aper.len >> 20; + buf = kzalloc(CMDLINE_SIZE, GFP_KERNEL); + if (!buf) { + dev_err(mdev->sdev->parent, + "%s %d allocation failed\n", __func__, __LINE__); + return -ENOMEM; + } + len += snprintf(buf, CMDLINE_SIZE - len, + " mem=%dM", boot_mem); + if (mdev->cmdline) + snprintf(buf + len, CMDLINE_SIZE - len, + " %s", mdev->cmdline); + memcpy_toio(cmd_line_va, buf, strlen(buf) + 1); + kfree(buf); + return 0; +} + +/** + * mic_x100_load_ramdisk - Load ramdisk to MIC. + * @mdev: pointer to mic_device instance + * + * RETURNS: An appropriate -ERRNO error value on error, or zero for success. + */ +static int +mic_x100_load_ramdisk(struct mic_device *mdev) +{ + const struct firmware *fw; + int rc; + struct boot_params __iomem *bp = mdev->aper.va + mdev->bootaddr; + + rc = request_firmware(&fw, + mdev->ramdisk, mdev->sdev->parent); + if (rc < 0) { + dev_err(mdev->sdev->parent, + "ramdisk request_firmware failed: %d %s\n", + rc, mdev->ramdisk); + goto error; + } + /* + * Typically the bootaddr for card OS is 64M + * so copy over the ramdisk @ 128M. + */ + memcpy_toio(mdev->aper.va + (mdev->bootaddr << 1), + fw->data, fw->size); + iowrite32(cpu_to_le32(mdev->bootaddr << 1), &bp->hdr.ramdisk_image); + iowrite32(cpu_to_le32(fw->size), &bp->hdr.ramdisk_size); + release_firmware(fw); +error: + return rc; +} + +/** + * mic_x100_get_boot_addr - Get MIC boot address. + * @mdev: pointer to mic_device instance + * + * This function is called during firmware load to determine + * the address at which the OS should be downloaded in card + * memory i.e. GDDR. + * RETURNS: An appropriate -ERRNO error value on error, or zero for success. + */ +static int +mic_x100_get_boot_addr(struct mic_device *mdev) +{ + u32 scratch2, boot_addr; + int rc = 0; + + scratch2 = mdev->ops->read_spad(mdev, MIC_X100_DOWNLOAD_INFO); + boot_addr = MIC_X100_SPAD2_DOWNLOAD_ADDR(scratch2); + dev_dbg(mdev->sdev->parent, "%s %d boot_addr 0x%x\n", + __func__, __LINE__, boot_addr); + if (boot_addr > (1 << 31)) { + dev_err(mdev->sdev->parent, + "incorrect bootaddr 0x%x\n", + boot_addr); + rc = -EINVAL; + goto error; + } + mdev->bootaddr = boot_addr; +error: + return rc; +} + +/** + * mic_x100_load_firmware - Load firmware to MIC. + * @mdev: pointer to mic_device instance + * @buf: buffer containing boot string including firmware/ramdisk path. + * + * RETURNS: An appropriate -ERRNO error value on error, or zero for success. + */ +static int +mic_x100_load_firmware(struct mic_device *mdev, const char *buf) +{ + int rc; + const struct firmware *fw; + + rc = mic_x100_get_boot_addr(mdev); + if (rc) + goto error; + /* load OS */ + rc = request_firmware(&fw, mdev->firmware, mdev->sdev->parent); + if (rc < 0) { + dev_err(mdev->sdev->parent, + "ramdisk request_firmware failed: %d %s\n", + rc, mdev->firmware); + goto error; + } + if (mdev->bootaddr > mdev->aper.len - fw->size) { + rc = -EINVAL; + dev_err(mdev->sdev->parent, "%s %d rc %d bootaddr 0x%x\n", + __func__, __LINE__, rc, mdev->bootaddr); + release_firmware(fw); + goto error; + } + memcpy_toio(mdev->aper.va + mdev->bootaddr, fw->data, fw->size); + mdev->ops->write_spad(mdev, MIC_X100_FW_SIZE, fw->size); + if (!strcmp(mdev->bootmode, "elf")) + goto done; + /* load command line */ + rc = mic_x100_load_command_line(mdev, fw); + if (rc) { + dev_err(mdev->sdev->parent, "%s %d rc %d\n", + __func__, __LINE__, rc); + goto error; + } + release_firmware(fw); + /* load ramdisk */ + if (mdev->ramdisk) + rc = mic_x100_load_ramdisk(mdev); +error: + dev_dbg(mdev->sdev->parent, "%s %d rc %d\n", + __func__, __LINE__, rc); +done: + return rc; +} + +/** + * mic_x100_get_postcode - Get postcode status from firmware. + * @mdev: pointer to mic_device instance + * + * RETURNS: postcode. + */ +static u32 mic_x100_get_postcode(struct mic_device *mdev) +{ + return mic_mmio_read(&mdev->mmio, MIC_X100_POSTCODE); +} + /** * mic_x100_smpt_set - Update an SMPT entry with a DMA address. * @mdev: pointer to mic_device instance @@ -311,6 +556,12 @@ struct mic_hw_ops mic_x100_ops = { .write_spad = mic_x100_write_spad, .send_intr = mic_x100_send_intr, .ack_interrupt = mic_x100_ack_interrupt, + .reset = mic_x100_hw_reset, + .reset_fw_ready = mic_x100_reset_fw_ready, + .is_fw_ready = mic_x100_is_fw_ready, + .send_firmware_intr = mic_x100_send_firmware_intr, + .load_mic_fw = mic_x100_load_firmware, + .get_postcode = mic_x100_get_postcode, }; struct mic_hw_intr_ops mic_x100_intr_ops = { diff --git a/drivers/misc/mic/host/mic_x100.h b/drivers/misc/mic/host/mic_x100.h index 642cae9..8b7daa1 100644 --- a/drivers/misc/mic/host/mic_x100.h +++ b/drivers/misc/mic/host/mic_x100.h @@ -69,6 +69,15 @@ #define MIC_X100_NUM_SBOX_IRQ 8 #define MIC_X100_NUM_RDMASR_IRQ 8 #define MIC_X100_RDMASR_IRQ_BASE 17 +#define MIC_X100_SPAD2_DOWNLOAD_STATUS(x) ((x) & 0x1) +#define MIC_X100_SPAD2_APIC_ID(x) (((x) >> 1) & 0x1ff) +#define MIC_X100_SPAD2_DOWNLOAD_ADDR(x) ((x) & 0xfffff000) +#define MIC_X100_SBOX_APICICR7 0x0000AA08 +#define MIC_X100_SBOX_RGCR 0x00004010 +#define MIC_X100_SBOX_SDBIC0 0x0000CC90 +#define MIC_X100_DOWNLOAD_INFO 2 +#define MIC_X100_FW_SIZE 5 +#define MIC_X100_POSTCODE 0x242c static const u16 mic_x100_intr_init[] = { MIC_X100_DOORBELL_IDX_START, @@ -79,6 +88,9 @@ static const u16 mic_x100_intr_init[] = { MIC_X100_NUM_ERR, }; +/* Host->Card(bootstrap) Interrupt Vector */ +#define MIC_X100_BSP_INTERRUPT_VECTOR 229 + extern struct mic_hw_ops mic_x100_ops; extern struct mic_smpt_ops mic_x100_smpt_ops; extern struct mic_hw_intr_ops mic_x100_intr_ops; diff --git a/include/uapi/linux/Kbuild b/include/uapi/linux/Kbuild index 115add2..d37263f 100644 --- a/include/uapi/linux/Kbuild +++ b/include/uapi/linux/Kbuild @@ -241,6 +241,7 @@ header-y += media.h header-y += mei.h header-y += mempolicy.h header-y += meye.h +header-y += mic_common.h header-y += mii.h header-y += minix_fs.h header-y += mman.h diff --git a/include/uapi/linux/mic_common.h b/include/uapi/linux/mic_common.h new file mode 100644 index 0000000..a9091e5 --- /dev/null +++ b/include/uapi/linux/mic_common.h @@ -0,0 +1,74 @@ +/* + * Intel MIC Platform Software Stack (MPSS) + * + * Copyright(c) 2013 Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, version 2, as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * The full GNU General Public License is included in this distribution in + * the file called "COPYING". + * + * Intel MIC driver. + * + */ +#ifndef __MIC_COMMON_H_ +#define __MIC_COMMON_H_ + +#include + +/** + * struct mic_bootparam: Virtio device independent information in device page + * + * @magic: A magic value used by the card to ensure it can see the host + * @c2h_shutdown_db: Card to Host shutdown doorbell set by host + * @h2c_shutdown_db: Host to Card shutdown doorbell set by card + * @h2c_config_db: Host to Card Virtio config doorbell set by card + * @shutdown_status: Card shutdown status set by card + * @shutdown_card: Set to 1 by the host when a card shutdown is initiated + */ +struct mic_bootparam { + __u32 magic; + __s8 c2h_shutdown_db; + __s8 h2c_shutdown_db; + __s8 h2c_config_db; + __u8 shutdown_status; + __u8 shutdown_card; +} __aligned(8); + +/* Device page size */ +#define MIC_DP_SIZE 4096 + +#define MIC_MAGIC 0xc0ffee00 + +/** + * enum mic_states - MIC states. + */ +enum mic_states { + MIC_OFFLINE = 0, + MIC_ONLINE, + MIC_SHUTTING_DOWN, + MIC_RESET_FAILED, + MIC_LAST +}; + +/** + * enum mic_status - MIC status reported by card after + * a host or card initiated shutdown or a card crash. + */ +enum mic_status { + MIC_NOP = 0, + MIC_CRASHED, + MIC_HALTED, + MIC_POWER_OFF, + MIC_RESTART, + MIC_STATUS_LAST +}; + +#endif -- cgit v0.10.2 From aa27badd8972adb731f05d49ab74ec63e0826935 Mon Sep 17 00:00:00 2001 From: Sudeep Dutt Date: Thu, 5 Sep 2013 16:42:06 -0700 Subject: Intel MIC Card Driver for X100 family. This patch does the following: a) Initializes the Intel MIC X100 platform device and driver. b) Sets up support to handle shutdown requests from the host. c) Maps the device page after obtaining the device page address from the scratchpad registers updated by the host. d) Informs the host upon a card crash by registering a panic notifier. e) Informs the host upon a poweroff/halt event. Co-author: Dasaratharaman Chandramouli Signed-off-by: Ashutosh Dixit Signed-off-by: Caz Yokoyama Signed-off-by: Dasaratharaman Chandramouli Signed-off-by: Nikhil Rao Signed-off-by: Harshavardhan R Kharche Signed-off-by: Sudeep Dutt Acked-by: Yaozu (Eddie) Dong Reviewed-by: Peter P Waskiewicz Jr Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/misc/mic/Kconfig b/drivers/misc/mic/Kconfig index aaefd0c..279a2e6 100644 --- a/drivers/misc/mic/Kconfig +++ b/drivers/misc/mic/Kconfig @@ -17,3 +17,21 @@ config INTEL_MIC_HOST More information about the Intel MIC family as well as the Linux OS and tools for MIC to use with this driver are available from . + +comment "Intel MIC Card Driver" + +config INTEL_MIC_CARD + tristate "Intel MIC Card Driver" + depends on 64BIT + default N + help + This enables card driver support for the Intel Many Integrated + Core (MIC) device family. The card driver communicates shutdown/ + crash events to the host and allows registration/configuration of + virtio devices. Intel MIC X100 devices are currently supported. + + If you are building a card kernel for an Intel MIC device then + say M (recommended) or Y, else say N. If unsure say N. + + For more information see + . diff --git a/drivers/misc/mic/Makefile b/drivers/misc/mic/Makefile index 8e72421..05b34d6 100644 --- a/drivers/misc/mic/Makefile +++ b/drivers/misc/mic/Makefile @@ -3,3 +3,4 @@ # Copyright(c) 2013, Intel Corporation. # obj-$(CONFIG_INTEL_MIC_HOST) += host/ +obj-$(CONFIG_INTEL_MIC_CARD) += card/ diff --git a/drivers/misc/mic/card/Makefile b/drivers/misc/mic/card/Makefile new file mode 100644 index 0000000..6e9675e --- /dev/null +++ b/drivers/misc/mic/card/Makefile @@ -0,0 +1,10 @@ +# +# Makefile - Intel MIC Linux driver. +# Copyright(c) 2013, Intel Corporation. +# +ccflags-y += -DINTEL_MIC_CARD + +obj-$(CONFIG_INTEL_MIC_CARD) += mic_card.o +mic_card-y += mic_x100.o +mic_card-y += mic_device.o +mic_card-y += mic_debugfs.o diff --git a/drivers/misc/mic/card/mic_debugfs.c b/drivers/misc/mic/card/mic_debugfs.c new file mode 100644 index 0000000..95cf186 --- /dev/null +++ b/drivers/misc/mic/card/mic_debugfs.c @@ -0,0 +1,130 @@ +/* + * Intel MIC Platform Software Stack (MPSS) + * + * Copyright(c) 2013 Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, version 2, as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * The full GNU General Public License is included in this distribution in + * the file called "COPYING". + * + * Disclaimer: The codes contained in these modules may be specific to + * the Intel Software Development Platform codenamed: Knights Ferry, and + * the Intel product codenamed: Knights Corner, and are not backward + * compatible with other Intel products. Additionally, Intel will NOT + * support the codes or instruction set in future products. + * + * Intel MIC Card driver. + * + */ +#include +#include +#include +#include +#include + +#include "../common/mic_device.h" +#include "mic_device.h" + +/* Debugfs parent dir */ +static struct dentry *mic_dbg; + +/** + * mic_intr_test - Send interrupts to host. + */ +static int mic_intr_test(struct seq_file *s, void *unused) +{ + struct mic_driver *mdrv = s->private; + struct mic_device *mdev = &mdrv->mdev; + + mic_send_intr(mdev, 0); + msleep(1000); + mic_send_intr(mdev, 1); + msleep(1000); + mic_send_intr(mdev, 2); + msleep(1000); + mic_send_intr(mdev, 3); + msleep(1000); + + return 0; +} + +static int mic_intr_test_open(struct inode *inode, struct file *file) +{ + return single_open(file, mic_intr_test, inode->i_private); +} + +static int mic_intr_test_release(struct inode *inode, struct file *file) +{ + return single_release(inode, file); +} + +static const struct file_operations intr_test_ops = { + .owner = THIS_MODULE, + .open = mic_intr_test_open, + .read = seq_read, + .llseek = seq_lseek, + .release = mic_intr_test_release +}; + +/** + * mic_create_card_debug_dir - Initialize MIC debugfs entries. + */ +void __init mic_create_card_debug_dir(struct mic_driver *mdrv) +{ + struct dentry *d; + + if (!mic_dbg) + return; + + mdrv->dbg_dir = debugfs_create_dir(mdrv->name, mic_dbg); + if (!mdrv->dbg_dir) { + dev_err(mdrv->dev, "Cant create dbg_dir %s\n", mdrv->name); + return; + } + + d = debugfs_create_file("intr_test", 0444, mdrv->dbg_dir, + mdrv, &intr_test_ops); + + if (!d) { + dev_err(mdrv->dev, + "Cant create dbg intr_test %s\n", mdrv->name); + return; + } +} + +/** + * mic_delete_card_debug_dir - Uninitialize MIC debugfs entries. + */ +void mic_delete_card_debug_dir(struct mic_driver *mdrv) +{ + if (!mdrv->dbg_dir) + return; + + debugfs_remove_recursive(mdrv->dbg_dir); +} + +/** + * mic_init_card_debugfs - Initialize global debugfs entry. + */ +void __init mic_init_card_debugfs(void) +{ + mic_dbg = debugfs_create_dir(KBUILD_MODNAME, NULL); + if (!mic_dbg) + pr_err("can't create debugfs dir\n"); +} + +/** + * mic_exit_card_debugfs - Uninitialize global debugfs entry + */ +void mic_exit_card_debugfs(void) +{ + debugfs_remove(mic_dbg); +} diff --git a/drivers/misc/mic/card/mic_device.c b/drivers/misc/mic/card/mic_device.c new file mode 100644 index 0000000..3c5c302 --- /dev/null +++ b/drivers/misc/mic/card/mic_device.c @@ -0,0 +1,299 @@ +/* + * Intel MIC Platform Software Stack (MPSS) + * + * Copyright(c) 2013 Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, version 2, as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * The full GNU General Public License is included in this distribution in + * the file called "COPYING". + * + * Disclaimer: The codes contained in these modules may be specific to + * the Intel Software Development Platform codenamed: Knights Ferry, and + * the Intel product codenamed: Knights Corner, and are not backward + * compatible with other Intel products. Additionally, Intel will NOT + * support the codes or instruction set in future products. + * + * Intel MIC Card driver. + * + */ +#include +#include +#include +#include + +#include +#include "../common/mic_device.h" +#include "mic_device.h" + +static struct mic_driver *g_drv; +static struct mic_irq *shutdown_cookie; + +static void mic_notify_host(u8 state) +{ + struct mic_driver *mdrv = g_drv; + struct mic_bootparam __iomem *bootparam = mdrv->dp; + + iowrite8(state, &bootparam->shutdown_status); + dev_dbg(mdrv->dev, "%s %d system_state %d\n", + __func__, __LINE__, state); + mic_send_intr(&mdrv->mdev, ioread8(&bootparam->c2h_shutdown_db)); +} + +static int mic_panic_event(struct notifier_block *this, unsigned long event, + void *ptr) +{ + struct mic_driver *mdrv = g_drv; + struct mic_bootparam __iomem *bootparam = mdrv->dp; + + iowrite8(-1, &bootparam->h2c_config_db); + iowrite8(-1, &bootparam->h2c_shutdown_db); + mic_notify_host(MIC_CRASHED); + return NOTIFY_DONE; +} + +static struct notifier_block mic_panic = { + .notifier_call = mic_panic_event, +}; + +static irqreturn_t mic_shutdown_isr(int irq, void *data) +{ + struct mic_driver *mdrv = g_drv; + struct mic_bootparam __iomem *bootparam = mdrv->dp; + + mic_ack_interrupt(&g_drv->mdev); + if (ioread8(&bootparam->shutdown_card)) + orderly_poweroff(true); + return IRQ_HANDLED; +} + +static int mic_shutdown_init(void) +{ + int rc = 0; + struct mic_driver *mdrv = g_drv; + struct mic_bootparam __iomem *bootparam = mdrv->dp; + int shutdown_db; + + shutdown_db = mic_next_card_db(); + shutdown_cookie = mic_request_card_irq(mic_shutdown_isr, + "Shutdown", mdrv, shutdown_db); + if (IS_ERR(shutdown_cookie)) + rc = PTR_ERR(shutdown_cookie); + else + iowrite8(shutdown_db, &bootparam->h2c_shutdown_db); + return rc; +} + +static void mic_shutdown_uninit(void) +{ + struct mic_driver *mdrv = g_drv; + struct mic_bootparam __iomem *bootparam = mdrv->dp; + + iowrite8(-1, &bootparam->h2c_shutdown_db); + mic_free_card_irq(shutdown_cookie, mdrv); +} + +static int __init mic_dp_init(void) +{ + struct mic_driver *mdrv = g_drv; + struct mic_device *mdev = &mdrv->mdev; + struct mic_bootparam __iomem *bootparam; + u64 lo, hi, dp_dma_addr; + u32 magic; + + lo = mic_read_spad(&mdrv->mdev, MIC_DPLO_SPAD); + hi = mic_read_spad(&mdrv->mdev, MIC_DPHI_SPAD); + + dp_dma_addr = lo | (hi << 32); + mdrv->dp = mic_card_map(mdev, dp_dma_addr, MIC_DP_SIZE); + if (!mdrv->dp) { + dev_err(mdrv->dev, "Cannot remap Aperture BAR\n"); + return -ENOMEM; + } + bootparam = mdrv->dp; + magic = ioread32(&bootparam->magic); + if (MIC_MAGIC != magic) { + dev_err(mdrv->dev, "bootparam magic mismatch 0x%x\n", magic); + return -EIO; + } + return 0; +} + +/* Uninitialize the device page */ +static void mic_dp_uninit(void) +{ + mic_card_unmap(&g_drv->mdev, g_drv->dp); +} + +/** + * mic_request_card_irq - request an irq. + * + * @func: The callback function that handles the interrupt. + * @name: The ASCII name of the callee requesting the irq. + * @data: private data that is returned back when calling the + * function handler. + * @index: The doorbell index of the requester. + * + * returns: The cookie that is transparent to the caller. Passed + * back when calling mic_free_irq. An appropriate error code + * is returned on failure. Caller needs to use IS_ERR(return_val) + * to check for failure and PTR_ERR(return_val) to obtained the + * error code. + * + */ +struct mic_irq *mic_request_card_irq(irqreturn_t (*func)(int irq, void *data), + const char *name, void *data, int index) +{ + int rc = 0; + unsigned long cookie; + struct mic_driver *mdrv = g_drv; + + rc = request_irq(mic_db_to_irq(mdrv, index), func, + 0, name, data); + if (rc) { + dev_err(mdrv->dev, "request_irq failed rc = %d\n", rc); + goto err; + } + mdrv->irq_info.irq_usage_count[index]++; + cookie = index; + return (struct mic_irq *)cookie; +err: + return ERR_PTR(rc); + +} + +/** + * mic_free_card_irq - free irq. + * + * @cookie: cookie obtained during a successful call to mic_request_irq + * @data: private data specified by the calling function during the + * mic_request_irq + * + * returns: none. + */ +void mic_free_card_irq(struct mic_irq *cookie, void *data) +{ + int index; + struct mic_driver *mdrv = g_drv; + + index = (unsigned long)cookie & 0xFFFFU; + free_irq(mic_db_to_irq(mdrv, index), data); + mdrv->irq_info.irq_usage_count[index]--; +} + +/** + * mic_next_card_db - Get the doorbell with minimum usage count. + * + * Returns the irq index. + */ +int mic_next_card_db(void) +{ + int i; + int index = 0; + struct mic_driver *mdrv = g_drv; + + for (i = 0; i < mdrv->intr_info.num_intr; i++) { + if (mdrv->irq_info.irq_usage_count[i] < + mdrv->irq_info.irq_usage_count[index]) + index = i; + } + + return index; +} + +/** + * mic_init_irq - Initialize irq information. + * + * Returns 0 in success. Appropriate error code on failure. + */ +static int mic_init_irq(void) +{ + struct mic_driver *mdrv = g_drv; + + mdrv->irq_info.irq_usage_count = kzalloc((sizeof(u32) * + mdrv->intr_info.num_intr), + GFP_KERNEL); + if (!mdrv->irq_info.irq_usage_count) + return -ENOMEM; + return 0; +} + +/** + * mic_uninit_irq - Uninitialize irq information. + * + * None. + */ +static void mic_uninit_irq(void) +{ + struct mic_driver *mdrv = g_drv; + + kfree(mdrv->irq_info.irq_usage_count); +} + +/* + * mic_driver_init - MIC driver initialization tasks. + * + * Returns 0 in success. Appropriate error code on failure. + */ +int __init mic_driver_init(struct mic_driver *mdrv) +{ + int rc; + + g_drv = mdrv; + /* + * Unloading the card module is not supported. The MIC card module + * handles fundamental operations like host/card initiated shutdowns + * and informing the host about card crashes and cannot be unloaded. + */ + if (!try_module_get(mdrv->dev->driver->owner)) { + rc = -ENODEV; + goto done; + } + rc = mic_dp_init(); + if (rc) + goto put; + rc = mic_init_irq(); + if (rc) + goto dp_uninit; + rc = mic_shutdown_init(); + if (rc) + goto irq_uninit; + mic_create_card_debug_dir(mdrv); + atomic_notifier_chain_register(&panic_notifier_list, &mic_panic); +done: + return rc; +irq_uninit: + mic_uninit_irq(); +dp_uninit: + mic_dp_uninit(); +put: + module_put(mdrv->dev->driver->owner); + return rc; +} + +/* + * mic_driver_uninit - MIC driver uninitialization tasks. + * + * Returns None + */ +void mic_driver_uninit(struct mic_driver *mdrv) +{ + mic_delete_card_debug_dir(mdrv); + /* + * Inform the host about the shutdown status i.e. poweroff/restart etc. + * The module cannot be unloaded so the only code path to call + * mic_devices_uninit(..) is the shutdown callback. + */ + mic_notify_host(system_state); + mic_shutdown_uninit(); + mic_uninit_irq(); + mic_dp_uninit(); + module_put(mdrv->dev->driver->owner); +} diff --git a/drivers/misc/mic/card/mic_device.h b/drivers/misc/mic/card/mic_device.h new file mode 100644 index 0000000..347b9b3 --- /dev/null +++ b/drivers/misc/mic/card/mic_device.h @@ -0,0 +1,133 @@ +/* + * Intel MIC Platform Software Stack (MPSS) + * + * Copyright(c) 2013 Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, version 2, as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * The full GNU General Public License is included in this distribution in + * the file called "COPYING". + * + * Disclaimer: The codes contained in these modules may be specific to + * the Intel Software Development Platform codenamed: Knights Ferry, and + * the Intel product codenamed: Knights Corner, and are not backward + * compatible with other Intel products. Additionally, Intel will NOT + * support the codes or instruction set in future products. + * + * Intel MIC Card driver. + * + */ +#ifndef _MIC_CARD_DEVICE_H_ +#define _MIC_CARD_DEVICE_H_ + +#include +#include + +/** + * struct mic_intr_info - Contains h/w specific interrupt sources info + * + * @num_intr: The number of irqs available + */ +struct mic_intr_info { + u32 num_intr; +}; + +/** + * struct mic_irq_info - OS specific irq information + * + * @irq_usage_count: usage count array tracking the number of sources + * assigned for each irq. + */ +struct mic_irq_info { + int *irq_usage_count; +}; + +/** + * struct mic_device - MIC device information. + * + * @mmio: MMIO bar information. + */ +struct mic_device { + struct mic_mw mmio; +}; + +/** + * struct mic_driver - MIC card driver information. + * + * @name: Name for MIC driver. + * @dbg_dir: debugfs directory of this MIC device. + * @dev: The device backing this MIC. + * @dp: The pointer to the virtio device page. + * @mdev: MIC device information for the host. + * @hotplug_work: Hot plug work for adding/removing virtio devices. + * @irq_info: The OS specific irq information + * @intr_info: H/W specific interrupt information. + */ +struct mic_driver { + char name[20]; + struct dentry *dbg_dir; + struct device *dev; + void __iomem *dp; + struct mic_device mdev; + struct work_struct hotplug_work; + struct mic_irq_info irq_info; + struct mic_intr_info intr_info; +}; + +/** + * struct mic_irq - opaque pointer used as cookie + */ +struct mic_irq; + +/** + * mic_mmio_read - read from an MMIO register. + * @mw: MMIO register base virtual address. + * @offset: register offset. + * + * RETURNS: register value. + */ +static inline u32 mic_mmio_read(struct mic_mw *mw, u32 offset) +{ + return ioread32(mw->va + offset); +} + +/** + * mic_mmio_write - write to an MMIO register. + * @mw: MMIO register base virtual address. + * @val: the data value to put into the register + * @offset: register offset. + * + * RETURNS: none. + */ +static inline void +mic_mmio_write(struct mic_mw *mw, u32 val, u32 offset) +{ + iowrite32(val, mw->va + offset); +} + +int mic_driver_init(struct mic_driver *mdrv); +void mic_driver_uninit(struct mic_driver *mdrv); +int mic_next_card_db(void); +struct mic_irq *mic_request_card_irq(irqreturn_t (*func)(int irq, void *data), + const char *name, void *data, int intr_src); +void mic_free_card_irq(struct mic_irq *cookie, void *data); +u32 mic_read_spad(struct mic_device *mdev, unsigned int idx); +void mic_send_intr(struct mic_device *mdev, int doorbell); +int mic_db_to_irq(struct mic_driver *mdrv, int db); +u32 mic_ack_interrupt(struct mic_device *mdev); +void mic_hw_intr_init(struct mic_driver *mdrv); +void __iomem * +mic_card_map(struct mic_device *mdev, dma_addr_t addr, size_t size); +void mic_card_unmap(struct mic_device *mdev, void __iomem *addr); +void __init mic_create_card_debug_dir(struct mic_driver *mdrv); +void mic_delete_card_debug_dir(struct mic_driver *mdrv); +void __init mic_init_card_debugfs(void); +void mic_exit_card_debugfs(void); +#endif diff --git a/drivers/misc/mic/card/mic_x100.c b/drivers/misc/mic/card/mic_x100.c new file mode 100644 index 0000000..7cb3469 --- /dev/null +++ b/drivers/misc/mic/card/mic_x100.c @@ -0,0 +1,256 @@ +/* + * Intel MIC Platform Software Stack (MPSS) + * + * Copyright(c) 2013 Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, version 2, as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * The full GNU General Public License is included in this distribution in + * the file called "COPYING". + * + * Disclaimer: The codes contained in these modules may be specific to + * the Intel Software Development Platform codenamed: Knights Ferry, and + * the Intel product codenamed: Knights Corner, and are not backward + * compatible with other Intel products. Additionally, Intel will NOT + * support the codes or instruction set in future products. + * + * Intel MIC Card driver. + * + */ +#include +#include +#include + +#include "../common/mic_device.h" +#include "mic_device.h" +#include "mic_x100.h" + +static const char mic_driver_name[] = "mic"; + +static struct mic_driver g_drv; + +/** + * mic_read_spad - read from the scratchpad register + * @mdev: pointer to mic_device instance + * @idx: index to scratchpad register, 0 based + * + * This function allows reading of the 32bit scratchpad register. + * + * RETURNS: An appropriate -ERRNO error value on error, or zero for success. + */ +u32 mic_read_spad(struct mic_device *mdev, unsigned int idx) +{ + return mic_mmio_read(&mdev->mmio, + MIC_X100_SBOX_BASE_ADDRESS + + MIC_X100_SBOX_SPAD0 + idx * 4); +} + +/** + * __mic_send_intr - Send interrupt to Host. + * @mdev: pointer to mic_device instance + * @doorbell: Doorbell number. + */ +void mic_send_intr(struct mic_device *mdev, int doorbell) +{ + struct mic_mw *mw = &mdev->mmio; + + if (doorbell > MIC_X100_MAX_DOORBELL_IDX) + return; + /* Ensure that the interrupt is ordered w.r.t previous stores. */ + wmb(); + mic_mmio_write(mw, MIC_X100_SBOX_SDBIC0_DBREQ_BIT, + MIC_X100_SBOX_BASE_ADDRESS + + (MIC_X100_SBOX_SDBIC0 + (4 * doorbell))); +} + +/** + * mic_ack_interrupt - Device specific interrupt handling. + * @mdev: pointer to mic_device instance + * + * Returns: bitmask of doorbell events triggered. + */ +u32 mic_ack_interrupt(struct mic_device *mdev) +{ + return 0; +} + +static inline int mic_get_sbox_irq(int db) +{ + return MIC_X100_IRQ_BASE + db; +} + +static inline int mic_get_rdmasr_irq(int index) +{ + return MIC_X100_RDMASR_IRQ_BASE + index; +} + +/** + * mic_hw_intr_init - Initialize h/w specific interrupt + * information. + * @mdrv: pointer to mic_driver + */ +void mic_hw_intr_init(struct mic_driver *mdrv) +{ + mdrv->intr_info.num_intr = MIC_X100_NUM_SBOX_IRQ + + MIC_X100_NUM_RDMASR_IRQ; +} + +/** + * mic_db_to_irq - Retrieve irq number corresponding to a doorbell. + * @mdrv: pointer to mic_driver + * @db: The doorbell obtained for which the irq is needed. Doorbell + * may correspond to an sbox doorbell or an rdmasr index. + * + * Returns the irq corresponding to the doorbell. + */ +int mic_db_to_irq(struct mic_driver *mdrv, int db) +{ + int rdmasr_index; + if (db < MIC_X100_NUM_SBOX_IRQ) { + return mic_get_sbox_irq(db); + } else { + rdmasr_index = db - MIC_X100_NUM_SBOX_IRQ + + MIC_X100_RDMASR_IRQ_BASE; + return mic_get_rdmasr_irq(rdmasr_index); + } +} + +/* + * mic_card_map - Allocate virtual address for a remote memory region. + * @mdev: pointer to mic_device instance. + * @addr: Remote DMA address. + * @size: Size of the region. + * + * Returns: Virtual address backing the remote memory region. + */ +void __iomem * +mic_card_map(struct mic_device *mdev, dma_addr_t addr, size_t size) +{ + return ioremap(addr, size); +} + +/* + * mic_card_unmap - Unmap the virtual address for a remote memory region. + * @mdev: pointer to mic_device instance. + * @addr: Virtual address for remote memory region. + * + * Returns: None. + */ +void mic_card_unmap(struct mic_device *mdev, void __iomem *addr) +{ + iounmap(addr); +} + +static int __init mic_probe(struct platform_device *pdev) +{ + struct mic_driver *mdrv = &g_drv; + struct mic_device *mdev = &mdrv->mdev; + int rc = 0; + + mdrv->dev = &pdev->dev; + snprintf(mdrv->name, sizeof(mic_driver_name), mic_driver_name); + + mdev->mmio.pa = MIC_X100_MMIO_BASE; + mdev->mmio.len = MIC_X100_MMIO_LEN; + mdev->mmio.va = ioremap(MIC_X100_MMIO_BASE, MIC_X100_MMIO_LEN); + if (!mdev->mmio.va) { + dev_err(&pdev->dev, "Cannot remap MMIO BAR\n"); + rc = -EIO; + goto done; + } + mic_hw_intr_init(mdrv); + rc = mic_driver_init(mdrv); + if (rc) { + dev_err(&pdev->dev, "mic_driver_init failed rc %d\n", rc); + goto iounmap; + } +done: + return rc; +iounmap: + iounmap(mdev->mmio.va); + return rc; +} + +static int mic_remove(struct platform_device *pdev) +{ + struct mic_driver *mdrv = &g_drv; + struct mic_device *mdev = &mdrv->mdev; + + mic_driver_uninit(mdrv); + iounmap(mdev->mmio.va); + return 0; +} + +static void mic_platform_shutdown(struct platform_device *pdev) +{ + mic_remove(pdev); +} + +static struct platform_device mic_platform_dev = { + .name = mic_driver_name, + .id = 0, + .num_resources = 0, +}; + +static struct platform_driver __refdata mic_platform_driver = { + .probe = mic_probe, + .remove = mic_remove, + .shutdown = mic_platform_shutdown, + .driver = { + .name = mic_driver_name, + .owner = THIS_MODULE, + }, +}; + +static int __init mic_init(void) +{ + int ret; + struct cpuinfo_x86 *c = &cpu_data(0); + + if (!(c->x86 == 11 && c->x86_model == 1)) { + ret = -ENODEV; + pr_err("%s not running on X100 ret %d\n", __func__, ret); + goto done; + } + + mic_init_card_debugfs(); + ret = platform_device_register(&mic_platform_dev); + if (ret) { + pr_err("platform_device_register ret %d\n", ret); + goto cleanup_debugfs; + } + ret = platform_driver_register(&mic_platform_driver); + if (ret) { + pr_err("platform_driver_register ret %d\n", ret); + goto device_unregister; + } + return ret; + +device_unregister: + platform_device_unregister(&mic_platform_dev); +cleanup_debugfs: + mic_exit_card_debugfs(); +done: + return ret; +} + +static void __exit mic_exit(void) +{ + platform_driver_unregister(&mic_platform_driver); + platform_device_unregister(&mic_platform_dev); + mic_exit_card_debugfs(); +} + +module_init(mic_init); +module_exit(mic_exit); + +MODULE_AUTHOR("Intel Corporation"); +MODULE_DESCRIPTION("Intel(R) MIC X100 Card driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/misc/mic/card/mic_x100.h b/drivers/misc/mic/card/mic_x100.h new file mode 100644 index 0000000..d66ea55 --- /dev/null +++ b/drivers/misc/mic/card/mic_x100.h @@ -0,0 +1,48 @@ +/* + * Intel MIC Platform Software Stack (MPSS) + * + * Copyright(c) 2013 Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, version 2, as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * The full GNU General Public License is included in this distribution in + * the file called "COPYING". + * + * Disclaimer: The codes contained in these modules may be specific to + * the Intel Software Development Platform codenamed: Knights Ferry, and + * the Intel product codenamed: Knights Corner, and are not backward + * compatible with other Intel products. Additionally, Intel will NOT + * support the codes or instruction set in future products. + * + * Intel MIC Card driver. + * + */ +#ifndef _MIC_X100_CARD_H_ +#define _MIC_X100_CARD_H_ + +#define MIC_X100_MMIO_BASE 0x08007C0000ULL +#define MIC_X100_MMIO_LEN 0x00020000ULL +#define MIC_X100_SBOX_BASE_ADDRESS 0x00010000ULL + +#define MIC_X100_SBOX_SPAD0 0x0000AB20 +#define MIC_X100_SBOX_SDBIC0 0x0000CC90 +#define MIC_X100_SBOX_SDBIC0_DBREQ_BIT 0x80000000 +#define MIC_X100_SBOX_RDMASR0 0x0000B180 + +#define MIC_X100_MAX_DOORBELL_IDX 8 + +#define MIC_X100_NUM_SBOX_IRQ 8 +#define MIC_X100_NUM_RDMASR_IRQ 8 +#define MIC_X100_SBOX_IRQ_BASE 0 +#define MIC_X100_RDMASR_IRQ_BASE 17 + +#define MIC_X100_IRQ_BASE 26 + +#endif -- cgit v0.10.2 From f69bcbf3b4c4b333dcd7a48eaf868bf0c88edab5 Mon Sep 17 00:00:00 2001 From: Ashutosh Dixit Date: Thu, 5 Sep 2013 16:42:18 -0700 Subject: Intel MIC Host Driver Changes for Virtio Devices. This patch introduces the host "Virtio over PCIe" interface for Intel MIC. It allows creating user space backends on the host and instantiating virtio devices for them on the Intel MIC card. It uses the existing VRINGH infrastructure in the kernel to access virtio rings from the host. A character device per MIC is exposed with IOCTL, mmap and poll callbacks. This allows the user space backend to: (a) add/remove a virtio device via a device page. (b) map (R/O) virtio rings and device page to user space. (c) poll for availability of data. (d) copy a descriptor or entire descriptor chain to/from the card. (e) modify virtio configuration. (f) handle virtio device reset. The buffers are copied over using CPU copies for this initial patch and host initiated MIC DMA support is planned for future patches. The avail and desc virtio rings are in host memory and the used ring is in card memory to maximize writes across PCIe for performance. Co-author: Sudeep Dutt Signed-off-by: Ashutosh Dixit Signed-off-by: Caz Yokoyama Signed-off-by: Dasaratharaman Chandramouli Signed-off-by: Nikhil Rao Signed-off-by: Harshavardhan R Kharche Signed-off-by: Sudeep Dutt Acked-by: Yaozu (Eddie) Dong Reviewed-by: Peter P Waskiewicz Jr Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/misc/mic/Kconfig b/drivers/misc/mic/Kconfig index 279a2e6..01f1a4a 100644 --- a/drivers/misc/mic/Kconfig +++ b/drivers/misc/mic/Kconfig @@ -3,6 +3,7 @@ comment "Intel MIC Host Driver" config INTEL_MIC_HOST tristate "Intel MIC Host Driver" depends on 64BIT && PCI + select VHOST_RING default N help This enables Host Driver support for the Intel Many Integrated diff --git a/drivers/misc/mic/common/mic_device.h b/drivers/misc/mic/common/mic_device.h index 6440e9d..01eb74f 100644 --- a/drivers/misc/mic/common/mic_device.h +++ b/drivers/misc/mic/common/mic_device.h @@ -41,4 +41,11 @@ struct mic_mw { #define MIC_DPLO_SPAD 14 #define MIC_DPHI_SPAD 15 +/* + * These values are supposed to be in the config_change field of the + * device page when the host sends a config change interrupt to the card. + */ +#define MIC_VIRTIO_PARAM_DEV_REMOVE 0x1 +#define MIC_VIRTIO_PARAM_CONFIG_CHANGED 0x2 + #endif diff --git a/drivers/misc/mic/host/Makefile b/drivers/misc/mic/host/Makefile index a375dd3..c2197f9 100644 --- a/drivers/misc/mic/host/Makefile +++ b/drivers/misc/mic/host/Makefile @@ -10,3 +10,5 @@ mic_host-objs += mic_smpt.o mic_host-objs += mic_intr.o mic_host-objs += mic_boot.o mic_host-objs += mic_debugfs.o +mic_host-objs += mic_fops.o +mic_host-objs += mic_virtio.o diff --git a/drivers/misc/mic/host/mic_boot.c b/drivers/misc/mic/host/mic_boot.c index 936fc58..fd9ff6d 100644 --- a/drivers/misc/mic/host/mic_boot.c +++ b/drivers/misc/mic/host/mic_boot.c @@ -20,12 +20,12 @@ */ #include #include -#include #include #include "../common/mic_device.h" #include "mic_device.h" #include "mic_smpt.h" +#include "mic_virtio.h" /** * mic_reset - Reset the MIC device. @@ -117,6 +117,7 @@ void mic_stop(struct mic_device *mdev, bool force) { mutex_lock(&mdev->mic_mutex); if (MIC_OFFLINE != mdev->state || force) { + mic_virtio_reset_devices(mdev); mic_bootparam_init(mdev); mic_reset(mdev); if (MIC_RESET_FAILED == mdev->state) diff --git a/drivers/misc/mic/host/mic_debugfs.c b/drivers/misc/mic/host/mic_debugfs.c index 78541d4..e22fb7b 100644 --- a/drivers/misc/mic/host/mic_debugfs.c +++ b/drivers/misc/mic/host/mic_debugfs.c @@ -26,6 +26,7 @@ #include "../common/mic_device.h" #include "mic_device.h" #include "mic_smpt.h" +#include "mic_virtio.h" /* Debugfs parent dir */ static struct dentry *mic_dbg; @@ -193,7 +194,13 @@ static const struct file_operations post_code_ops = { static int mic_dp_show(struct seq_file *s, void *pos) { struct mic_device *mdev = s->private; + struct mic_device_desc *d; + struct mic_device_ctrl *dc; + struct mic_vqconfig *vqconfig; + __u32 *features; + __u8 *config; struct mic_bootparam *bootparam = mdev->dp; + int i, j; seq_printf(s, "Bootparam: magic 0x%x\n", bootparam->magic); @@ -208,6 +215,53 @@ static int mic_dp_show(struct seq_file *s, void *pos) seq_printf(s, "Bootparam: shutdown_card %d\n", bootparam->shutdown_card); + for (i = sizeof(*bootparam); i < MIC_DP_SIZE; + i += mic_total_desc_size(d)) { + d = mdev->dp + i; + dc = (void *)d + mic_aligned_desc_size(d); + + /* end of list */ + if (d->type == 0) + break; + + if (d->type == -1) + continue; + + seq_printf(s, "Type %d ", d->type); + seq_printf(s, "Num VQ %d ", d->num_vq); + seq_printf(s, "Feature Len %d\n", d->feature_len); + seq_printf(s, "Config Len %d ", d->config_len); + seq_printf(s, "Shutdown Status %d\n", d->status); + + for (j = 0; j < d->num_vq; j++) { + vqconfig = mic_vq_config(d) + j; + seq_printf(s, "vqconfig[%d]: ", j); + seq_printf(s, "address 0x%llx ", vqconfig->address); + seq_printf(s, "num %d ", vqconfig->num); + seq_printf(s, "used address 0x%llx\n", + vqconfig->used_address); + } + + features = (__u32 *) mic_vq_features(d); + seq_printf(s, "Features: Host 0x%x ", features[0]); + seq_printf(s, "Guest 0x%x\n", features[1]); + + config = mic_vq_configspace(d); + for (j = 0; j < d->config_len; j++) + seq_printf(s, "config[%d]=%d\n", j, config[j]); + + seq_puts(s, "Device control:\n"); + seq_printf(s, "Config Change %d ", dc->config_change); + seq_printf(s, "Vdev reset %d\n", dc->vdev_reset); + seq_printf(s, "Guest Ack %d ", dc->guest_ack); + seq_printf(s, "Host ack %d\n", dc->host_ack); + seq_printf(s, "Used address updated %d ", + dc->used_address_updated); + seq_printf(s, "Vdev 0x%llx\n", dc->vdev); + seq_printf(s, "c2h doorbell %d ", dc->c2h_vdev_db); + seq_printf(s, "h2c doorbell %d\n", dc->h2c_vdev_db); + } + return 0; } @@ -229,6 +283,89 @@ static const struct file_operations dp_ops = { .release = mic_dp_debug_release }; +static int mic_vdev_info_show(struct seq_file *s, void *unused) +{ + struct mic_device *mdev = s->private; + struct list_head *pos, *tmp; + struct mic_vdev *mvdev; + int i, j; + + mutex_lock(&mdev->mic_mutex); + list_for_each_safe(pos, tmp, &mdev->vdev_list) { + mvdev = list_entry(pos, struct mic_vdev, list); + seq_printf(s, "VDEV type %d state %s in %ld out %ld\n", + mvdev->virtio_id, + mic_vdevup(mvdev) ? "UP" : "DOWN", + mvdev->in_bytes, + mvdev->out_bytes); + for (i = 0; i < MIC_MAX_VRINGS; i++) { + struct vring_desc *desc; + struct vring_avail *avail; + struct vring_used *used; + struct mic_vringh *mvr = &mvdev->mvr[i]; + struct vringh *vrh = &mvr->vrh; + int num = vrh->vring.num; + if (!num) + continue; + desc = vrh->vring.desc; + seq_printf(s, "vring i %d avail_idx %d", + i, mvr->vring.info->avail_idx & (num - 1)); + seq_printf(s, " vring i %d avail_idx %d\n", + i, mvr->vring.info->avail_idx); + seq_printf(s, "vrh i %d weak_barriers %d", + i, vrh->weak_barriers); + seq_printf(s, " last_avail_idx %d last_used_idx %d", + vrh->last_avail_idx, vrh->last_used_idx); + seq_printf(s, " completed %d\n", vrh->completed); + for (j = 0; j < num; j++) { + seq_printf(s, "desc[%d] addr 0x%llx len %d", + j, desc->addr, desc->len); + seq_printf(s, " flags 0x%x next %d\n", + desc->flags, + desc->next); + desc++; + } + avail = vrh->vring.avail; + seq_printf(s, "avail flags 0x%x idx %d\n", + avail->flags, avail->idx & (num - 1)); + seq_printf(s, "avail flags 0x%x idx %d\n", + avail->flags, avail->idx); + for (j = 0; j < num; j++) + seq_printf(s, "avail ring[%d] %d\n", + j, avail->ring[j]); + used = vrh->vring.used; + seq_printf(s, "used flags 0x%x idx %d\n", + used->flags, used->idx & (num - 1)); + seq_printf(s, "used flags 0x%x idx %d\n", + used->flags, used->idx); + for (j = 0; j < num; j++) + seq_printf(s, "used ring[%d] id %d len %d\n", + j, used->ring[j].id, used->ring[j].len); + } + } + mutex_unlock(&mdev->mic_mutex); + + return 0; +} + +static int mic_vdev_info_debug_open(struct inode *inode, struct file *file) +{ + return single_open(file, mic_vdev_info_show, inode->i_private); +} + +static int mic_vdev_info_debug_release(struct inode *inode, struct file *file) +{ + return single_release(inode, file); +} + +static const struct file_operations vdev_info_ops = { + .owner = THIS_MODULE, + .open = mic_vdev_info_debug_open, + .read = seq_read, + .llseek = seq_lseek, + .release = mic_vdev_info_debug_release +}; + static int mic_msi_irq_info_show(struct seq_file *s, void *pos) { struct mic_device *mdev = s->private; @@ -321,6 +458,9 @@ void mic_create_debug_dir(struct mic_device *mdev) debugfs_create_file("dp", 0444, mdev->dbg_dir, mdev, &dp_ops); + debugfs_create_file("vdev_info", 0444, mdev->dbg_dir, + mdev, &vdev_info_ops); + debugfs_create_file("msi_irq_info", 0444, mdev->dbg_dir, mdev, &msi_irq_info_ops); } diff --git a/drivers/misc/mic/host/mic_device.h b/drivers/misc/mic/host/mic_device.h index 50b8b88..dcba2a5 100644 --- a/drivers/misc/mic/host/mic_device.h +++ b/drivers/misc/mic/host/mic_device.h @@ -21,6 +21,7 @@ #ifndef _MIC_DEVICE_H_ #define _MIC_DEVICE_H_ +#include #include #include "mic_intr.h" @@ -80,6 +81,8 @@ enum mic_stepping { * @dp_dma_addr: virtio device page DMA address. * @shutdown_db: shutdown doorbell. * @shutdown_cookie: shutdown cookie. + * @cdev: Character device for MIC. + * @vdev_list: list of virtio devices. */ struct mic_device { struct mic_mw mmio; @@ -113,6 +116,8 @@ struct mic_device { dma_addr_t dp_dma_addr; int shutdown_db; struct mic_irq *shutdown_cookie; + struct cdev cdev; + struct list_head vdev_list; }; /** diff --git a/drivers/misc/mic/host/mic_fops.c b/drivers/misc/mic/host/mic_fops.c new file mode 100644 index 0000000..661469a --- /dev/null +++ b/drivers/misc/mic/host/mic_fops.c @@ -0,0 +1,221 @@ +/* + * Intel MIC Platform Software Stack (MPSS) + * + * Copyright(c) 2013 Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, version 2, as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * The full GNU General Public License is included in this distribution in + * the file called "COPYING". + * + * Intel MIC Host driver. + * + */ +#include + +#include +#include "../common/mic_device.h" +#include "mic_device.h" +#include "mic_fops.h" +#include "mic_virtio.h" + +int mic_open(struct inode *inode, struct file *f) +{ + struct mic_vdev *mvdev; + struct mic_device *mdev = container_of(inode->i_cdev, + struct mic_device, cdev); + + mvdev = kzalloc(sizeof(*mvdev), GFP_KERNEL); + if (!mvdev) + return -ENOMEM; + + init_waitqueue_head(&mvdev->waitq); + INIT_LIST_HEAD(&mvdev->list); + mvdev->mdev = mdev; + mvdev->virtio_id = -1; + + f->private_data = mvdev; + return 0; +} + +int mic_release(struct inode *inode, struct file *f) +{ + struct mic_vdev *mvdev = (struct mic_vdev *)f->private_data; + + if (-1 != mvdev->virtio_id) + mic_virtio_del_device(mvdev); + f->private_data = NULL; + kfree(mvdev); + return 0; +} + +long mic_ioctl(struct file *f, unsigned int cmd, unsigned long arg) +{ + struct mic_vdev *mvdev = (struct mic_vdev *)f->private_data; + void __user *argp = (void __user *)arg; + int ret; + + switch (cmd) { + case MIC_VIRTIO_ADD_DEVICE: + { + ret = mic_virtio_add_device(mvdev, argp); + if (ret < 0) { + dev_err(mic_dev(mvdev), + "%s %d errno ret %d\n", + __func__, __LINE__, ret); + return ret; + } + break; + } + case MIC_VIRTIO_COPY_DESC: + { + struct mic_copy_desc copy; + + ret = mic_vdev_inited(mvdev); + if (ret) + return ret; + + if (copy_from_user(©, argp, sizeof(copy))) + return -EFAULT; + + dev_dbg(mic_dev(mvdev), + "%s %d === iovcnt 0x%x vr_idx 0x%x update_used %d\n", + __func__, __LINE__, copy.iovcnt, copy.vr_idx, + copy.update_used); + + ret = mic_virtio_copy_desc(mvdev, ©); + if (ret < 0) { + dev_err(mic_dev(mvdev), + "%s %d errno ret %d\n", + __func__, __LINE__, ret); + return ret; + } + if (copy_to_user( + &((struct mic_copy_desc __user *)argp)->out_len, + ©.out_len, sizeof(copy.out_len))) { + dev_err(mic_dev(mvdev), "%s %d errno ret %d\n", + __func__, __LINE__, -EFAULT); + return -EFAULT; + } + break; + } + case MIC_VIRTIO_CONFIG_CHANGE: + { + ret = mic_vdev_inited(mvdev); + if (ret) + return ret; + + ret = mic_virtio_config_change(mvdev, argp); + if (ret < 0) { + dev_err(mic_dev(mvdev), + "%s %d errno ret %d\n", + __func__, __LINE__, ret); + return ret; + } + break; + } + default: + return -ENOIOCTLCMD; + }; + return 0; +} + +/* + * We return POLLIN | POLLOUT from poll when new buffers are enqueued, and + * not when previously enqueued buffers may be available. This means that + * in the card->host (TX) path, when userspace is unblocked by poll it + * must drain all available descriptors or it can stall. + */ +unsigned int mic_poll(struct file *f, poll_table *wait) +{ + struct mic_vdev *mvdev = (struct mic_vdev *)f->private_data; + int mask = 0; + + poll_wait(f, &mvdev->waitq, wait); + + if (mic_vdev_inited(mvdev)) + mask = POLLERR; + else if (mvdev->poll_wake) { + mvdev->poll_wake = 0; + mask = POLLIN | POLLOUT; + } + + return mask; +} + +static inline int +mic_query_offset(struct mic_vdev *mvdev, unsigned long offset, + unsigned long *size, unsigned long *pa) +{ + struct mic_device *mdev = mvdev->mdev; + unsigned long start = MIC_DP_SIZE; + int i; + + /* + * MMAP interface is as follows: + * offset region + * 0x0 virtio device_page + * 0x1000 first vring + * 0x1000 + size of 1st vring second vring + * .... + */ + if (!offset) { + *pa = virt_to_phys(mdev->dp); + *size = MIC_DP_SIZE; + return 0; + } + + for (i = 0; i < mvdev->dd->num_vq; i++) { + struct mic_vringh *mvr = &mvdev->mvr[i]; + if (offset == start) { + *pa = virt_to_phys(mvr->vring.va); + *size = mvr->vring.len; + return 0; + } + start += mvr->vring.len; + } + return -1; +} + +/* + * Maps the device page and virtio rings to user space for readonly access. + */ +int +mic_mmap(struct file *f, struct vm_area_struct *vma) +{ + struct mic_vdev *mvdev = (struct mic_vdev *)f->private_data; + unsigned long offset = vma->vm_pgoff << PAGE_SHIFT; + unsigned long pa, size = vma->vm_end - vma->vm_start, size_rem = size; + int i, err; + + err = mic_vdev_inited(mvdev); + if (err) + return err; + + if (vma->vm_flags & VM_WRITE) + return -EACCES; + + while (size_rem) { + i = mic_query_offset(mvdev, offset, &size, &pa); + if (i < 0) + return -EINVAL; + err = remap_pfn_range(vma, vma->vm_start + offset, + pa >> PAGE_SHIFT, size, vma->vm_page_prot); + if (err) + return err; + dev_dbg(mic_dev(mvdev), + "%s %d type %d size 0x%lx off 0x%lx pa 0x%lx vma 0x%lx\n", + __func__, __LINE__, mvdev->virtio_id, size, offset, + pa, vma->vm_start + offset); + size_rem -= size; + offset += size; + } + return 0; +} diff --git a/drivers/misc/mic/host/mic_fops.h b/drivers/misc/mic/host/mic_fops.h new file mode 100644 index 0000000..dc3893d --- /dev/null +++ b/drivers/misc/mic/host/mic_fops.h @@ -0,0 +1,32 @@ +/* + * Intel MIC Platform Software Stack (MPSS) + * + * Copyright(c) 2013 Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, version 2, as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * The full GNU General Public License is included in this distribution in + * the file called "COPYING". + * + * Intel MIC Host driver. + * + */ +#ifndef _MIC_FOPS_H_ +#define _MIC_FOPS_H_ + +int mic_open(struct inode *inode, struct file *filp); +int mic_release(struct inode *inode, struct file *filp); +ssize_t mic_read(struct file *filp, char __user *buf, + size_t count, loff_t *pos); +long mic_ioctl(struct file *filp, unsigned int cmd, unsigned long arg); +int mic_mmap(struct file *f, struct vm_area_struct *vma); +unsigned int mic_poll(struct file *f, poll_table *wait); + +#endif diff --git a/drivers/misc/mic/host/mic_main.c b/drivers/misc/mic/host/mic_main.c index 998a20a..a8965d4 100644 --- a/drivers/misc/mic/host/mic_main.c +++ b/drivers/misc/mic/host/mic_main.c @@ -25,12 +25,15 @@ #include #include #include +#include #include #include "../common/mic_device.h" #include "mic_device.h" #include "mic_x100.h" #include "mic_smpt.h" +#include "mic_fops.h" +#include "mic_virtio.h" static const char mic_driver_name[] = "mic"; @@ -64,6 +67,15 @@ static struct class *g_mic_class; /* Base device node number for MIC devices */ static dev_t g_mic_devno; +static const struct file_operations mic_fops = { + .open = mic_open, + .release = mic_release, + .unlocked_ioctl = mic_ioctl, + .poll = mic_poll, + .mmap = mic_mmap, + .owner = THIS_MODULE, +}; + /* Initialize the device page */ static int mic_dp_init(struct mic_device *mdev) { @@ -193,6 +205,7 @@ mic_device_init(struct mic_device *mdev, struct pci_dev *pdev) mdev->irq_info.next_avail_src = 0; INIT_WORK(&mdev->reset_trigger_work, mic_reset_trigger_work); INIT_WORK(&mdev->shutdown_work, mic_shutdown_work); + INIT_LIST_HEAD(&mdev->vdev_list); } /** @@ -330,7 +343,19 @@ static int mic_probe(struct pci_dev *pdev, mic_bootparam_init(mdev); mic_create_debug_dir(mdev); + cdev_init(&mdev->cdev, &mic_fops); + mdev->cdev.owner = THIS_MODULE; + rc = cdev_add(&mdev->cdev, MKDEV(MAJOR(g_mic_devno), mdev->id), 1); + if (rc) { + dev_err(&pdev->dev, "cdev_add err id %d rc %d\n", mdev->id, rc); + goto cleanup_debug_dir; + } return 0; +cleanup_debug_dir: + mic_delete_debug_dir(mdev); + mutex_lock(&mdev->mic_mutex); + mic_free_irq(mdev, mdev->shutdown_cookie, mdev); + mutex_unlock(&mdev->mic_mutex); dp_uninit: mic_dp_uninit(mdev); sysfs_put: @@ -375,6 +400,7 @@ static void mic_remove(struct pci_dev *pdev) return; mic_stop(mdev, false); + cdev_del(&mdev->cdev); mic_delete_debug_dir(mdev); mutex_lock(&mdev->mic_mutex); mic_free_irq(mdev, mdev->shutdown_cookie, mdev); diff --git a/drivers/misc/mic/host/mic_virtio.c b/drivers/misc/mic/host/mic_virtio.c new file mode 100644 index 0000000..be2a1f0 --- /dev/null +++ b/drivers/misc/mic/host/mic_virtio.c @@ -0,0 +1,703 @@ +/* + * Intel MIC Platform Software Stack (MPSS) + * + * Copyright(c) 2013 Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, version 2, as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * The full GNU General Public License is included in this distribution in + * the file called "COPYING". + * + * Intel MIC Host driver. + * + */ +#include +#include +#include + +#include +#include "../common/mic_device.h" +#include "mic_device.h" +#include "mic_smpt.h" +#include "mic_virtio.h" + +/* + * Initiates the copies across the PCIe bus from card memory to + * a user space buffer. + */ +static int mic_virtio_copy_to_user(struct mic_vdev *mvdev, + void __user *ubuf, size_t len, u64 addr) +{ + int err; + void __iomem *dbuf = mvdev->mdev->aper.va + addr; + /* + * We are copying from IO below an should ideally use something + * like copy_to_user_fromio(..) if it existed. + */ + if (copy_to_user(ubuf, dbuf, len)) { + err = -EFAULT; + dev_err(mic_dev(mvdev), "%s %d err %d\n", + __func__, __LINE__, err); + goto err; + } + mvdev->in_bytes += len; + err = 0; +err: + return err; +} + +/* + * Initiates copies across the PCIe bus from a user space + * buffer to card memory. + */ +static int mic_virtio_copy_from_user(struct mic_vdev *mvdev, + void __user *ubuf, size_t len, u64 addr) +{ + int err; + void __iomem *dbuf = mvdev->mdev->aper.va + addr; + /* + * We are copying to IO below and should ideally use something + * like copy_from_user_toio(..) if it existed. + */ + if (copy_from_user(dbuf, ubuf, len)) { + err = -EFAULT; + dev_err(mic_dev(mvdev), "%s %d err %d\n", + __func__, __LINE__, err); + goto err; + } + mvdev->out_bytes += len; + err = 0; +err: + return err; +} + +#define MIC_VRINGH_READ true + +/* The function to call to notify the card about added buffers */ +static void mic_notify(struct vringh *vrh) +{ + struct mic_vringh *mvrh = container_of(vrh, struct mic_vringh, vrh); + struct mic_vdev *mvdev = mvrh->mvdev; + s8 db = mvdev->dc->h2c_vdev_db; + + if (db != -1) + mvdev->mdev->ops->send_intr(mvdev->mdev, db); +} + +/* Determine the total number of bytes consumed in a VRINGH KIOV */ +static inline u32 mic_vringh_iov_consumed(struct vringh_kiov *iov) +{ + int i; + u32 total = iov->consumed; + + for (i = 0; i < iov->i; i++) + total += iov->iov[i].iov_len; + return total; +} + +/* + * Traverse the VRINGH KIOV and issue the APIs to trigger the copies. + * This API is heavily based on the vringh_iov_xfer(..) implementation + * in vringh.c. The reason we cannot reuse vringh_iov_pull_kern(..) + * and vringh_iov_push_kern(..) directly is because there is no + * way to override the VRINGH xfer(..) routines as of v3.10. + */ +static int mic_vringh_copy(struct mic_vdev *mvdev, struct vringh_kiov *iov, + void __user *ubuf, size_t len, bool read, size_t *out_len) +{ + int ret = 0; + size_t partlen, tot_len = 0; + + while (len && iov->i < iov->used) { + partlen = min(iov->iov[iov->i].iov_len, len); + if (read) + ret = mic_virtio_copy_to_user(mvdev, + ubuf, partlen, + (u64)iov->iov[iov->i].iov_base); + else + ret = mic_virtio_copy_from_user(mvdev, + ubuf, partlen, + (u64)iov->iov[iov->i].iov_base); + if (ret) { + dev_err(mic_dev(mvdev), "%s %d err %d\n", + __func__, __LINE__, ret); + break; + } + len -= partlen; + ubuf += partlen; + tot_len += partlen; + iov->consumed += partlen; + iov->iov[iov->i].iov_len -= partlen; + iov->iov[iov->i].iov_base += partlen; + if (!iov->iov[iov->i].iov_len) { + /* Fix up old iov element then increment. */ + iov->iov[iov->i].iov_len = iov->consumed; + iov->iov[iov->i].iov_base -= iov->consumed; + + iov->consumed = 0; + iov->i++; + } + } + *out_len = tot_len; + return ret; +} + +/* + * Use the standard VRINGH infrastructure in the kernel to fetch new + * descriptors, initiate the copies and update the used ring. + */ +static int _mic_virtio_copy(struct mic_vdev *mvdev, + struct mic_copy_desc *copy) +{ + int ret = 0, iovcnt = copy->iovcnt; + struct iovec iov; + struct iovec __user *u_iov = copy->iov; + void __user *ubuf = NULL; + struct mic_vringh *mvr = &mvdev->mvr[copy->vr_idx]; + struct vringh_kiov *riov = &mvr->riov; + struct vringh_kiov *wiov = &mvr->wiov; + struct vringh *vrh = &mvr->vrh; + u16 *head = &mvr->head; + struct mic_vring *vr = &mvr->vring; + size_t len = 0, out_len; + + copy->out_len = 0; + /* Fetch a new IOVEC if all previous elements have been processed */ + if (riov->i == riov->used && wiov->i == wiov->used) { + ret = vringh_getdesc_kern(vrh, riov, wiov, + head, GFP_KERNEL); + /* Check if there are available descriptors */ + if (ret <= 0) + return ret; + } + while (iovcnt) { + if (!len) { + /* Copy over a new iovec from user space. */ + ret = copy_from_user(&iov, u_iov, sizeof(*u_iov)); + if (ret) { + ret = -EINVAL; + dev_err(mic_dev(mvdev), "%s %d err %d\n", + __func__, __LINE__, ret); + break; + } + len = iov.iov_len; + ubuf = iov.iov_base; + } + /* Issue all the read descriptors first */ + ret = mic_vringh_copy(mvdev, riov, ubuf, len, + MIC_VRINGH_READ, &out_len); + if (ret) { + dev_err(mic_dev(mvdev), "%s %d err %d\n", + __func__, __LINE__, ret); + break; + } + len -= out_len; + ubuf += out_len; + copy->out_len += out_len; + /* Issue the write descriptors next */ + ret = mic_vringh_copy(mvdev, wiov, ubuf, len, + !MIC_VRINGH_READ, &out_len); + if (ret) { + dev_err(mic_dev(mvdev), "%s %d err %d\n", + __func__, __LINE__, ret); + break; + } + len -= out_len; + ubuf += out_len; + copy->out_len += out_len; + if (!len) { + /* One user space iovec is now completed */ + iovcnt--; + u_iov++; + } + /* Exit loop if all elements in KIOVs have been processed. */ + if (riov->i == riov->used && wiov->i == wiov->used) + break; + } + /* + * Update the used ring if a descriptor was available and some data was + * copied in/out and the user asked for a used ring update. + */ + if (*head != USHRT_MAX && copy->out_len && + copy->update_used) { + u32 total = 0; + + /* Determine the total data consumed */ + total += mic_vringh_iov_consumed(riov); + total += mic_vringh_iov_consumed(wiov); + vringh_complete_kern(vrh, *head, total); + *head = USHRT_MAX; + if (vringh_need_notify_kern(vrh) > 0) + vringh_notify(vrh); + vringh_kiov_cleanup(riov); + vringh_kiov_cleanup(wiov); + /* Update avail idx for user space */ + vr->info->avail_idx = vrh->last_avail_idx; + } + return ret; +} + +static inline int mic_verify_copy_args(struct mic_vdev *mvdev, + struct mic_copy_desc *copy) +{ + if (copy->vr_idx >= mvdev->dd->num_vq) { + dev_err(mic_dev(mvdev), "%s %d err %d\n", + __func__, __LINE__, -EINVAL); + return -EINVAL; + } + return 0; +} + +/* Copy a specified number of virtio descriptors in a chain */ +int mic_virtio_copy_desc(struct mic_vdev *mvdev, + struct mic_copy_desc *copy) +{ + int err; + struct mic_vringh *mvr = &mvdev->mvr[copy->vr_idx]; + + err = mic_verify_copy_args(mvdev, copy); + if (err) + return err; + + mutex_lock(&mvr->vr_mutex); + if (!mic_vdevup(mvdev)) { + err = -ENODEV; + dev_err(mic_dev(mvdev), "%s %d err %d\n", + __func__, __LINE__, err); + goto err; + } + err = _mic_virtio_copy(mvdev, copy); + if (err) { + dev_err(mic_dev(mvdev), "%s %d err %d\n", + __func__, __LINE__, err); + } +err: + mutex_unlock(&mvr->vr_mutex); + return err; +} + +static void mic_virtio_init_post(struct mic_vdev *mvdev) +{ + struct mic_vqconfig *vqconfig = mic_vq_config(mvdev->dd); + int i; + + for (i = 0; i < mvdev->dd->num_vq; i++) { + if (!le64_to_cpu(vqconfig[i].used_address)) { + dev_warn(mic_dev(mvdev), "used_address zero??\n"); + continue; + } + mvdev->mvr[i].vrh.vring.used = + mvdev->mdev->aper.va + + le64_to_cpu(vqconfig[i].used_address); + } + + mvdev->dc->used_address_updated = 0; + + dev_dbg(mic_dev(mvdev), "%s: device type %d LINKUP\n", + __func__, mvdev->virtio_id); +} + +static inline void mic_virtio_device_reset(struct mic_vdev *mvdev) +{ + int i; + + dev_dbg(mic_dev(mvdev), "%s: status %d device type %d RESET\n", + __func__, mvdev->dd->status, mvdev->virtio_id); + + for (i = 0; i < mvdev->dd->num_vq; i++) + /* + * Avoid lockdep false positive. The + 1 is for the mic + * mutex which is held in the reset devices code path. + */ + mutex_lock_nested(&mvdev->mvr[i].vr_mutex, i + 1); + + /* 0 status means "reset" */ + mvdev->dd->status = 0; + mvdev->dc->vdev_reset = 0; + mvdev->dc->host_ack = 1; + + for (i = 0; i < mvdev->dd->num_vq; i++) { + struct vringh *vrh = &mvdev->mvr[i].vrh; + mvdev->mvr[i].vring.info->avail_idx = 0; + vrh->completed = 0; + vrh->last_avail_idx = 0; + vrh->last_used_idx = 0; + } + + for (i = 0; i < mvdev->dd->num_vq; i++) + mutex_unlock(&mvdev->mvr[i].vr_mutex); +} + +void mic_virtio_reset_devices(struct mic_device *mdev) +{ + struct list_head *pos, *tmp; + struct mic_vdev *mvdev; + + dev_dbg(mdev->sdev->parent, "%s\n", __func__); + + list_for_each_safe(pos, tmp, &mdev->vdev_list) { + mvdev = list_entry(pos, struct mic_vdev, list); + mic_virtio_device_reset(mvdev); + mvdev->poll_wake = 1; + wake_up(&mvdev->waitq); + } +} + +void mic_bh_handler(struct work_struct *work) +{ + struct mic_vdev *mvdev = container_of(work, struct mic_vdev, + virtio_bh_work); + + if (mvdev->dc->used_address_updated) + mic_virtio_init_post(mvdev); + + if (mvdev->dc->vdev_reset) + mic_virtio_device_reset(mvdev); + + mvdev->poll_wake = 1; + wake_up(&mvdev->waitq); +} + +static irqreturn_t mic_virtio_intr_handler(int irq, void *data) +{ + + struct mic_vdev *mvdev = data; + struct mic_device *mdev = mvdev->mdev; + + mdev->ops->ack_interrupt(mdev); + schedule_work(&mvdev->virtio_bh_work); + return IRQ_HANDLED; +} + +int mic_virtio_config_change(struct mic_vdev *mvdev, + void __user *argp) +{ + DECLARE_WAIT_QUEUE_HEAD_ONSTACK(wake); + int ret = 0, retry = 100, i; + struct mic_bootparam *bootparam = mvdev->mdev->dp; + s8 db = bootparam->h2c_config_db; + + mutex_lock(&mvdev->mdev->mic_mutex); + for (i = 0; i < mvdev->dd->num_vq; i++) + mutex_lock_nested(&mvdev->mvr[i].vr_mutex, i + 1); + + if (db == -1 || mvdev->dd->type == -1) { + ret = -EIO; + goto exit; + } + + if (copy_from_user(mic_vq_configspace(mvdev->dd), + argp, mvdev->dd->config_len)) { + dev_err(mic_dev(mvdev), "%s %d err %d\n", + __func__, __LINE__, -EFAULT); + ret = -EFAULT; + goto exit; + } + mvdev->dc->config_change = MIC_VIRTIO_PARAM_CONFIG_CHANGED; + mvdev->mdev->ops->send_intr(mvdev->mdev, db); + + for (i = retry; i--;) { + ret = wait_event_timeout(wake, + mvdev->dc->guest_ack, msecs_to_jiffies(100)); + if (ret) + break; + } + + dev_dbg(mic_dev(mvdev), + "%s %d retry: %d\n", __func__, __LINE__, retry); + mvdev->dc->config_change = 0; + mvdev->dc->guest_ack = 0; +exit: + for (i = 0; i < mvdev->dd->num_vq; i++) + mutex_unlock(&mvdev->mvr[i].vr_mutex); + mutex_unlock(&mvdev->mdev->mic_mutex); + return ret; +} + +static int mic_copy_dp_entry(struct mic_vdev *mvdev, + void __user *argp, + __u8 *type, + struct mic_device_desc **devpage) +{ + struct mic_device *mdev = mvdev->mdev; + struct mic_device_desc dd, *dd_config, *devp; + struct mic_vqconfig *vqconfig; + int ret = 0, i; + bool slot_found = false; + + if (copy_from_user(&dd, argp, sizeof(dd))) { + dev_err(mic_dev(mvdev), "%s %d err %d\n", + __func__, __LINE__, -EFAULT); + return -EFAULT; + } + + if (mic_aligned_desc_size(&dd) > MIC_MAX_DESC_BLK_SIZE + || dd.num_vq > MIC_MAX_VRINGS) { + dev_err(mic_dev(mvdev), "%s %d err %d\n", + __func__, __LINE__, -EINVAL); + return -EINVAL; + } + + dd_config = kmalloc(mic_desc_size(&dd), GFP_KERNEL); + if (dd_config == NULL) { + dev_err(mic_dev(mvdev), "%s %d err %d\n", + __func__, __LINE__, -ENOMEM); + return -ENOMEM; + } + if (copy_from_user(dd_config, argp, mic_desc_size(&dd))) { + ret = -EFAULT; + dev_err(mic_dev(mvdev), "%s %d err %d\n", + __func__, __LINE__, ret); + goto exit; + } + + vqconfig = mic_vq_config(dd_config); + for (i = 0; i < dd.num_vq; i++) { + if (le16_to_cpu(vqconfig[i].num) > MIC_MAX_VRING_ENTRIES) { + ret = -EINVAL; + dev_err(mic_dev(mvdev), "%s %d err %d\n", + __func__, __LINE__, ret); + goto exit; + } + } + + /* Find the first free device page entry */ + for (i = mic_aligned_size(struct mic_bootparam); + i < MIC_DP_SIZE - mic_total_desc_size(dd_config); + i += mic_total_desc_size(devp)) { + devp = mdev->dp + i; + if (devp->type == 0 || devp->type == -1) { + slot_found = true; + break; + } + } + if (!slot_found) { + ret = -EINVAL; + dev_err(mic_dev(mvdev), "%s %d err %d\n", + __func__, __LINE__, ret); + goto exit; + } + /* + * Save off the type before doing the memcpy. Type will be set in the + * end after completing all initialization for the new device. + */ + *type = dd_config->type; + dd_config->type = 0; + memcpy(devp, dd_config, mic_desc_size(dd_config)); + + *devpage = devp; +exit: + kfree(dd_config); + return ret; +} + +static void mic_init_device_ctrl(struct mic_vdev *mvdev, + struct mic_device_desc *devpage) +{ + struct mic_device_ctrl *dc; + + dc = mvdev->dc = (void *)devpage + mic_aligned_desc_size(devpage); + + dc->config_change = 0; + dc->guest_ack = 0; + dc->vdev_reset = 0; + dc->host_ack = 0; + dc->used_address_updated = 0; + dc->c2h_vdev_db = -1; + dc->h2c_vdev_db = -1; +} + +int mic_virtio_add_device(struct mic_vdev *mvdev, + void __user *argp) +{ + struct mic_device *mdev = mvdev->mdev; + struct mic_device_desc *dd; + struct mic_vqconfig *vqconfig; + int vr_size, i, j, ret; + u8 type; + s8 db; + char irqname[10]; + struct mic_bootparam *bootparam = mdev->dp; + u16 num; + + mutex_lock(&mdev->mic_mutex); + + ret = mic_copy_dp_entry(mvdev, argp, &type, &dd); + if (ret) { + mutex_unlock(&mdev->mic_mutex); + return ret; + } + + mic_init_device_ctrl(mvdev, dd); + + mvdev->dd = dd; + mvdev->virtio_id = type; + vqconfig = mic_vq_config(dd); + INIT_WORK(&mvdev->virtio_bh_work, mic_bh_handler); + + for (i = 0; i < dd->num_vq; i++) { + struct mic_vringh *mvr = &mvdev->mvr[i]; + struct mic_vring *vr = &mvdev->mvr[i].vring; + num = le16_to_cpu(vqconfig[i].num); + mutex_init(&mvr->vr_mutex); + vr_size = PAGE_ALIGN(vring_size(num, MIC_VIRTIO_RING_ALIGN) + + sizeof(struct _mic_vring_info)); + vr->va = (void *) + __get_free_pages(GFP_KERNEL | __GFP_ZERO, + get_order(vr_size)); + if (!vr->va) { + ret = -ENOMEM; + dev_err(mic_dev(mvdev), "%s %d err %d\n", + __func__, __LINE__, ret); + goto err; + } + vr->len = vr_size; + vr->info = vr->va + vring_size(num, MIC_VIRTIO_RING_ALIGN); + vr->info->magic = MIC_MAGIC + mvdev->virtio_id + i; + vqconfig[i].address = mic_map_single(mdev, + vr->va, vr_size); + if (mic_map_error(vqconfig[i].address)) { + free_pages((unsigned long)vr->va, + get_order(vr_size)); + ret = -ENOMEM; + dev_err(mic_dev(mvdev), "%s %d err %d\n", + __func__, __LINE__, ret); + goto err; + } + vqconfig[i].address = cpu_to_le64(vqconfig[i].address); + + vring_init(&vr->vr, num, + vr->va, MIC_VIRTIO_RING_ALIGN); + ret = vringh_init_kern(&mvr->vrh, + *(u32 *)mic_vq_features(mvdev->dd), num, false, + vr->vr.desc, vr->vr.avail, vr->vr.used); + if (ret) { + dev_err(mic_dev(mvdev), "%s %d err %d\n", + __func__, __LINE__, ret); + goto err; + } + vringh_kiov_init(&mvr->riov, NULL, 0); + vringh_kiov_init(&mvr->wiov, NULL, 0); + mvr->head = USHRT_MAX; + mvr->mvdev = mvdev; + mvr->vrh.notify = mic_notify; + dev_dbg(mdev->sdev->parent, + "%s %d index %d va %p info %p vr_size 0x%x\n", + __func__, __LINE__, i, vr->va, vr->info, vr_size); + } + + snprintf(irqname, sizeof(irqname), + "mic%dvirtio%d", mdev->id, mvdev->virtio_id); + mvdev->virtio_db = mic_next_db(mdev); + mvdev->virtio_cookie = mic_request_irq(mdev, mic_virtio_intr_handler, + irqname, mvdev, mvdev->virtio_db, MIC_INTR_DB); + if (IS_ERR(mvdev->virtio_cookie)) { + ret = PTR_ERR(mvdev->virtio_cookie); + dev_dbg(mdev->sdev->parent, "request irq failed\n"); + goto err; + } + + mvdev->dc->c2h_vdev_db = mvdev->virtio_db; + + list_add_tail(&mvdev->list, &mdev->vdev_list); + /* + * Order the type update with previous stores. This write barrier + * is paired with the corresponding read barrier before the uncached + * system memory read of the type, on the card while scanning the + * device page. + */ + smp_wmb(); + dd->type = type; + + dev_dbg(mdev->sdev->parent, "Added virtio device id %d\n", dd->type); + + db = bootparam->h2c_config_db; + if (db != -1) + mdev->ops->send_intr(mdev, db); + mutex_unlock(&mdev->mic_mutex); + return 0; +err: + vqconfig = mic_vq_config(dd); + for (j = 0; j < i; j++) { + struct mic_vringh *mvr = &mvdev->mvr[j]; + mic_unmap_single(mdev, le64_to_cpu(vqconfig[j].address), + mvr->vring.len); + free_pages((unsigned long)mvr->vring.va, + get_order(mvr->vring.len)); + } + mutex_unlock(&mdev->mic_mutex); + return ret; +} + +void mic_virtio_del_device(struct mic_vdev *mvdev) +{ + struct list_head *pos, *tmp; + struct mic_vdev *tmp_mvdev; + struct mic_device *mdev = mvdev->mdev; + DECLARE_WAIT_QUEUE_HEAD_ONSTACK(wake); + int i, ret, retry = 100; + struct mic_vqconfig *vqconfig; + struct mic_bootparam *bootparam = mdev->dp; + s8 db; + + mutex_lock(&mdev->mic_mutex); + db = bootparam->h2c_config_db; + if (db == -1) + goto skip_hot_remove; + dev_dbg(mdev->sdev->parent, + "Requesting hot remove id %d\n", mvdev->virtio_id); + mvdev->dc->config_change = MIC_VIRTIO_PARAM_DEV_REMOVE; + mdev->ops->send_intr(mdev, db); + for (i = retry; i--;) { + ret = wait_event_timeout(wake, + mvdev->dc->guest_ack, msecs_to_jiffies(100)); + if (ret) + break; + } + dev_dbg(mdev->sdev->parent, + "Device id %d config_change %d guest_ack %d\n", + mvdev->virtio_id, mvdev->dc->config_change, + mvdev->dc->guest_ack); + mvdev->dc->config_change = 0; + mvdev->dc->guest_ack = 0; +skip_hot_remove: + mic_free_irq(mdev, mvdev->virtio_cookie, mvdev); + flush_work(&mvdev->virtio_bh_work); + vqconfig = mic_vq_config(mvdev->dd); + for (i = 0; i < mvdev->dd->num_vq; i++) { + struct mic_vringh *mvr = &mvdev->mvr[i]; + vringh_kiov_cleanup(&mvr->riov); + vringh_kiov_cleanup(&mvr->wiov); + mic_unmap_single(mdev, le64_to_cpu(vqconfig[i].address), + mvr->vring.len); + free_pages((unsigned long)mvr->vring.va, + get_order(mvr->vring.len)); + } + + list_for_each_safe(pos, tmp, &mdev->vdev_list) { + tmp_mvdev = list_entry(pos, struct mic_vdev, list); + if (tmp_mvdev == mvdev) { + list_del(pos); + dev_dbg(mdev->sdev->parent, + "Removing virtio device id %d\n", + mvdev->virtio_id); + break; + } + } + /* + * Order the type update with previous stores. This write barrier + * is paired with the corresponding read barrier before the uncached + * system memory read of the type, on the card while scanning the + * device page. + */ + smp_wmb(); + mvdev->dd->type = -1; + mutex_unlock(&mdev->mic_mutex); +} diff --git a/drivers/misc/mic/host/mic_virtio.h b/drivers/misc/mic/host/mic_virtio.h new file mode 100644 index 0000000..184f3c8 --- /dev/null +++ b/drivers/misc/mic/host/mic_virtio.h @@ -0,0 +1,138 @@ +/* + * Intel MIC Platform Software Stack (MPSS) + * + * Copyright(c) 2013 Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, version 2, as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * The full GNU General Public License is included in this distribution in + * the file called "COPYING". + * + * Intel MIC Host driver. + * + */ +#ifndef MIC_VIRTIO_H +#define MIC_VIRTIO_H + +#include +#include + +/* + * Note on endianness. + * 1. Host can be both BE or LE + * 2. Guest/card is LE. Host uses le_to_cpu to access desc/avail + * rings and ioreadXX/iowriteXX to access used ring. + * 3. Device page exposed by host to guest contains LE values. Guest + * accesses these using ioreadXX/iowriteXX etc. This way in general we + * obey the virtio spec according to which guest works with native + * endianness and host is aware of guest endianness and does all + * required endianness conversion. + * 4. Data provided from user space to guest (in ADD_DEVICE and + * CONFIG_CHANGE ioctl's) is not interpreted by the driver and should be + * in guest endianness. + */ + +/** + * struct mic_vringh - Virtio ring host information. + * + * @vring: The MIC vring used for setting up user space mappings. + * @vrh: The host VRINGH used for accessing the card vrings. + * @riov: The VRINGH read kernel IOV. + * @wiov: The VRINGH write kernel IOV. + * @head: The VRINGH head index address passed to vringh_getdesc_kern(..). + * @vr_mutex: Mutex for synchronizing access to the VRING. + * @mvdev: Back pointer to MIC virtio device for vringh_notify(..). + */ +struct mic_vringh { + struct mic_vring vring; + struct vringh vrh; + struct vringh_kiov riov; + struct vringh_kiov wiov; + u16 head; + struct mutex vr_mutex; + struct mic_vdev *mvdev; +}; + +/** + * struct mic_vdev - Host information for a card Virtio device. + * + * @virtio_id - Virtio device id. + * @waitq - Waitqueue to allow ring3 apps to poll. + * @mdev - Back pointer to host MIC device. + * @poll_wake - Used for waking up threads blocked in poll. + * @out_bytes - Debug stats for number of bytes copied from host to card. + * @in_bytes - Debug stats for number of bytes copied from card to host. + * @mvr - Store per VRING data structures. + * @virtio_bh_work - Work struct used to schedule virtio bottom half handling. + * @dd - Virtio device descriptor. + * @dc - Virtio device control fields. + * @list - List of Virtio devices. + * @virtio_db - The doorbell used by the card to interrupt the host. + * @virtio_cookie - The cookie returned while requesting interrupts. + */ +struct mic_vdev { + int virtio_id; + wait_queue_head_t waitq; + struct mic_device *mdev; + int poll_wake; + unsigned long out_bytes; + unsigned long in_bytes; + struct mic_vringh mvr[MIC_MAX_VRINGS]; + struct work_struct virtio_bh_work; + struct mic_device_desc *dd; + struct mic_device_ctrl *dc; + struct list_head list; + int virtio_db; + struct mic_irq *virtio_cookie; +}; + +void mic_virtio_uninit(struct mic_device *mdev); +int mic_virtio_add_device(struct mic_vdev *mvdev, + void __user *argp); +void mic_virtio_del_device(struct mic_vdev *mvdev); +int mic_virtio_config_change(struct mic_vdev *mvdev, + void __user *argp); +int mic_virtio_copy_desc(struct mic_vdev *mvdev, + struct mic_copy_desc *request); +void mic_virtio_reset_devices(struct mic_device *mdev); +void mic_bh_handler(struct work_struct *work); + +/* Helper API to obtain the MIC PCIe device */ +static inline struct device *mic_dev(struct mic_vdev *mvdev) +{ + return mvdev->mdev->sdev->parent; +} + +/* Helper API to check if a virtio device is initialized */ +static inline int mic_vdev_inited(struct mic_vdev *mvdev) +{ + /* Device has not been created yet */ + if (!mvdev->dd || !mvdev->dd->type) { + dev_err(mic_dev(mvdev), "%s %d err %d\n", + __func__, __LINE__, -EINVAL); + return -EINVAL; + } + + /* Device has been removed/deleted */ + if (mvdev->dd->type == -1) { + dev_err(mic_dev(mvdev), "%s %d err %d\n", + __func__, __LINE__, -ENODEV); + return -ENODEV; + } + + return 0; +} + +/* Helper API to check if a virtio device is running */ +static inline bool mic_vdevup(struct mic_vdev *mvdev) +{ + return !!mvdev->dd->status; +} +#endif diff --git a/include/uapi/linux/Kbuild b/include/uapi/linux/Kbuild index d37263f..33d2b8f 100644 --- a/include/uapi/linux/Kbuild +++ b/include/uapi/linux/Kbuild @@ -242,6 +242,7 @@ header-y += mei.h header-y += mempolicy.h header-y += meye.h header-y += mic_common.h +header-y += mic_ioctl.h header-y += mii.h header-y += minix_fs.h header-y += mman.h diff --git a/include/uapi/linux/mic_common.h b/include/uapi/linux/mic_common.h index a9091e5..364ac75 100644 --- a/include/uapi/linux/mic_common.h +++ b/include/uapi/linux/mic_common.h @@ -21,7 +21,60 @@ #ifndef __MIC_COMMON_H_ #define __MIC_COMMON_H_ -#include +#include + +#ifndef __KERNEL__ +#define ALIGN(a, x) (((a) + (x) - 1) & ~((x) - 1)) +#define __aligned(x) __attribute__ ((aligned(x))) +#endif + +#define mic_aligned_size(x) ALIGN(sizeof(x), 8) + +/** + * struct mic_device_desc: Virtio device information shared between the + * virtio driver and userspace backend + * + * @type: Device type: console/network/disk etc. Type 0/-1 terminates. + * @num_vq: Number of virtqueues. + * @feature_len: Number of bytes of feature bits. Multiply by 2: one for + host features and one for guest acknowledgements. + * @config_len: Number of bytes of the config array after virtqueues. + * @status: A status byte, written by the Guest. + * @config: Start of the following variable length config. + */ +struct mic_device_desc { + __s8 type; + __u8 num_vq; + __u8 feature_len; + __u8 config_len; + __u8 status; + __u64 config[0]; +} __aligned(8); + +/** + * struct mic_device_ctrl: Per virtio device information in the device page + * used internally by the host and card side drivers. + * + * @vdev: Used for storing MIC vdev information by the guest. + * @config_change: Set to 1 by host when a config change is requested. + * @vdev_reset: Set to 1 by guest to indicate virtio device has been reset. + * @guest_ack: Set to 1 by guest to ack a command. + * @host_ack: Set to 1 by host to ack a command. + * @used_address_updated: Set to 1 by guest when the used address should be + * updated. + * @c2h_vdev_db: The doorbell number to be used by guest. Set by host. + * @h2c_vdev_db: The doorbell number to be used by host. Set by guest. + */ +struct mic_device_ctrl { + __u64 vdev; + __u8 config_change; + __u8 vdev_reset; + __u8 guest_ack; + __u8 host_ack; + __u8 used_address_updated; + __s8 c2h_vdev_db; + __s8 h2c_vdev_db; +} __aligned(8); /** * struct mic_bootparam: Virtio device independent information in device page @@ -42,6 +95,117 @@ struct mic_bootparam { __u8 shutdown_card; } __aligned(8); +/** + * struct mic_device_page: High level representation of the device page + * + * @bootparam: The bootparam structure is used for sharing information and + * status updates between MIC host and card drivers. + * @desc: Array of MIC virtio device descriptors. + */ +struct mic_device_page { + struct mic_bootparam bootparam; + struct mic_device_desc desc[0]; +}; +/** + * struct mic_vqconfig: This is how we expect the device configuration field + * for a virtqueue to be laid out in config space. + * + * @address: Guest/MIC physical address of the virtio ring + * (avail and desc rings) + * @used_address: Guest/MIC physical address of the used ring + * @num: The number of entries in the virtio_ring + */ +struct mic_vqconfig { + __u64 address; + __u64 used_address; + __u16 num; +} __aligned(8); + +/* + * The alignment to use between consumer and producer parts of vring. + * This is pagesize for historical reasons. + */ +#define MIC_VIRTIO_RING_ALIGN 4096 + +#define MIC_MAX_VRINGS 4 +#define MIC_VRING_ENTRIES 128 + +/* + * Max vring entries (power of 2) to ensure desc and avail rings + * fit in a single page + */ +#define MIC_MAX_VRING_ENTRIES 128 + +/** + * Max size of the desc block in bytes: includes: + * - struct mic_device_desc + * - struct mic_vqconfig (num_vq of these) + * - host and guest features + * - virtio device config space + */ +#define MIC_MAX_DESC_BLK_SIZE 256 + +/** + * struct _mic_vring_info - Host vring info exposed to userspace backend + * for the avail index and magic for the card. + * + * @avail_idx: host avail idx + * @magic: A magic debug cookie. + */ +struct _mic_vring_info { + __u16 avail_idx; + int magic; +}; + +/** + * struct mic_vring - Vring information. + * + * @vr: The virtio ring. + * @info: Host vring information exposed to the userspace backend for the + * avail index and magic for the card. + * @va: The va for the buffer allocated for vr and info. + * @len: The length of the buffer required for allocating vr and info. + */ +struct mic_vring { + struct vring vr; + struct _mic_vring_info *info; + void *va; + int len; +}; + +#define mic_aligned_desc_size(d) ALIGN(mic_desc_size(d), 8) + +#ifndef INTEL_MIC_CARD +static inline unsigned mic_desc_size(const struct mic_device_desc *desc) +{ + return mic_aligned_size(*desc) + + desc->num_vq * mic_aligned_size(struct mic_vqconfig) + + desc->feature_len * 2 + + desc->config_len; +} + +static inline struct mic_vqconfig * +mic_vq_config(const struct mic_device_desc *desc) +{ + return (struct mic_vqconfig *)(desc + 1); +} + +static inline __u8 *mic_vq_features(const struct mic_device_desc *desc) +{ + return (__u8 *)(mic_vq_config(desc) + desc->num_vq); +} + +static inline __u8 *mic_vq_configspace(const struct mic_device_desc *desc) +{ + return mic_vq_features(desc) + desc->feature_len * 2; +} +static inline unsigned mic_total_desc_size(struct mic_device_desc *desc) +{ + return mic_aligned_desc_size(desc) + + mic_aligned_size(struct mic_device_ctrl); +} +#endif + /* Device page size */ #define MIC_DP_SIZE 4096 diff --git a/include/uapi/linux/mic_ioctl.h b/include/uapi/linux/mic_ioctl.h new file mode 100644 index 0000000..0e6cbf3 --- /dev/null +++ b/include/uapi/linux/mic_ioctl.h @@ -0,0 +1,74 @@ +/* + * Intel MIC Platform Software Stack (MPSS) + * + * Copyright(c) 2013 Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, version 2, as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * The full GNU General Public License is included in this distribution in + * the file called "COPYING". + * + * Intel MIC Host driver. + * + */ +#ifndef _MIC_IOCTL_H_ +#define _MIC_IOCTL_H_ + +/* + * mic_copy - MIC virtio descriptor copy. + * + * @iov: An array of IOVEC structures containing user space buffers. + * @iovcnt: Number of IOVEC structures in iov. + * @vr_idx: The vring index. + * @update_used: A non zero value results in used index being updated. + * @out_len: The aggregate of the total length written to or read from + * the virtio device. + */ +struct mic_copy_desc { +#ifdef __KERNEL__ + struct iovec __user *iov; +#else + struct iovec *iov; +#endif + int iovcnt; + __u8 vr_idx; + __u8 update_used; + __u32 out_len; +}; + +/* + * Add a new virtio device + * The (struct mic_device_desc *) pointer points to a device page entry + * for the virtio device consisting of: + * - struct mic_device_desc + * - struct mic_vqconfig (num_vq of these) + * - host and guest features + * - virtio device config space + * The total size referenced by the pointer should equal the size returned + * by desc_size() in mic_common.h + */ +#define MIC_VIRTIO_ADD_DEVICE _IOWR('s', 1, struct mic_device_desc *) + +/* + * Copy the number of entries in the iovec and update the used index + * if requested by the user. + */ +#define MIC_VIRTIO_COPY_DESC _IOWR('s', 2, struct mic_copy_desc *) + +/* + * Notify virtio device of a config change + * The (__u8 *) pointer points to config space values for the device + * as they should be written into the device page. The total size + * referenced by the pointer should equal the config_len field of struct + * mic_device_desc. + */ +#define MIC_VIRTIO_CONFIG_CHANGE _IOWR('s', 5, __u8 *) + +#endif -- cgit v0.10.2 From 2141c7c5ee677014023cb50c793f91e85f44d2ea Mon Sep 17 00:00:00 2001 From: Ashutosh Dixit Date: Thu, 5 Sep 2013 16:42:28 -0700 Subject: Intel MIC Card Driver Changes for Virtio Devices. This patch introduces the card "Virtio over PCIe" interface for Intel MIC. It allows virtio drivers on the card to communicate with their user space backends on the host via a device page. Ring 3 apps on the host can add, remove and configure virtio devices. A thin MIC specific virtio_config_ops is implemented which is borrowed heavily from previous similar implementations in lguest and s390 @ drivers/lguest/lguest_device.c drivers/s390/kvm/kvm_virtio.c Co-author: Sudeep Dutt Signed-off-by: Ashutosh Dixit Signed-off-by: Caz Yokoyama Signed-off-by: Dasaratharaman Chandramouli Signed-off-by: Nikhil Rao Signed-off-by: Harshavardhan R Kharche Signed-off-by: Sudeep Dutt Acked-by: Yaozu (Eddie) Dong Reviewed-by: Peter P Waskiewicz Jr Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/misc/mic/Kconfig b/drivers/misc/mic/Kconfig index 01f1a4a..d453768 100644 --- a/drivers/misc/mic/Kconfig +++ b/drivers/misc/mic/Kconfig @@ -24,6 +24,7 @@ comment "Intel MIC Card Driver" config INTEL_MIC_CARD tristate "Intel MIC Card Driver" depends on 64BIT + select VIRTIO default N help This enables card driver support for the Intel Many Integrated diff --git a/drivers/misc/mic/card/Makefile b/drivers/misc/mic/card/Makefile index 6e9675e..69d58be 100644 --- a/drivers/misc/mic/card/Makefile +++ b/drivers/misc/mic/card/Makefile @@ -8,3 +8,4 @@ obj-$(CONFIG_INTEL_MIC_CARD) += mic_card.o mic_card-y += mic_x100.o mic_card-y += mic_device.o mic_card-y += mic_debugfs.o +mic_card-y += mic_virtio.o diff --git a/drivers/misc/mic/card/mic_device.c b/drivers/misc/mic/card/mic_device.c index 3c5c302..4125217 100644 --- a/drivers/misc/mic/card/mic_device.c +++ b/drivers/misc/mic/card/mic_device.c @@ -32,6 +32,7 @@ #include #include "../common/mic_device.h" #include "mic_device.h" +#include "mic_virtio.h" static struct mic_driver *g_drv; static struct mic_irq *shutdown_cookie; @@ -265,10 +266,15 @@ int __init mic_driver_init(struct mic_driver *mdrv) rc = mic_shutdown_init(); if (rc) goto irq_uninit; + rc = mic_devices_init(mdrv); + if (rc) + goto shutdown_uninit; mic_create_card_debug_dir(mdrv); atomic_notifier_chain_register(&panic_notifier_list, &mic_panic); done: return rc; +shutdown_uninit: + mic_shutdown_uninit(); irq_uninit: mic_uninit_irq(); dp_uninit: @@ -286,6 +292,7 @@ put: void mic_driver_uninit(struct mic_driver *mdrv) { mic_delete_card_debug_dir(mdrv); + mic_devices_uninit(mdrv); /* * Inform the host about the shutdown status i.e. poweroff/restart etc. * The module cannot be unloaded so the only code path to call diff --git a/drivers/misc/mic/card/mic_virtio.c b/drivers/misc/mic/card/mic_virtio.c new file mode 100644 index 0000000..38275c1 --- /dev/null +++ b/drivers/misc/mic/card/mic_virtio.c @@ -0,0 +1,631 @@ +/* + * Intel MIC Platform Software Stack (MPSS) + * + * Copyright(c) 2013 Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, version 2, as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * The full GNU General Public License is included in this distribution in + * the file called "COPYING". + * + * Disclaimer: The codes contained in these modules may be specific to + * the Intel Software Development Platform codenamed: Knights Ferry, and + * the Intel product codenamed: Knights Corner, and are not backward + * compatible with other Intel products. Additionally, Intel will NOT + * support the codes or instruction set in future products. + * + * Adapted from: + * + * virtio for kvm on s390 + * + * Copyright IBM Corp. 2008 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License (version 2 only) + * as published by the Free Software Foundation. + * + * Author(s): Christian Borntraeger + * + * Intel MIC Card driver. + * + */ +#include +#include +#include + +#include "../common/mic_device.h" +#include "mic_virtio.h" + +#define VIRTIO_SUBCODE_64 0x0D00 + +#define MIC_MAX_VRINGS 4 +struct mic_vdev { + struct virtio_device vdev; + struct mic_device_desc __iomem *desc; + struct mic_device_ctrl __iomem *dc; + struct mic_device *mdev; + void __iomem *vr[MIC_MAX_VRINGS]; + int used_size[MIC_MAX_VRINGS]; + struct completion reset_done; + struct mic_irq *virtio_cookie; + int c2h_vdev_db; +}; + +static struct mic_irq *virtio_config_cookie; +#define to_micvdev(vd) container_of(vd, struct mic_vdev, vdev) + +/* Helper API to obtain the parent of the virtio device */ +static inline struct device *mic_dev(struct mic_vdev *mvdev) +{ + return mvdev->vdev.dev.parent; +} + +/* This gets the device's feature bits. */ +static u32 mic_get_features(struct virtio_device *vdev) +{ + unsigned int i, bits; + u32 features = 0; + struct mic_device_desc __iomem *desc = to_micvdev(vdev)->desc; + u8 __iomem *in_features = mic_vq_features(desc); + int feature_len = ioread8(&desc->feature_len); + + bits = min_t(unsigned, feature_len, + sizeof(vdev->features)) * 8; + for (i = 0; i < bits; i++) + if (ioread8(&in_features[i / 8]) & (BIT(i % 8))) + features |= BIT(i); + + return features; +} + +static void mic_finalize_features(struct virtio_device *vdev) +{ + unsigned int i, bits; + struct mic_device_desc __iomem *desc = to_micvdev(vdev)->desc; + u8 feature_len = ioread8(&desc->feature_len); + /* Second half of bitmap is features we accept. */ + u8 __iomem *out_features = + mic_vq_features(desc) + feature_len; + + /* Give virtio_ring a chance to accept features. */ + vring_transport_features(vdev); + + memset_io(out_features, 0, feature_len); + bits = min_t(unsigned, feature_len, + sizeof(vdev->features)) * 8; + for (i = 0; i < bits; i++) { + if (test_bit(i, vdev->features)) + iowrite8(ioread8(&out_features[i / 8]) | (1 << (i % 8)), + &out_features[i / 8]); + } +} + +/* + * Reading and writing elements in config space + */ +static void mic_get(struct virtio_device *vdev, unsigned int offset, + void *buf, unsigned len) +{ + struct mic_device_desc __iomem *desc = to_micvdev(vdev)->desc; + + if (offset + len > ioread8(&desc->config_len)) + return; + memcpy_fromio(buf, mic_vq_configspace(desc) + offset, len); +} + +static void mic_set(struct virtio_device *vdev, unsigned int offset, + const void *buf, unsigned len) +{ + struct mic_device_desc __iomem *desc = to_micvdev(vdev)->desc; + + if (offset + len > ioread8(&desc->config_len)) + return; + memcpy_toio(mic_vq_configspace(desc) + offset, buf, len); +} + +/* + * The operations to get and set the status word just access the status + * field of the device descriptor. set_status also interrupts the host + * to tell about status changes. + */ +static u8 mic_get_status(struct virtio_device *vdev) +{ + return ioread8(&to_micvdev(vdev)->desc->status); +} + +static void mic_set_status(struct virtio_device *vdev, u8 status) +{ + struct mic_vdev *mvdev = to_micvdev(vdev); + if (!status) + return; + iowrite8(status, &mvdev->desc->status); + mic_send_intr(mvdev->mdev, mvdev->c2h_vdev_db); +} + +/* Inform host on a virtio device reset and wait for ack from host */ +static void mic_reset_inform_host(struct virtio_device *vdev) +{ + struct mic_vdev *mvdev = to_micvdev(vdev); + struct mic_device_ctrl __iomem *dc = mvdev->dc; + int retry = 100, i; + + iowrite8(0, &dc->host_ack); + iowrite8(1, &dc->vdev_reset); + mic_send_intr(mvdev->mdev, mvdev->c2h_vdev_db); + + /* Wait till host completes all card accesses and acks the reset */ + for (i = retry; i--;) { + if (ioread8(&dc->host_ack)) + break; + msleep(100); + }; + + dev_dbg(mic_dev(mvdev), "%s: retry: %d\n", __func__, retry); + + /* Reset status to 0 in case we timed out */ + iowrite8(0, &mvdev->desc->status); +} + +static void mic_reset(struct virtio_device *vdev) +{ + struct mic_vdev *mvdev = to_micvdev(vdev); + + dev_dbg(mic_dev(mvdev), "%s: virtio id %d\n", + __func__, vdev->id.device); + + mic_reset_inform_host(vdev); + complete_all(&mvdev->reset_done); +} + +/* + * The virtio_ring code calls this API when it wants to notify the Host. + */ +static void mic_notify(struct virtqueue *vq) +{ + struct mic_vdev *mvdev = vq->priv; + + mic_send_intr(mvdev->mdev, mvdev->c2h_vdev_db); +} + +static void mic_del_vq(struct virtqueue *vq, int n) +{ + struct mic_vdev *mvdev = to_micvdev(vq->vdev); + struct vring *vr = (struct vring *) (vq + 1); + + free_pages((unsigned long) vr->used, + get_order(mvdev->used_size[n])); + vring_del_virtqueue(vq); + mic_card_unmap(mvdev->mdev, mvdev->vr[n]); + mvdev->vr[n] = NULL; +} + +static void mic_del_vqs(struct virtio_device *vdev) +{ + struct mic_vdev *mvdev = to_micvdev(vdev); + struct virtqueue *vq, *n; + int idx = 0; + + dev_dbg(mic_dev(mvdev), "%s\n", __func__); + + list_for_each_entry_safe(vq, n, &vdev->vqs, list) + mic_del_vq(vq, idx++); +} + +/* + * This routine will assign vring's allocated in host/io memory. Code in + * virtio_ring.c however continues to access this io memory as if it were local + * memory without io accessors. + */ +static struct virtqueue *mic_find_vq(struct virtio_device *vdev, + unsigned index, + void (*callback)(struct virtqueue *vq), + const char *name) +{ + struct mic_vdev *mvdev = to_micvdev(vdev); + struct mic_vqconfig __iomem *vqconfig; + struct mic_vqconfig config; + struct virtqueue *vq; + void __iomem *va; + struct _mic_vring_info __iomem *info; + void *used; + int vr_size, _vr_size, err, magic; + struct vring *vr; + u8 type = ioread8(&mvdev->desc->type); + + if (index >= ioread8(&mvdev->desc->num_vq)) + return ERR_PTR(-ENOENT); + + if (!name) + return ERR_PTR(-ENOENT); + + /* First assign the vring's allocated in host memory */ + vqconfig = mic_vq_config(mvdev->desc) + index; + memcpy_fromio(&config, vqconfig, sizeof(config)); + _vr_size = vring_size(config.num, MIC_VIRTIO_RING_ALIGN); + vr_size = PAGE_ALIGN(_vr_size + sizeof(struct _mic_vring_info)); + va = mic_card_map(mvdev->mdev, config.address, vr_size); + if (!va) + return ERR_PTR(-ENOMEM); + mvdev->vr[index] = va; + memset_io(va, 0x0, _vr_size); + vq = vring_new_virtqueue(index, + config.num, MIC_VIRTIO_RING_ALIGN, vdev, + false, + va, mic_notify, callback, name); + if (!vq) { + err = -ENOMEM; + goto unmap; + } + info = va + _vr_size; + magic = ioread32(&info->magic); + + if (WARN(magic != MIC_MAGIC + type + index, "magic mismatch")) { + err = -EIO; + goto unmap; + } + + /* Allocate and reassign used ring now */ + mvdev->used_size[index] = PAGE_ALIGN(sizeof(__u16) * 3 + + sizeof(struct vring_used_elem) * config.num); + used = (void *) __get_free_pages(GFP_KERNEL | __GFP_ZERO, + get_order(mvdev->used_size[index])); + if (!used) { + err = -ENOMEM; + dev_err(mic_dev(mvdev), "%s %d err %d\n", + __func__, __LINE__, err); + goto del_vq; + } + iowrite64(virt_to_phys(used), &vqconfig->used_address); + + /* + * To reassign the used ring here we are directly accessing + * struct vring_virtqueue which is a private data structure + * in virtio_ring.c. At the minimum, a BUILD_BUG_ON() in + * vring_new_virtqueue() would ensure that + * (&vq->vring == (struct vring *) (&vq->vq + 1)); + */ + vr = (struct vring *) (vq + 1); + vr->used = used; + + vq->priv = mvdev; + return vq; +del_vq: + vring_del_virtqueue(vq); +unmap: + mic_card_unmap(mvdev->mdev, mvdev->vr[index]); + return ERR_PTR(err); +} + +static int mic_find_vqs(struct virtio_device *vdev, unsigned nvqs, + struct virtqueue *vqs[], + vq_callback_t *callbacks[], + const char *names[]) +{ + struct mic_vdev *mvdev = to_micvdev(vdev); + struct mic_device_ctrl __iomem *dc = mvdev->dc; + int i, err, retry = 100; + + /* We must have this many virtqueues. */ + if (nvqs > ioread8(&mvdev->desc->num_vq)) + return -ENOENT; + + for (i = 0; i < nvqs; ++i) { + dev_dbg(mic_dev(mvdev), "%s: %d: %s\n", + __func__, i, names[i]); + vqs[i] = mic_find_vq(vdev, i, callbacks[i], names[i]); + if (IS_ERR(vqs[i])) { + err = PTR_ERR(vqs[i]); + goto error; + } + } + + iowrite8(1, &dc->used_address_updated); + /* + * Send an interrupt to the host to inform it that used + * rings have been re-assigned. + */ + mic_send_intr(mvdev->mdev, mvdev->c2h_vdev_db); + for (i = retry; i--;) { + if (!ioread8(&dc->used_address_updated)) + break; + msleep(100); + }; + + dev_dbg(mic_dev(mvdev), "%s: retry: %d\n", __func__, retry); + if (!retry) { + err = -ENODEV; + goto error; + } + + return 0; +error: + mic_del_vqs(vdev); + return err; +} + +/* + * The config ops structure as defined by virtio config + */ +static struct virtio_config_ops mic_vq_config_ops = { + .get_features = mic_get_features, + .finalize_features = mic_finalize_features, + .get = mic_get, + .set = mic_set, + .get_status = mic_get_status, + .set_status = mic_set_status, + .reset = mic_reset, + .find_vqs = mic_find_vqs, + .del_vqs = mic_del_vqs, +}; + +static irqreturn_t +mic_virtio_intr_handler(int irq, void *data) +{ + struct mic_vdev *mvdev = data; + struct virtqueue *vq; + + mic_ack_interrupt(mvdev->mdev); + list_for_each_entry(vq, &mvdev->vdev.vqs, list) + vring_interrupt(0, vq); + + return IRQ_HANDLED; +} + +static void mic_virtio_release_dev(struct device *_d) +{ + /* + * No need for a release method similar to virtio PCI. + * Provide an empty one to avoid getting a warning from core. + */ +} + +/* + * adds a new device and register it with virtio + * appropriate drivers are loaded by the device model + */ +static int mic_add_device(struct mic_device_desc __iomem *d, + unsigned int offset, struct mic_driver *mdrv) +{ + struct mic_vdev *mvdev; + int ret; + int virtio_db; + u8 type = ioread8(&d->type); + + mvdev = kzalloc(sizeof(*mvdev), GFP_KERNEL); + if (!mvdev) { + dev_err(mdrv->dev, "Cannot allocate mic dev %u type %u\n", + offset, type); + return -ENOMEM; + } + + mvdev->mdev = &mdrv->mdev; + mvdev->vdev.dev.parent = mdrv->dev; + mvdev->vdev.dev.release = mic_virtio_release_dev; + mvdev->vdev.id.device = type; + mvdev->vdev.config = &mic_vq_config_ops; + mvdev->desc = d; + mvdev->dc = (void __iomem *)d + mic_aligned_desc_size(d); + init_completion(&mvdev->reset_done); + + virtio_db = mic_next_card_db(); + mvdev->virtio_cookie = mic_request_card_irq(mic_virtio_intr_handler, + "virtio intr", mvdev, virtio_db); + if (IS_ERR(mvdev->virtio_cookie)) { + ret = PTR_ERR(mvdev->virtio_cookie); + goto kfree; + } + iowrite8((u8)virtio_db, &mvdev->dc->h2c_vdev_db); + mvdev->c2h_vdev_db = ioread8(&mvdev->dc->c2h_vdev_db); + + ret = register_virtio_device(&mvdev->vdev); + if (ret) { + dev_err(mic_dev(mvdev), + "Failed to register mic device %u type %u\n", + offset, type); + goto free_irq; + } + iowrite64((u64)mvdev, &mvdev->dc->vdev); + dev_dbg(mic_dev(mvdev), "%s: registered mic device %u type %u mvdev %p\n", + __func__, offset, type, mvdev); + + return 0; + +free_irq: + mic_free_card_irq(mvdev->virtio_cookie, mvdev); +kfree: + kfree(mvdev); + return ret; +} + +/* + * match for a mic device with a specific desc pointer + */ +static int mic_match_desc(struct device *dev, void *data) +{ + struct virtio_device *vdev = dev_to_virtio(dev); + struct mic_vdev *mvdev = to_micvdev(vdev); + + return mvdev->desc == (void __iomem *)data; +} + +static void mic_handle_config_change(struct mic_device_desc __iomem *d, + unsigned int offset, struct mic_driver *mdrv) +{ + struct mic_device_ctrl __iomem *dc + = (void __iomem *)d + mic_aligned_desc_size(d); + struct mic_vdev *mvdev = (struct mic_vdev *)ioread64(&dc->vdev); + struct virtio_driver *drv; + + if (ioread8(&dc->config_change) != MIC_VIRTIO_PARAM_CONFIG_CHANGED) + return; + + dev_dbg(mdrv->dev, "%s %d\n", __func__, __LINE__); + drv = container_of(mvdev->vdev.dev.driver, + struct virtio_driver, driver); + if (drv->config_changed) + drv->config_changed(&mvdev->vdev); + iowrite8(1, &dc->guest_ack); +} + +/* + * removes a virtio device if a hot remove event has been + * requested by the host. + */ +static int mic_remove_device(struct mic_device_desc __iomem *d, + unsigned int offset, struct mic_driver *mdrv) +{ + struct mic_device_ctrl __iomem *dc + = (void __iomem *)d + mic_aligned_desc_size(d); + struct mic_vdev *mvdev = (struct mic_vdev *)ioread64(&dc->vdev); + u8 status; + int ret = -1; + + if (ioread8(&dc->config_change) == MIC_VIRTIO_PARAM_DEV_REMOVE) { + dev_dbg(mdrv->dev, + "%s %d config_change %d type %d mvdev %p\n", + __func__, __LINE__, + ioread8(&dc->config_change), ioread8(&d->type), mvdev); + + status = ioread8(&d->status); + INIT_COMPLETION(mvdev->reset_done); + unregister_virtio_device(&mvdev->vdev); + mic_free_card_irq(mvdev->virtio_cookie, mvdev); + if (status & VIRTIO_CONFIG_S_DRIVER_OK) + wait_for_completion(&mvdev->reset_done); + kfree(mvdev); + iowrite8(1, &dc->guest_ack); + dev_dbg(mdrv->dev, "%s %d guest_ack %d\n", + __func__, __LINE__, ioread8(&dc->guest_ack)); + ret = 0; + } + + return ret; +} + +#define REMOVE_DEVICES true + +static void mic_scan_devices(struct mic_driver *mdrv, bool remove) +{ + s8 type; + unsigned int i; + struct mic_device_desc __iomem *d; + struct mic_device_ctrl __iomem *dc; + struct device *dev; + int ret; + + for (i = mic_aligned_size(struct mic_bootparam); + i < MIC_DP_SIZE; i += mic_total_desc_size(d)) { + d = mdrv->dp + i; + dc = (void __iomem *)d + mic_aligned_desc_size(d); + /* + * This read barrier is paired with the corresponding write + * barrier on the host which is inserted before adding or + * removing a virtio device descriptor, by updating the type. + */ + rmb(); + type = ioread8(&d->type); + + /* end of list */ + if (type == 0) + break; + + if (type == -1) + continue; + + /* device already exists */ + dev = device_find_child(mdrv->dev, d, mic_match_desc); + if (dev) { + if (remove) + iowrite8(MIC_VIRTIO_PARAM_DEV_REMOVE, + &dc->config_change); + put_device(dev); + mic_handle_config_change(d, i, mdrv); + ret = mic_remove_device(d, i, mdrv); + if (!ret && !remove) + iowrite8(-1, &d->type); + if (remove) { + iowrite8(0, &dc->config_change); + iowrite8(0, &dc->guest_ack); + } + continue; + } + + /* new device */ + dev_dbg(mdrv->dev, "%s %d Adding new virtio device %p\n", + __func__, __LINE__, d); + if (!remove) + mic_add_device(d, i, mdrv); + } +} + +/* + * mic_hotplug_device tries to find changes in the device page. + */ +static void mic_hotplug_devices(struct work_struct *work) +{ + struct mic_driver *mdrv = container_of(work, + struct mic_driver, hotplug_work); + + mic_scan_devices(mdrv, !REMOVE_DEVICES); +} + +/* + * Interrupt handler for hot plug/config changes etc. + */ +static irqreturn_t +mic_extint_handler(int irq, void *data) +{ + struct mic_driver *mdrv = (struct mic_driver *)data; + + dev_dbg(mdrv->dev, "%s %d hotplug work\n", + __func__, __LINE__); + mic_ack_interrupt(&mdrv->mdev); + schedule_work(&mdrv->hotplug_work); + return IRQ_HANDLED; +} + +/* + * Init function for virtio + */ +int mic_devices_init(struct mic_driver *mdrv) +{ + int rc; + struct mic_bootparam __iomem *bootparam; + int config_db; + + INIT_WORK(&mdrv->hotplug_work, mic_hotplug_devices); + mic_scan_devices(mdrv, !REMOVE_DEVICES); + + config_db = mic_next_card_db(); + virtio_config_cookie = mic_request_card_irq(mic_extint_handler, + "virtio_config_intr", mdrv, config_db); + if (IS_ERR(virtio_config_cookie)) { + rc = PTR_ERR(virtio_config_cookie); + goto exit; + } + + bootparam = mdrv->dp; + iowrite8(config_db, &bootparam->h2c_config_db); + return 0; +exit: + return rc; +} + +/* + * Uninit function for virtio + */ +void mic_devices_uninit(struct mic_driver *mdrv) +{ + struct mic_bootparam __iomem *bootparam = mdrv->dp; + iowrite8(-1, &bootparam->h2c_config_db); + mic_free_card_irq(virtio_config_cookie, mdrv); + flush_work(&mdrv->hotplug_work); + mic_scan_devices(mdrv, REMOVE_DEVICES); +} diff --git a/drivers/misc/mic/card/mic_virtio.h b/drivers/misc/mic/card/mic_virtio.h new file mode 100644 index 0000000..2c5c22c --- /dev/null +++ b/drivers/misc/mic/card/mic_virtio.h @@ -0,0 +1,77 @@ +/* + * Intel MIC Platform Software Stack (MPSS) + * + * Copyright(c) 2013 Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, version 2, as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * The full GNU General Public License is included in this distribution in + * the file called "COPYING". + * + * Disclaimer: The codes contained in these modules may be specific to + * the Intel Software Development Platform codenamed: Knights Ferry, and + * the Intel product codenamed: Knights Corner, and are not backward + * compatible with other Intel products. Additionally, Intel will NOT + * support the codes or instruction set in future products. + * + * Intel MIC Card driver. + * + */ +#ifndef __MIC_CARD_VIRTIO_H +#define __MIC_CARD_VIRTIO_H + +#include +#include "mic_device.h" + +/* + * 64 bit I/O access + */ +#ifndef ioread64 +#define ioread64 readq +#endif +#ifndef iowrite64 +#define iowrite64 writeq +#endif + +static inline unsigned mic_desc_size(struct mic_device_desc __iomem *desc) +{ + return mic_aligned_size(*desc) + + ioread8(&desc->num_vq) * mic_aligned_size(struct mic_vqconfig) + + ioread8(&desc->feature_len) * 2 + + ioread8(&desc->config_len); +} + +static inline struct mic_vqconfig __iomem * +mic_vq_config(struct mic_device_desc __iomem *desc) +{ + return (struct mic_vqconfig __iomem *)(desc + 1); +} + +static inline __u8 __iomem * +mic_vq_features(struct mic_device_desc __iomem *desc) +{ + return (__u8 __iomem *)(mic_vq_config(desc) + ioread8(&desc->num_vq)); +} + +static inline __u8 __iomem * +mic_vq_configspace(struct mic_device_desc __iomem *desc) +{ + return mic_vq_features(desc) + ioread8(&desc->feature_len) * 2; +} +static inline unsigned mic_total_desc_size(struct mic_device_desc __iomem *desc) +{ + return mic_aligned_desc_size(desc) + + mic_aligned_size(struct mic_device_ctrl); +} + +int mic_devices_init(struct mic_driver *mdrv); +void mic_devices_uninit(struct mic_driver *mdrv); + +#endif -- cgit v0.10.2 From 8d49751580db804a02caf6a5b7cebe2ff26c0d7e Mon Sep 17 00:00:00 2001 From: Caz Yokoyama Date: Thu, 5 Sep 2013 16:42:39 -0700 Subject: Sample Implementation of Intel MIC User Space Daemon. This patch introduces a sample user space daemon which implements the virtio device backends on the host. The daemon creates/removes/configures virtio device backends by communicating with the Intel MIC Host Driver. The virtio devices currently supported are virtio net, virtio console and virtio block. Virtio net supports TSO/GSO. The daemon also monitors card shutdown status and takes appropriate actions like killing the virtio backends and resetting the card upon card shutdown and crashes. Co-author: Ashutosh Dixit Co-author: Sudeep Dutt Signed-off-by: Ashutosh Dixit Signed-off-by: Caz Yokoyama Signed-off-by: Dasaratharaman Chandramouli Signed-off-by: Nikhil Rao Signed-off-by: Harshavardhan R Kharche Signed-off-by: Sudeep Dutt Acked-by: Yaozu (Eddie) Dong Signed-off-by: Greg Kroah-Hartman diff --git a/Documentation/mic/mic_overview.txt b/Documentation/mic/mic_overview.txt new file mode 100644 index 0000000..c4424ed --- /dev/null +++ b/Documentation/mic/mic_overview.txt @@ -0,0 +1,49 @@ +An Intel MIC X100 device is a PCIe form factor add-in coprocessor +card based on the Intel Many Integrated Core (MIC) architecture +that runs a Linux OS. It is a PCIe endpoint in a platform and therefore +implements the three required standard address spaces i.e. configuration, +memory and I/O. The host OS loads a device driver as is typical for +PCIe devices. The card itself runs a bootstrap after reset that +transfers control to the card OS downloaded from the host driver. +The card OS as shipped by Intel is a Linux kernel with modifications +for the X100 devices. + +Since it is a PCIe card, it does not have the ability to host hardware +devices for networking, storage and console. We provide these devices +on X100 coprocessors thus enabling a self-bootable equivalent environment +for applications. A key benefit of our solution is that it leverages +the standard virtio framework for network, disk and console devices, +though in our case the virtio framework is used across a PCIe bus. + +Here is a block diagram of the various components described above. The +virtio backends are situated on the host rather than the card given better +single threaded performance for the host compared to MIC, the ability of +the host to initiate DMA's to/from the card using the MIC DMA engine and +the fact that the virtio block storage backend can only be on the host. + + | + +----------+ | +----------+ + | Card OS | | | Host OS | + +----------+ | +----------+ + | ++-------+ +--------+ +------+ | +---------+ +--------+ +--------+ +| Virtio| |Virtio | |Virtio| | |Virtio | |Virtio | |Virtio | +| Net | |Console | |Block | | |Net | |Console | |Block | +| Driver| |Driver | |Driver| | |backend | |backend | |backend | ++-------+ +--------+ +------+ | +---------+ +--------+ +--------+ + | | | | | | | + | | | |User | | | + | | | |------|------------|---------|------- + +-------------------+ |Kernel +--------------------------+ + | | | Virtio over PCIe IOCTLs | + | | +--------------------------+ + +--------------+ | | + |Intel MIC | | +---------------+ + |Card Driver | | |Intel MIC | + +--------------+ | |Host Driver | + | | +---------------+ + | | | + +-------------------------------------------------------------+ + | | + | PCIe Bus | + +-------------------------------------------------------------+ diff --git a/Documentation/mic/mpssd/.gitignore b/Documentation/mic/mpssd/.gitignore new file mode 100644 index 0000000..8b7c72f --- /dev/null +++ b/Documentation/mic/mpssd/.gitignore @@ -0,0 +1 @@ +mpssd diff --git a/Documentation/mic/mpssd/Makefile b/Documentation/mic/mpssd/Makefile new file mode 100644 index 0000000..eb860a7 --- /dev/null +++ b/Documentation/mic/mpssd/Makefile @@ -0,0 +1,19 @@ +# +# Makefile - Intel MIC User Space Tools. +# Copyright(c) 2013, Intel Corporation. +# +ifdef DEBUG +CFLAGS += $(USERWARNFLAGS) -I. -g -Wall -DDEBUG=$(DEBUG) +else +CFLAGS += $(USERWARNFLAGS) -I. -g -Wall +endif + +mpssd: mpssd.o sysfs.o + $(CC) $(CFLAGS) -o $@ $^ -lpthread + +install: + install mpssd /usr/sbin/mpssd + install micctrl /usr/sbin/micctrl + +clean: + rm -f mpssd *.o diff --git a/Documentation/mic/mpssd/micctrl b/Documentation/mic/mpssd/micctrl new file mode 100755 index 0000000..8f2629b --- /dev/null +++ b/Documentation/mic/mpssd/micctrl @@ -0,0 +1,173 @@ +#!/bin/bash +# Intel MIC Platform Software Stack (MPSS) +# +# Copyright(c) 2013 Intel Corporation. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License, version 2, as +# published by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# The full GNU General Public License is included in this distribution in +# the file called "COPYING". +# +# Intel MIC User Space Tools. +# +# micctrl - Controls MIC boot/start/stop. +# +# chkconfig: 2345 95 05 +# description: start MPSS stack processing. +# +### BEGIN INIT INFO +# Provides: micctrl +### END INIT INFO + +# Source function library. +. /etc/init.d/functions + +sysfs="/sys/class/mic" + +_status() +{ + f=$sysfs/$1 + echo -e $1 state: "`cat $f/state`" shutdown_status: "`cat $f/shutdown_status`" +} + +status() +{ + if [ "`echo $1 | head -c3`" == "mic" ]; then + _status $1 + return $? + fi + for f in $sysfs/* + do + _status `basename $f` + RETVAL=$? + [ $RETVAL -ne 0 ] && return $RETVAL + done + return 0 +} + +_reset() +{ + f=$sysfs/$1 + echo reset > $f/state +} + +reset() +{ + if [ "`echo $1 | head -c3`" == "mic" ]; then + _reset $1 + return $? + fi + for f in $sysfs/* + do + _reset `basename $f` + RETVAL=$? + [ $RETVAL -ne 0 ] && return $RETVAL + done + return 0 +} + +_boot() +{ + f=$sysfs/$1 + echo "linux" > $f/bootmode + echo "mic/uos.img" > $f/firmware + echo "mic/$1.image" > $f/ramdisk + echo "boot" > $f/state +} + +boot() +{ + if [ "`echo $1 | head -c3`" == "mic" ]; then + _boot $1 + return $? + fi + for f in $sysfs/* + do + _boot `basename $f` + RETVAL=$? + [ $RETVAL -ne 0 ] && return $RETVAL + done + return 0 +} + +_shutdown() +{ + f=$sysfs/$1 + echo shutdown > $f/state +} + +shutdown() +{ + if [ "`echo $1 | head -c3`" == "mic" ]; then + _shutdown $1 + return $? + fi + for f in $sysfs/* + do + _shutdown `basename $f` + RETVAL=$? + [ $RETVAL -ne 0 ] && return $RETVAL + done + return 0 +} + +_wait() +{ + f=$sysfs/$1 + while [ "`cat $f/state`" != "offline" -a "`cat $f/state`" != "online" ] + do + sleep 1 + echo -e "Waiting for $1 to go offline" + done +} + +wait() +{ + if [ "`echo $1 | head -c3`" == "mic" ]; then + _wait $1 + return $? + fi + # Wait for the cards to go offline + for f in $sysfs/* + do + _wait `basename $f` + RETVAL=$? + [ $RETVAL -ne 0 ] && return $RETVAL + done + return 0 +} + +if [ ! -d "$sysfs" ]; then + echo -e $"Module unloaded " + exit 3 +fi + +case $1 in + -s) + status $2 + ;; + -r) + reset $2 + ;; + -b) + boot $2 + ;; + -S) + shutdown $2 + ;; + -w) + wait $2 + ;; + *) + echo $"Usage: $0 {-s (status) |-r (reset) |-b (boot) |-S (shutdown) |-w (wait)}" + exit 2 +esac + +exit $? diff --git a/Documentation/mic/mpssd/mpss b/Documentation/mic/mpssd/mpss new file mode 100755 index 0000000..3136c68 --- /dev/null +++ b/Documentation/mic/mpssd/mpss @@ -0,0 +1,202 @@ +#!/bin/bash +# Intel MIC Platform Software Stack (MPSS) +# +# Copyright(c) 2013 Intel Corporation. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License, version 2, as +# published by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# The full GNU General Public License is included in this distribution in +# the file called "COPYING". +# +# Intel MIC User Space Tools. +# +# mpss Start mpssd. +# +# chkconfig: 2345 95 05 +# description: start MPSS stack processing. +# +### BEGIN INIT INFO +# Provides: mpss +# Required-Start: +# Required-Stop: +# Short-Description: MPSS stack control +# Description: MPSS stack control +### END INIT INFO + +# Source function library. +. /etc/init.d/functions + +exec=/usr/sbin/mpssd +sysfs="/sys/class/mic" + +start() +{ + [ -x $exec ] || exit 5 + + if [ "`ps -e | awk '{print $4}' | grep mpssd | head -1`" = "mpssd" ]; then + echo -e $"MPSSD already running! " + success + echo + return 0 + fi + + echo -e $"Starting MPSS Stack" + echo -e $"Loading MIC_HOST Module" + + # Ensure the driver is loaded + if [ ! -d "$sysfs" ]; then + modprobe mic_host + RETVAL=$? + if [ $RETVAL -ne 0 ]; then + failure + echo + return $RETVAL + fi + fi + + # Start the daemon + echo -n $"Starting MPSSD " + $exec + RETVAL=$? + if [ $RETVAL -ne 0 ]; then + failure + echo + return $RETVAL + fi + success + echo + + sleep 5 + + # Boot the cards + micctrl -b + + # Wait till ping works + for f in $sysfs/* + do + count=100 + ipaddr=`cat $f/cmdline` + ipaddr=${ipaddr#*address,} + ipaddr=`echo $ipaddr | cut -d, -f1 | cut -d\; -f1` + while [ $count -ge 0 ] + do + echo -e "Pinging "`basename $f`" " + ping -c 1 $ipaddr &> /dev/null + RETVAL=$? + if [ $RETVAL -eq 0 ]; then + success + break + fi + sleep 1 + count=`expr $count - 1` + done + [ $RETVAL -ne 0 ] && failure || success + echo + done + return $RETVAL +} + +stop() +{ + echo -e $"Shutting down MPSS Stack: " + + # Bail out if module is unloaded + if [ ! -d "$sysfs" ]; then + echo -n $"Module unloaded " + success + echo + return 0 + fi + + # Shut down the cards. + micctrl -S + + # Wait for the cards to go offline + for f in $sysfs/* + do + while [ "`cat $f/state`" != "offline" ] + do + sleep 1 + echo -e "Waiting for "`basename $f`" to go offline" + done + done + + # Display the status of the cards + micctrl -s + + # Kill MPSSD now + echo -n $"Killing MPSSD" + killall -9 mpssd 2>/dev/null + RETVAL=$? + [ $RETVAL -ne 0 ] && failure || success + echo + return $RETVAL +} + +restart() +{ + stop + sleep 5 + start +} + +status() +{ + micctrl -s + if [ "`ps -e | awk '{print $4}' | grep mpssd | head -n 1`" = "mpssd" ]; then + echo "mpssd is running" + else + echo "mpssd is stopped" + fi + return 0 +} + +unload() +{ + if [ ! -d "$sysfs" ]; then + echo -n $"No MIC_HOST Module: " + success + echo + return + fi + + stop + + sleep 5 + echo -n $"Removing MIC_HOST Module: " + modprobe -r mic_host + RETVAL=$? + [ $RETVAL -ne 0 ] && failure || success + echo + return $RETVAL +} + +case $1 in + start) + start + ;; + stop) + stop + ;; + restart) + restart + ;; + status) + status + ;; + unload) + unload + ;; + *) + echo $"Usage: $0 {start|stop|restart|status|unload}" + exit 2 +esac + +exit $? diff --git a/Documentation/mic/mpssd/mpssd.c b/Documentation/mic/mpssd/mpssd.c new file mode 100644 index 0000000..8064804 --- /dev/null +++ b/Documentation/mic/mpssd/mpssd.c @@ -0,0 +1,1701 @@ +/* + * Intel MIC Platform Software Stack (MPSS) + * + * Copyright(c) 2013 Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, version 2, as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * The full GNU General Public License is included in this distribution in + * the file called "COPYING". + * + * Intel MIC User Space Tools. + */ + +#define _GNU_SOURCE + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "mpssd.h" +#include +#include + +static void init_mic(struct mic_info *mic); + +static FILE *logfp; +static struct mic_info mic_list; + +#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) + +#define min_t(type, x, y) ({ \ + type __min1 = (x); \ + type __min2 = (y); \ + __min1 < __min2 ? __min1 : __min2; }) + +/* align addr on a size boundary - adjust address up/down if needed */ +#define _ALIGN_DOWN(addr, size) ((addr)&(~((size)-1))) +#define _ALIGN_UP(addr, size) _ALIGN_DOWN(addr + size - 1, size) + +/* align addr on a size boundary - adjust address up if needed */ +#define _ALIGN(addr, size) _ALIGN_UP(addr, size) + +/* to align the pointer to the (next) page boundary */ +#define PAGE_ALIGN(addr) _ALIGN(addr, PAGE_SIZE) + +#define ACCESS_ONCE(x) (*(volatile typeof(x) *)&(x)) + +#define GSO_ENABLED 1 +#define MAX_GSO_SIZE (64 * 1024) +#define ETH_H_LEN 14 +#define MAX_NET_PKT_SIZE (_ALIGN_UP(MAX_GSO_SIZE + ETH_H_LEN, 64)) +#define MIC_DEVICE_PAGE_END 0x1000 + +#ifndef VIRTIO_NET_HDR_F_DATA_VALID +#define VIRTIO_NET_HDR_F_DATA_VALID 2 /* Csum is valid */ +#endif + +static struct { + struct mic_device_desc dd; + struct mic_vqconfig vqconfig[2]; + __u32 host_features, guest_acknowledgements; + struct virtio_console_config cons_config; +} virtcons_dev_page = { + .dd = { + .type = VIRTIO_ID_CONSOLE, + .num_vq = ARRAY_SIZE(virtcons_dev_page.vqconfig), + .feature_len = sizeof(virtcons_dev_page.host_features), + .config_len = sizeof(virtcons_dev_page.cons_config), + }, + .vqconfig[0] = { + .num = htole16(MIC_VRING_ENTRIES), + }, + .vqconfig[1] = { + .num = htole16(MIC_VRING_ENTRIES), + }, +}; + +static struct { + struct mic_device_desc dd; + struct mic_vqconfig vqconfig[2]; + __u32 host_features, guest_acknowledgements; + struct virtio_net_config net_config; +} virtnet_dev_page = { + .dd = { + .type = VIRTIO_ID_NET, + .num_vq = ARRAY_SIZE(virtnet_dev_page.vqconfig), + .feature_len = sizeof(virtnet_dev_page.host_features), + .config_len = sizeof(virtnet_dev_page.net_config), + }, + .vqconfig[0] = { + .num = htole16(MIC_VRING_ENTRIES), + }, + .vqconfig[1] = { + .num = htole16(MIC_VRING_ENTRIES), + }, +#if GSO_ENABLED + .host_features = htole32( + 1 << VIRTIO_NET_F_CSUM | + 1 << VIRTIO_NET_F_GSO | + 1 << VIRTIO_NET_F_GUEST_TSO4 | + 1 << VIRTIO_NET_F_GUEST_TSO6 | + 1 << VIRTIO_NET_F_GUEST_ECN | + 1 << VIRTIO_NET_F_GUEST_UFO), +#else + .host_features = 0, +#endif +}; + +static const char *mic_config_dir = "/etc/sysconfig/mic"; +static const char *virtblk_backend = "VIRTBLK_BACKEND"; +static struct { + struct mic_device_desc dd; + struct mic_vqconfig vqconfig[1]; + __u32 host_features, guest_acknowledgements; + struct virtio_blk_config blk_config; +} virtblk_dev_page = { + .dd = { + .type = VIRTIO_ID_BLOCK, + .num_vq = ARRAY_SIZE(virtblk_dev_page.vqconfig), + .feature_len = sizeof(virtblk_dev_page.host_features), + .config_len = sizeof(virtblk_dev_page.blk_config), + }, + .vqconfig[0] = { + .num = htole16(MIC_VRING_ENTRIES), + }, + .host_features = + htole32(1<name, strerror(errno)); + return ret; + } + } + if (pid < 0) { + mpsslog("%s fork failed errno %s\n", + mic->name, strerror(errno)); + return ret; + } + + ret = waitpid(pid, NULL, 0); + if (ret < 0) { + mpsslog("%s waitpid failed errno %s\n", + mic->name, strerror(errno)); + return ret; + } + + snprintf(ipaddr, IFNAMSIZ, "172.31.%d.254/24", mic->id); + + pid = fork(); + if (pid == 0) { + ifargv[0] = "ip"; + ifargv[1] = "addr"; + ifargv[2] = "add"; + ifargv[3] = ipaddr; + ifargv[4] = "dev"; + ifargv[5] = dev; + ifargv[6] = NULL; + mpsslog("Configuring %s ipaddr %s\n", dev, ipaddr); + ret = execvp("ip", ifargv); + if (ret < 0) { + mpsslog("%s execvp failed errno %s\n", + mic->name, strerror(errno)); + return ret; + } + } + if (pid < 0) { + mpsslog("%s fork failed errno %s\n", + mic->name, strerror(errno)); + return ret; + } + + ret = waitpid(pid, NULL, 0); + if (ret < 0) { + mpsslog("%s waitpid failed errno %s\n", + mic->name, strerror(errno)); + return ret; + } + mpsslog("MIC name %s %s %d DONE!\n", + mic->name, __func__, __LINE__); + return 0; +} + +static int tun_alloc(struct mic_info *mic, char *dev) +{ + struct ifreq ifr; + int fd, err; +#if GSO_ENABLED + unsigned offload; +#endif + fd = open("/dev/net/tun", O_RDWR); + if (fd < 0) { + mpsslog("Could not open /dev/net/tun %s\n", strerror(errno)); + goto done; + } + + memset(&ifr, 0, sizeof(ifr)); + + ifr.ifr_flags = IFF_TAP | IFF_NO_PI | IFF_VNET_HDR; + if (*dev) + strncpy(ifr.ifr_name, dev, IFNAMSIZ); + + err = ioctl(fd, TUNSETIFF, (void *) &ifr); + if (err < 0) { + mpsslog("%s %s %d TUNSETIFF failed %s\n", + mic->name, __func__, __LINE__, strerror(errno)); + close(fd); + return err; + } +#if GSO_ENABLED + offload = TUN_F_CSUM | TUN_F_TSO4 | TUN_F_TSO6 | + TUN_F_TSO_ECN | TUN_F_UFO; + + err = ioctl(fd, TUNSETOFFLOAD, offload); + if (err < 0) { + mpsslog("%s %s %d TUNSETOFFLOAD failed %s\n", + mic->name, __func__, __LINE__, strerror(errno)); + close(fd); + return err; + } +#endif + strcpy(dev, ifr.ifr_name); + mpsslog("Created TAP %s\n", dev); +done: + return fd; +} + +#define NET_FD_VIRTIO_NET 0 +#define NET_FD_TUN 1 +#define MAX_NET_FD 2 + +static void set_dp(struct mic_info *mic, int type, void *dp) +{ + switch (type) { + case VIRTIO_ID_CONSOLE: + mic->mic_console.console_dp = dp; + return; + case VIRTIO_ID_NET: + mic->mic_net.net_dp = dp; + return; + case VIRTIO_ID_BLOCK: + mic->mic_virtblk.block_dp = dp; + return; + } + mpsslog("%s %s %d not found\n", mic->name, __func__, type); + assert(0); +} + +static void *get_dp(struct mic_info *mic, int type) +{ + switch (type) { + case VIRTIO_ID_CONSOLE: + return mic->mic_console.console_dp; + case VIRTIO_ID_NET: + return mic->mic_net.net_dp; + case VIRTIO_ID_BLOCK: + return mic->mic_virtblk.block_dp; + } + mpsslog("%s %s %d not found\n", mic->name, __func__, type); + assert(0); + return NULL; +} + +static struct mic_device_desc *get_device_desc(struct mic_info *mic, int type) +{ + struct mic_device_desc *d; + int i; + void *dp = get_dp(mic, type); + + for (i = mic_aligned_size(struct mic_bootparam); i < PAGE_SIZE; + i += mic_total_desc_size(d)) { + d = dp + i; + + /* End of list */ + if (d->type == 0) + break; + + if (d->type == -1) + continue; + + mpsslog("%s %s d-> type %d d %p\n", + mic->name, __func__, d->type, d); + + if (d->type == (__u8)type) + return d; + } + mpsslog("%s %s %d not found\n", mic->name, __func__, type); + assert(0); + return NULL; +} + +/* See comments in vhost.c for explanation of next_desc() */ +static unsigned next_desc(struct vring_desc *desc) +{ + unsigned int next; + + if (!(le16toh(desc->flags) & VRING_DESC_F_NEXT)) + return -1U; + next = le16toh(desc->next); + return next; +} + +/* Sum up all the IOVEC length */ +static ssize_t +sum_iovec_len(struct mic_copy_desc *copy) +{ + ssize_t sum = 0; + int i; + + for (i = 0; i < copy->iovcnt; i++) + sum += copy->iov[i].iov_len; + return sum; +} + +static inline void verify_out_len(struct mic_info *mic, + struct mic_copy_desc *copy) +{ + if (copy->out_len != sum_iovec_len(copy)) { + mpsslog("%s %s %d BUG copy->out_len 0x%x len 0x%x\n", + mic->name, __func__, __LINE__, + copy->out_len, sum_iovec_len(copy)); + assert(copy->out_len == sum_iovec_len(copy)); + } +} + +/* Display an iovec */ +static void +disp_iovec(struct mic_info *mic, struct mic_copy_desc *copy, + const char *s, int line) +{ + int i; + + for (i = 0; i < copy->iovcnt; i++) + mpsslog("%s %s %d copy->iov[%d] addr %p len 0x%lx\n", + mic->name, s, line, i, + copy->iov[i].iov_base, copy->iov[i].iov_len); +} + +static inline __u16 read_avail_idx(struct mic_vring *vr) +{ + return ACCESS_ONCE(vr->info->avail_idx); +} + +static inline void txrx_prepare(int type, bool tx, struct mic_vring *vr, + struct mic_copy_desc *copy, ssize_t len) +{ + copy->vr_idx = tx ? 0 : 1; + copy->update_used = true; + if (type == VIRTIO_ID_NET) + copy->iov[1].iov_len = len - sizeof(struct virtio_net_hdr); + else + copy->iov[0].iov_len = len; +} + +/* Central API which triggers the copies */ +static int +mic_virtio_copy(struct mic_info *mic, int fd, + struct mic_vring *vr, struct mic_copy_desc *copy) +{ + int ret; + + ret = ioctl(fd, MIC_VIRTIO_COPY_DESC, copy); + if (ret) { + mpsslog("%s %s %d errno %s ret %d\n", + mic->name, __func__, __LINE__, + strerror(errno), ret); + } + return ret; +} + +/* + * This initialization routine requires at least one + * vring i.e. vr0. vr1 is optional. + */ +static void * +init_vr(struct mic_info *mic, int fd, int type, + struct mic_vring *vr0, struct mic_vring *vr1, int num_vq) +{ + int vr_size; + char *va; + + vr_size = PAGE_ALIGN(vring_size(MIC_VRING_ENTRIES, + MIC_VIRTIO_RING_ALIGN) + sizeof(struct _mic_vring_info)); + va = mmap(NULL, MIC_DEVICE_PAGE_END + vr_size * num_vq, + PROT_READ, MAP_SHARED, fd, 0); + if (MAP_FAILED == va) { + mpsslog("%s %s %d mmap failed errno %s\n", + mic->name, __func__, __LINE__, + strerror(errno)); + goto done; + } + set_dp(mic, type, va); + vr0->va = (struct mic_vring *)&va[MIC_DEVICE_PAGE_END]; + vr0->info = vr0->va + + vring_size(MIC_VRING_ENTRIES, MIC_VIRTIO_RING_ALIGN); + vring_init(&vr0->vr, + MIC_VRING_ENTRIES, vr0->va, MIC_VIRTIO_RING_ALIGN); + mpsslog("%s %s vr0 %p vr0->info %p vr_size 0x%x vring 0x%x ", + __func__, mic->name, vr0->va, vr0->info, vr_size, + vring_size(MIC_VRING_ENTRIES, MIC_VIRTIO_RING_ALIGN)); + mpsslog("magic 0x%x expected 0x%x\n", + vr0->info->magic, MIC_MAGIC + type); + assert(vr0->info->magic == MIC_MAGIC + type); + if (vr1) { + vr1->va = (struct mic_vring *) + &va[MIC_DEVICE_PAGE_END + vr_size]; + vr1->info = vr1->va + vring_size(MIC_VRING_ENTRIES, + MIC_VIRTIO_RING_ALIGN); + vring_init(&vr1->vr, + MIC_VRING_ENTRIES, vr1->va, MIC_VIRTIO_RING_ALIGN); + mpsslog("%s %s vr1 %p vr1->info %p vr_size 0x%x vring 0x%x ", + __func__, mic->name, vr1->va, vr1->info, vr_size, + vring_size(MIC_VRING_ENTRIES, MIC_VIRTIO_RING_ALIGN)); + mpsslog("magic 0x%x expected 0x%x\n", + vr1->info->magic, MIC_MAGIC + type + 1); + assert(vr1->info->magic == MIC_MAGIC + type + 1); + } +done: + return va; +} + +static void +wait_for_card_driver(struct mic_info *mic, int fd, int type) +{ + struct pollfd pollfd; + int err; + struct mic_device_desc *desc = get_device_desc(mic, type); + + pollfd.fd = fd; + mpsslog("%s %s Waiting .... desc-> type %d status 0x%x\n", + mic->name, __func__, type, desc->status); + while (1) { + pollfd.events = POLLIN; + pollfd.revents = 0; + err = poll(&pollfd, 1, -1); + if (err < 0) { + mpsslog("%s %s poll failed %s\n", + mic->name, __func__, strerror(errno)); + continue; + } + + if (pollfd.revents) { + mpsslog("%s %s Waiting... desc-> type %d status 0x%x\n", + mic->name, __func__, type, desc->status); + if (desc->status & VIRTIO_CONFIG_S_DRIVER_OK) { + mpsslog("%s %s poll.revents %d\n", + mic->name, __func__, pollfd.revents); + mpsslog("%s %s desc-> type %d status 0x%x\n", + mic->name, __func__, type, + desc->status); + break; + } + } + } +} + +/* Spin till we have some descriptors */ +static void +spin_for_descriptors(struct mic_info *mic, struct mic_vring *vr) +{ + __u16 avail_idx = read_avail_idx(vr); + + while (avail_idx == le16toh(ACCESS_ONCE(vr->vr.avail->idx))) { +#ifdef DEBUG + mpsslog("%s %s waiting for desc avail %d info_avail %d\n", + mic->name, __func__, + le16toh(vr->vr.avail->idx), vr->info->avail_idx); +#endif + sched_yield(); + } +} + +static void * +virtio_net(void *arg) +{ + static __u8 vnet_hdr[2][sizeof(struct virtio_net_hdr)]; + static __u8 vnet_buf[2][MAX_NET_PKT_SIZE] __aligned(64); + struct iovec vnet_iov[2][2] = { + { { .iov_base = vnet_hdr[0], .iov_len = sizeof(vnet_hdr[0]) }, + { .iov_base = vnet_buf[0], .iov_len = sizeof(vnet_buf[0]) } }, + { { .iov_base = vnet_hdr[1], .iov_len = sizeof(vnet_hdr[1]) }, + { .iov_base = vnet_buf[1], .iov_len = sizeof(vnet_buf[1]) } }, + }; + struct iovec *iov0 = vnet_iov[0], *iov1 = vnet_iov[1]; + struct mic_info *mic = (struct mic_info *)arg; + char if_name[IFNAMSIZ]; + struct pollfd net_poll[MAX_NET_FD]; + struct mic_vring tx_vr, rx_vr; + struct mic_copy_desc copy; + struct mic_device_desc *desc; + int err; + + snprintf(if_name, IFNAMSIZ, "mic%d", mic->id); + mic->mic_net.tap_fd = tun_alloc(mic, if_name); + if (mic->mic_net.tap_fd < 0) + goto done; + + if (tap_configure(mic, if_name)) + goto done; + mpsslog("MIC name %s id %d\n", mic->name, mic->id); + + net_poll[NET_FD_VIRTIO_NET].fd = mic->mic_net.virtio_net_fd; + net_poll[NET_FD_VIRTIO_NET].events = POLLIN; + net_poll[NET_FD_TUN].fd = mic->mic_net.tap_fd; + net_poll[NET_FD_TUN].events = POLLIN; + + if (MAP_FAILED == init_vr(mic, mic->mic_net.virtio_net_fd, + VIRTIO_ID_NET, &tx_vr, &rx_vr, + virtnet_dev_page.dd.num_vq)) { + mpsslog("%s init_vr failed %s\n", + mic->name, strerror(errno)); + goto done; + } + + copy.iovcnt = 2; + desc = get_device_desc(mic, VIRTIO_ID_NET); + + while (1) { + ssize_t len; + + net_poll[NET_FD_VIRTIO_NET].revents = 0; + net_poll[NET_FD_TUN].revents = 0; + + /* Start polling for data from tap and virtio net */ + err = poll(net_poll, 2, -1); + if (err < 0) { + mpsslog("%s poll failed %s\n", + __func__, strerror(errno)); + continue; + } + if (!(desc->status & VIRTIO_CONFIG_S_DRIVER_OK)) + wait_for_card_driver(mic, mic->mic_net.virtio_net_fd, + VIRTIO_ID_NET); + /* + * Check if there is data to be read from TUN and write to + * virtio net fd if there is. + */ + if (net_poll[NET_FD_TUN].revents & POLLIN) { + copy.iov = iov0; + len = readv(net_poll[NET_FD_TUN].fd, + copy.iov, copy.iovcnt); + if (len > 0) { + struct virtio_net_hdr *hdr + = (struct virtio_net_hdr *) vnet_hdr[0]; + + /* Disable checksums on the card since we are on + a reliable PCIe link */ + hdr->flags |= VIRTIO_NET_HDR_F_DATA_VALID; +#ifdef DEBUG + mpsslog("%s %s %d hdr->flags 0x%x ", mic->name, + __func__, __LINE__, hdr->flags); + mpsslog("copy.out_len %d hdr->gso_type 0x%x\n", + copy.out_len, hdr->gso_type); +#endif +#ifdef DEBUG + disp_iovec(mic, copy, __func__, __LINE__); + mpsslog("%s %s %d read from tap 0x%lx\n", + mic->name, __func__, __LINE__, + len); +#endif + spin_for_descriptors(mic, &tx_vr); + txrx_prepare(VIRTIO_ID_NET, 1, &tx_vr, ©, + len); + + err = mic_virtio_copy(mic, + mic->mic_net.virtio_net_fd, &tx_vr, + ©); + if (err < 0) { + mpsslog("%s %s %d mic_virtio_copy %s\n", + mic->name, __func__, __LINE__, + strerror(errno)); + } + if (!err) + verify_out_len(mic, ©); +#ifdef DEBUG + disp_iovec(mic, copy, __func__, __LINE__); + mpsslog("%s %s %d wrote to net 0x%lx\n", + mic->name, __func__, __LINE__, + sum_iovec_len(©)); +#endif + /* Reinitialize IOV for next run */ + iov0[1].iov_len = MAX_NET_PKT_SIZE; + } else if (len < 0) { + disp_iovec(mic, ©, __func__, __LINE__); + mpsslog("%s %s %d read failed %s ", mic->name, + __func__, __LINE__, strerror(errno)); + mpsslog("cnt %d sum %d\n", + copy.iovcnt, sum_iovec_len(©)); + } + } + + /* + * Check if there is data to be read from virtio net and + * write to TUN if there is. + */ + if (net_poll[NET_FD_VIRTIO_NET].revents & POLLIN) { + while (rx_vr.info->avail_idx != + le16toh(rx_vr.vr.avail->idx)) { + copy.iov = iov1; + txrx_prepare(VIRTIO_ID_NET, 0, &rx_vr, ©, + MAX_NET_PKT_SIZE + + sizeof(struct virtio_net_hdr)); + + err = mic_virtio_copy(mic, + mic->mic_net.virtio_net_fd, &rx_vr, + ©); + if (!err) { +#ifdef DEBUG + struct virtio_net_hdr *hdr + = (struct virtio_net_hdr *) + vnet_hdr[1]; + + mpsslog("%s %s %d hdr->flags 0x%x, ", + mic->name, __func__, __LINE__, + hdr->flags); + mpsslog("out_len %d gso_type 0x%x\n", + copy.out_len, + hdr->gso_type); +#endif + /* Set the correct output iov_len */ + iov1[1].iov_len = copy.out_len - + sizeof(struct virtio_net_hdr); + verify_out_len(mic, ©); +#ifdef DEBUG + disp_iovec(mic, copy, __func__, + __LINE__); + mpsslog("%s %s %d ", + mic->name, __func__, __LINE__); + mpsslog("read from net 0x%lx\n", + sum_iovec_len(copy)); +#endif + len = writev(net_poll[NET_FD_TUN].fd, + copy.iov, copy.iovcnt); + if (len != sum_iovec_len(©)) { + mpsslog("Tun write failed %s ", + strerror(errno)); + mpsslog("len 0x%x ", len); + mpsslog("read_len 0x%x\n", + sum_iovec_len(©)); + } else { +#ifdef DEBUG + disp_iovec(mic, ©, __func__, + __LINE__); + mpsslog("%s %s %d ", + mic->name, __func__, + __LINE__); + mpsslog("wrote to tap 0x%lx\n", + len); +#endif + } + } else { + mpsslog("%s %s %d mic_virtio_copy %s\n", + mic->name, __func__, __LINE__, + strerror(errno)); + break; + } + } + } + if (net_poll[NET_FD_VIRTIO_NET].revents & POLLERR) + mpsslog("%s: %s: POLLERR\n", __func__, mic->name); + } +done: + pthread_exit(NULL); +} + +/* virtio_console */ +#define VIRTIO_CONSOLE_FD 0 +#define MONITOR_FD (VIRTIO_CONSOLE_FD + 1) +#define MAX_CONSOLE_FD (MONITOR_FD + 1) /* must be the last one + 1 */ +#define MAX_BUFFER_SIZE PAGE_SIZE + +static void * +virtio_console(void *arg) +{ + static __u8 vcons_buf[2][PAGE_SIZE]; + struct iovec vcons_iov[2] = { + { .iov_base = vcons_buf[0], .iov_len = sizeof(vcons_buf[0]) }, + { .iov_base = vcons_buf[1], .iov_len = sizeof(vcons_buf[1]) }, + }; + struct iovec *iov0 = &vcons_iov[0], *iov1 = &vcons_iov[1]; + struct mic_info *mic = (struct mic_info *)arg; + int err; + struct pollfd console_poll[MAX_CONSOLE_FD]; + int pty_fd; + char *pts_name; + ssize_t len; + struct mic_vring tx_vr, rx_vr; + struct mic_copy_desc copy; + struct mic_device_desc *desc; + + pty_fd = posix_openpt(O_RDWR); + if (pty_fd < 0) { + mpsslog("can't open a pseudoterminal master device: %s\n", + strerror(errno)); + goto _return; + } + pts_name = ptsname(pty_fd); + if (pts_name == NULL) { + mpsslog("can't get pts name\n"); + goto _close_pty; + } + printf("%s console message goes to %s\n", mic->name, pts_name); + mpsslog("%s console message goes to %s\n", mic->name, pts_name); + err = grantpt(pty_fd); + if (err < 0) { + mpsslog("can't grant access: %s %s\n", + pts_name, strerror(errno)); + goto _close_pty; + } + err = unlockpt(pty_fd); + if (err < 0) { + mpsslog("can't unlock a pseudoterminal: %s %s\n", + pts_name, strerror(errno)); + goto _close_pty; + } + console_poll[MONITOR_FD].fd = pty_fd; + console_poll[MONITOR_FD].events = POLLIN; + + console_poll[VIRTIO_CONSOLE_FD].fd = mic->mic_console.virtio_console_fd; + console_poll[VIRTIO_CONSOLE_FD].events = POLLIN; + + if (MAP_FAILED == init_vr(mic, mic->mic_console.virtio_console_fd, + VIRTIO_ID_CONSOLE, &tx_vr, &rx_vr, + virtcons_dev_page.dd.num_vq)) { + mpsslog("%s init_vr failed %s\n", + mic->name, strerror(errno)); + goto _close_pty; + } + + copy.iovcnt = 1; + desc = get_device_desc(mic, VIRTIO_ID_CONSOLE); + + for (;;) { + console_poll[MONITOR_FD].revents = 0; + console_poll[VIRTIO_CONSOLE_FD].revents = 0; + err = poll(console_poll, MAX_CONSOLE_FD, -1); + if (err < 0) { + mpsslog("%s %d: poll failed: %s\n", __func__, __LINE__, + strerror(errno)); + continue; + } + if (!(desc->status & VIRTIO_CONFIG_S_DRIVER_OK)) + wait_for_card_driver(mic, + mic->mic_console.virtio_console_fd, + VIRTIO_ID_CONSOLE); + + if (console_poll[MONITOR_FD].revents & POLLIN) { + copy.iov = iov0; + len = readv(pty_fd, copy.iov, copy.iovcnt); + if (len > 0) { +#ifdef DEBUG + disp_iovec(mic, copy, __func__, __LINE__); + mpsslog("%s %s %d read from tap 0x%lx\n", + mic->name, __func__, __LINE__, + len); +#endif + spin_for_descriptors(mic, &tx_vr); + txrx_prepare(VIRTIO_ID_CONSOLE, 1, &tx_vr, + ©, len); + + err = mic_virtio_copy(mic, + mic->mic_console.virtio_console_fd, + &tx_vr, ©); + if (err < 0) { + mpsslog("%s %s %d mic_virtio_copy %s\n", + mic->name, __func__, __LINE__, + strerror(errno)); + } + if (!err) + verify_out_len(mic, ©); +#ifdef DEBUG + disp_iovec(mic, copy, __func__, __LINE__); + mpsslog("%s %s %d wrote to net 0x%lx\n", + mic->name, __func__, __LINE__, + sum_iovec_len(copy)); +#endif + /* Reinitialize IOV for next run */ + iov0->iov_len = PAGE_SIZE; + } else if (len < 0) { + disp_iovec(mic, ©, __func__, __LINE__); + mpsslog("%s %s %d read failed %s ", + mic->name, __func__, __LINE__, + strerror(errno)); + mpsslog("cnt %d sum %d\n", + copy.iovcnt, sum_iovec_len(©)); + } + } + + if (console_poll[VIRTIO_CONSOLE_FD].revents & POLLIN) { + while (rx_vr.info->avail_idx != + le16toh(rx_vr.vr.avail->idx)) { + copy.iov = iov1; + txrx_prepare(VIRTIO_ID_CONSOLE, 0, &rx_vr, + ©, PAGE_SIZE); + + err = mic_virtio_copy(mic, + mic->mic_console.virtio_console_fd, + &rx_vr, ©); + if (!err) { + /* Set the correct output iov_len */ + iov1->iov_len = copy.out_len; + verify_out_len(mic, ©); +#ifdef DEBUG + disp_iovec(mic, copy, __func__, + __LINE__); + mpsslog("%s %s %d ", + mic->name, __func__, __LINE__); + mpsslog("read from net 0x%lx\n", + sum_iovec_len(copy)); +#endif + len = writev(pty_fd, + copy.iov, copy.iovcnt); + if (len != sum_iovec_len(©)) { + mpsslog("Tun write failed %s ", + strerror(errno)); + mpsslog("len 0x%x ", len); + mpsslog("read_len 0x%x\n", + sum_iovec_len(©)); + } else { +#ifdef DEBUG + disp_iovec(mic, copy, __func__, + __LINE__); + mpsslog("%s %s %d ", + mic->name, __func__, + __LINE__); + mpsslog("wrote to tap 0x%lx\n", + len); +#endif + } + } else { + mpsslog("%s %s %d mic_virtio_copy %s\n", + mic->name, __func__, __LINE__, + strerror(errno)); + break; + } + } + } + if (console_poll[NET_FD_VIRTIO_NET].revents & POLLERR) + mpsslog("%s: %s: POLLERR\n", __func__, mic->name); + } +_close_pty: + close(pty_fd); +_return: + pthread_exit(NULL); +} + +static void +add_virtio_device(struct mic_info *mic, struct mic_device_desc *dd) +{ + char path[PATH_MAX]; + int fd, err; + + snprintf(path, PATH_MAX, "/dev/mic%d", mic->id); + fd = open(path, O_RDWR); + if (fd < 0) { + mpsslog("Could not open %s %s\n", path, strerror(errno)); + return; + } + + err = ioctl(fd, MIC_VIRTIO_ADD_DEVICE, dd); + if (err < 0) { + mpsslog("Could not add %d %s\n", dd->type, strerror(errno)); + close(fd); + return; + } + switch (dd->type) { + case VIRTIO_ID_NET: + mic->mic_net.virtio_net_fd = fd; + mpsslog("Added VIRTIO_ID_NET for %s\n", mic->name); + break; + case VIRTIO_ID_CONSOLE: + mic->mic_console.virtio_console_fd = fd; + mpsslog("Added VIRTIO_ID_CONSOLE for %s\n", mic->name); + break; + case VIRTIO_ID_BLOCK: + mic->mic_virtblk.virtio_block_fd = fd; + mpsslog("Added VIRTIO_ID_BLOCK for %s\n", mic->name); + break; + } +} + +static bool +set_backend_file(struct mic_info *mic) +{ + FILE *config; + char buff[PATH_MAX], *line, *evv, *p; + + snprintf(buff, PATH_MAX, "%s/mpssd%03d.conf", mic_config_dir, mic->id); + config = fopen(buff, "r"); + if (config == NULL) + return false; + do { /* look for "virtblk_backend=XXXX" */ + line = fgets(buff, PATH_MAX, config); + if (line == NULL) + break; + if (*line == '#') + continue; + p = strchr(line, '\n'); + if (p) + *p = '\0'; + } while (strncmp(line, virtblk_backend, strlen(virtblk_backend)) != 0); + fclose(config); + if (line == NULL) + return false; + evv = strchr(line, '='); + if (evv == NULL) + return false; + mic->mic_virtblk.backend_file = malloc(strlen(evv) + 1); + if (mic->mic_virtblk.backend_file == NULL) { + mpsslog("can't allocate memory\n", mic->name, mic->id); + return false; + } + strcpy(mic->mic_virtblk.backend_file, evv + 1); + return true; +} + +#define SECTOR_SIZE 512 +static bool +set_backend_size(struct mic_info *mic) +{ + mic->mic_virtblk.backend_size = lseek(mic->mic_virtblk.backend, 0, + SEEK_END); + if (mic->mic_virtblk.backend_size < 0) { + mpsslog("%s: can't seek: %s\n", + mic->name, mic->mic_virtblk.backend_file); + return false; + } + virtblk_dev_page.blk_config.capacity = + mic->mic_virtblk.backend_size / SECTOR_SIZE; + if ((mic->mic_virtblk.backend_size % SECTOR_SIZE) != 0) + virtblk_dev_page.blk_config.capacity++; + + virtblk_dev_page.blk_config.capacity = + htole64(virtblk_dev_page.blk_config.capacity); + + return true; +} + +static bool +open_backend(struct mic_info *mic) +{ + if (!set_backend_file(mic)) + goto _error_exit; + mic->mic_virtblk.backend = open(mic->mic_virtblk.backend_file, O_RDWR); + if (mic->mic_virtblk.backend < 0) { + mpsslog("%s: can't open: %s\n", mic->name, + mic->mic_virtblk.backend_file); + goto _error_free; + } + if (!set_backend_size(mic)) + goto _error_close; + mic->mic_virtblk.backend_addr = mmap(NULL, + mic->mic_virtblk.backend_size, + PROT_READ|PROT_WRITE, MAP_SHARED, + mic->mic_virtblk.backend, 0L); + if (mic->mic_virtblk.backend_addr == MAP_FAILED) { + mpsslog("%s: can't map: %s %s\n", + mic->name, mic->mic_virtblk.backend_file, + strerror(errno)); + goto _error_close; + } + return true; + + _error_close: + close(mic->mic_virtblk.backend); + _error_free: + free(mic->mic_virtblk.backend_file); + _error_exit: + return false; +} + +static void +close_backend(struct mic_info *mic) +{ + munmap(mic->mic_virtblk.backend_addr, mic->mic_virtblk.backend_size); + close(mic->mic_virtblk.backend); + free(mic->mic_virtblk.backend_file); +} + +static bool +start_virtblk(struct mic_info *mic, struct mic_vring *vring) +{ + if (((__u64)&virtblk_dev_page.blk_config % 8) != 0) { + mpsslog("%s: blk_config is not 8 byte aligned.\n", + mic->name); + return false; + } + add_virtio_device(mic, &virtblk_dev_page.dd); + if (MAP_FAILED == init_vr(mic, mic->mic_virtblk.virtio_block_fd, + VIRTIO_ID_BLOCK, vring, NULL, virtblk_dev_page.dd.num_vq)) { + mpsslog("%s init_vr failed %s\n", + mic->name, strerror(errno)); + return false; + } + return true; +} + +static void +stop_virtblk(struct mic_info *mic) +{ + int vr_size, ret; + + vr_size = PAGE_ALIGN(vring_size(MIC_VRING_ENTRIES, + MIC_VIRTIO_RING_ALIGN) + sizeof(struct _mic_vring_info)); + ret = munmap(mic->mic_virtblk.block_dp, + MIC_DEVICE_PAGE_END + vr_size * virtblk_dev_page.dd.num_vq); + if (ret < 0) + mpsslog("%s munmap errno %d\n", mic->name, errno); + close(mic->mic_virtblk.virtio_block_fd); +} + +static __u8 +header_error_check(struct vring_desc *desc) +{ + if (le32toh(desc->len) != sizeof(struct virtio_blk_outhdr)) { + mpsslog("%s() %d: length is not sizeof(virtio_blk_outhd)\n", + __func__, __LINE__); + return -EIO; + } + if (!(le16toh(desc->flags) & VRING_DESC_F_NEXT)) { + mpsslog("%s() %d: alone\n", + __func__, __LINE__); + return -EIO; + } + if (le16toh(desc->flags) & VRING_DESC_F_WRITE) { + mpsslog("%s() %d: not read\n", + __func__, __LINE__); + return -EIO; + } + return 0; +} + +static int +read_header(int fd, struct virtio_blk_outhdr *hdr, __u32 desc_idx) +{ + struct iovec iovec; + struct mic_copy_desc copy; + + iovec.iov_len = sizeof(*hdr); + iovec.iov_base = hdr; + copy.iov = &iovec; + copy.iovcnt = 1; + copy.vr_idx = 0; /* only one vring on virtio_block */ + copy.update_used = false; /* do not update used index */ + return ioctl(fd, MIC_VIRTIO_COPY_DESC, ©); +} + +static int +transfer_blocks(int fd, struct iovec *iovec, __u32 iovcnt) +{ + struct mic_copy_desc copy; + + copy.iov = iovec; + copy.iovcnt = iovcnt; + copy.vr_idx = 0; /* only one vring on virtio_block */ + copy.update_used = false; /* do not update used index */ + return ioctl(fd, MIC_VIRTIO_COPY_DESC, ©); +} + +static __u8 +status_error_check(struct vring_desc *desc) +{ + if (le32toh(desc->len) != sizeof(__u8)) { + mpsslog("%s() %d: length is not sizeof(status)\n", + __func__, __LINE__); + return -EIO; + } + return 0; +} + +static int +write_status(int fd, __u8 *status) +{ + struct iovec iovec; + struct mic_copy_desc copy; + + iovec.iov_base = status; + iovec.iov_len = sizeof(*status); + copy.iov = &iovec; + copy.iovcnt = 1; + copy.vr_idx = 0; /* only one vring on virtio_block */ + copy.update_used = true; /* Update used index */ + return ioctl(fd, MIC_VIRTIO_COPY_DESC, ©); +} + +static void * +virtio_block(void *arg) +{ + struct mic_info *mic = (struct mic_info *) arg; + int ret; + struct pollfd block_poll; + struct mic_vring vring; + __u16 avail_idx; + __u32 desc_idx; + struct vring_desc *desc; + struct iovec *iovec, *piov; + __u8 status; + __u32 buffer_desc_idx; + struct virtio_blk_outhdr hdr; + void *fos; + + for (;;) { /* forever */ + if (!open_backend(mic)) { /* No virtblk */ + for (mic->mic_virtblk.signaled = 0; + !mic->mic_virtblk.signaled;) + sleep(1); + continue; + } + + /* backend file is specified. */ + if (!start_virtblk(mic, &vring)) + goto _close_backend; + iovec = malloc(sizeof(*iovec) * + le32toh(virtblk_dev_page.blk_config.seg_max)); + if (!iovec) { + mpsslog("%s: can't alloc iovec: %s\n", + mic->name, strerror(ENOMEM)); + goto _stop_virtblk; + } + + block_poll.fd = mic->mic_virtblk.virtio_block_fd; + block_poll.events = POLLIN; + for (mic->mic_virtblk.signaled = 0; + !mic->mic_virtblk.signaled;) { + block_poll.revents = 0; + /* timeout in 1 sec to see signaled */ + ret = poll(&block_poll, 1, 1000); + if (ret < 0) { + mpsslog("%s %d: poll failed: %s\n", + __func__, __LINE__, + strerror(errno)); + continue; + } + + if (!(block_poll.revents & POLLIN)) { +#ifdef DEBUG + mpsslog("%s %d: block_poll.revents=0x%x\n", + __func__, __LINE__, block_poll.revents); +#endif + continue; + } + + /* POLLIN */ + while (vring.info->avail_idx != + le16toh(vring.vr.avail->idx)) { + /* read header element */ + avail_idx = + vring.info->avail_idx & + (vring.vr.num - 1); + desc_idx = le16toh( + vring.vr.avail->ring[avail_idx]); + desc = &vring.vr.desc[desc_idx]; +#ifdef DEBUG + mpsslog("%s() %d: avail_idx=%d ", + __func__, __LINE__, + vring.info->avail_idx); + mpsslog("vring.vr.num=%d desc=%p\n", + vring.vr.num, desc); +#endif + status = header_error_check(desc); + ret = read_header( + mic->mic_virtblk.virtio_block_fd, + &hdr, desc_idx); + if (ret < 0) { + mpsslog("%s() %d %s: ret=%d %s\n", + __func__, __LINE__, + mic->name, ret, + strerror(errno)); + break; + } + /* buffer element */ + piov = iovec; + status = 0; + fos = mic->mic_virtblk.backend_addr + + (hdr.sector * SECTOR_SIZE); + buffer_desc_idx = desc_idx = + next_desc(desc); + for (desc = &vring.vr.desc[buffer_desc_idx]; + desc->flags & VRING_DESC_F_NEXT; + desc_idx = next_desc(desc), + desc = &vring.vr.desc[desc_idx]) { + piov->iov_len = desc->len; + piov->iov_base = fos; + piov++; + fos += desc->len; + } + /* Returning NULLs for VIRTIO_BLK_T_GET_ID. */ + if (hdr.type & ~(VIRTIO_BLK_T_OUT | + VIRTIO_BLK_T_GET_ID)) { + /* + VIRTIO_BLK_T_IN - does not do + anything. Probably for documenting. + VIRTIO_BLK_T_SCSI_CMD - for + virtio_scsi. + VIRTIO_BLK_T_FLUSH - turned off in + config space. + VIRTIO_BLK_T_BARRIER - defined but not + used in anywhere. + */ + mpsslog("%s() %d: type %x ", + __func__, __LINE__, + hdr.type); + mpsslog("is not supported\n"); + status = -ENOTSUP; + + } else { + ret = transfer_blocks( + mic->mic_virtblk.virtio_block_fd, + iovec, + piov - iovec); + if (ret < 0 && + status != 0) + status = ret; + } + /* write status and update used pointer */ + if (status != 0) + status = status_error_check(desc); + ret = write_status( + mic->mic_virtblk.virtio_block_fd, + &status); +#ifdef DEBUG + mpsslog("%s() %d: write status=%d on desc=%p\n", + __func__, __LINE__, + status, desc); +#endif + } + } + free(iovec); +_stop_virtblk: + stop_virtblk(mic); +_close_backend: + close_backend(mic); + } /* forever */ + + pthread_exit(NULL); +} + +static void +reset(struct mic_info *mic) +{ +#define RESET_TIMEOUT 120 + int i = RESET_TIMEOUT; + setsysfs(mic->name, "state", "reset"); + while (i) { + char *state; + state = readsysfs(mic->name, "state"); + if (!state) + goto retry; + mpsslog("%s: %s %d state %s\n", + mic->name, __func__, __LINE__, state); + if ((!strcmp(state, "offline"))) { + free(state); + break; + } + free(state); +retry: + sleep(1); + i--; + } +} + +static int +get_mic_shutdown_status(struct mic_info *mic, char *shutdown_status) +{ + if (!strcmp(shutdown_status, "nop")) + return MIC_NOP; + if (!strcmp(shutdown_status, "crashed")) + return MIC_CRASHED; + if (!strcmp(shutdown_status, "halted")) + return MIC_HALTED; + if (!strcmp(shutdown_status, "poweroff")) + return MIC_POWER_OFF; + if (!strcmp(shutdown_status, "restart")) + return MIC_RESTART; + mpsslog("%s: BUG invalid status %s\n", mic->name, shutdown_status); + /* Invalid state */ + assert(0); +}; + +static int get_mic_state(struct mic_info *mic, char *state) +{ + if (!strcmp(state, "offline")) + return MIC_OFFLINE; + if (!strcmp(state, "online")) + return MIC_ONLINE; + if (!strcmp(state, "shutting_down")) + return MIC_SHUTTING_DOWN; + if (!strcmp(state, "reset_failed")) + return MIC_RESET_FAILED; + mpsslog("%s: BUG invalid state %s\n", mic->name, state); + /* Invalid state */ + assert(0); +}; + +static void mic_handle_shutdown(struct mic_info *mic) +{ +#define SHUTDOWN_TIMEOUT 60 + int i = SHUTDOWN_TIMEOUT, ret, stat = 0; + char *shutdown_status; + while (i) { + shutdown_status = readsysfs(mic->name, "shutdown_status"); + if (!shutdown_status) + continue; + mpsslog("%s: %s %d shutdown_status %s\n", + mic->name, __func__, __LINE__, shutdown_status); + switch (get_mic_shutdown_status(mic, shutdown_status)) { + case MIC_RESTART: + mic->restart = 1; + case MIC_HALTED: + case MIC_POWER_OFF: + case MIC_CRASHED: + free(shutdown_status); + goto reset; + default: + break; + } + free(shutdown_status); + sleep(1); + i--; + } +reset: + ret = kill(mic->pid, SIGTERM); + mpsslog("%s: %s %d kill pid %d ret %d\n", + mic->name, __func__, __LINE__, + mic->pid, ret); + if (!ret) { + ret = waitpid(mic->pid, &stat, + WIFSIGNALED(stat)); + mpsslog("%s: %s %d waitpid ret %d pid %d\n", + mic->name, __func__, __LINE__, + ret, mic->pid); + } + if (ret == mic->pid) + reset(mic); +} + +static void * +mic_config(void *arg) +{ + struct mic_info *mic = (struct mic_info *)arg; + char *state = NULL; + char pathname[PATH_MAX]; + int fd, ret; + struct pollfd ufds[1]; + char value[4096]; + + snprintf(pathname, PATH_MAX - 1, "%s/%s/%s", + MICSYSFSDIR, mic->name, "state"); + + fd = open(pathname, O_RDONLY); + if (fd < 0) { + mpsslog("%s: opening file %s failed %s\n", + mic->name, pathname, strerror(errno)); + goto error; + } + + do { + ret = read(fd, value, sizeof(value)); + if (ret < 0) { + mpsslog("%s: Failed to read sysfs entry '%s': %s\n", + mic->name, pathname, strerror(errno)); + goto close_error1; + } +retry: + state = readsysfs(mic->name, "state"); + if (!state) + goto retry; + mpsslog("%s: %s %d state %s\n", + mic->name, __func__, __LINE__, state); + switch (get_mic_state(mic, state)) { + case MIC_SHUTTING_DOWN: + mic_handle_shutdown(mic); + goto close_error; + default: + break; + } + free(state); + + ufds[0].fd = fd; + ufds[0].events = POLLERR | POLLPRI; + ret = poll(ufds, 1, -1); + if (ret < 0) { + mpsslog("%s: poll failed %s\n", + mic->name, strerror(errno)); + goto close_error1; + } + } while (1); +close_error: + free(state); +close_error1: + close(fd); +error: + init_mic(mic); + pthread_exit(NULL); +} + +static void +set_cmdline(struct mic_info *mic) +{ + char buffer[PATH_MAX]; + int len; + + len = snprintf(buffer, PATH_MAX, + "clocksource=tsc highres=off nohz=off "); + len += snprintf(buffer + len, PATH_MAX, + "cpufreq_on;corec6_off;pc3_off;pc6_off "); + len += snprintf(buffer + len, PATH_MAX, + "ifcfg=static;address,172.31.%d.1;netmask,255.255.255.0", + mic->id); + + setsysfs(mic->name, "cmdline", buffer); + mpsslog("%s: Command line: \"%s\"\n", mic->name, buffer); + snprintf(buffer, PATH_MAX, "172.31.%d.1", mic->id); + mpsslog("%s: IPADDR: \"%s\"\n", mic->name, buffer); +} + +static void +set_log_buf_info(struct mic_info *mic) +{ + int fd; + off_t len; + char system_map[] = "/lib/firmware/mic/System.map"; + char *map, *temp, log_buf[17] = {'\0'}; + + fd = open(system_map, O_RDONLY); + if (fd < 0) { + mpsslog("%s: Opening System.map failed: %d\n", + mic->name, errno); + return; + } + len = lseek(fd, 0, SEEK_END); + if (len < 0) { + mpsslog("%s: Reading System.map size failed: %d\n", + mic->name, errno); + close(fd); + return; + } + map = mmap(NULL, len, PROT_READ, MAP_PRIVATE, fd, 0); + if (map == MAP_FAILED) { + mpsslog("%s: mmap of System.map failed: %d\n", + mic->name, errno); + close(fd); + return; + } + temp = strstr(map, "__log_buf"); + if (!temp) { + mpsslog("%s: __log_buf not found: %d\n", mic->name, errno); + munmap(map, len); + close(fd); + return; + } + strncpy(log_buf, temp - 19, 16); + setsysfs(mic->name, "log_buf_addr", log_buf); + mpsslog("%s: log_buf_addr: %s\n", mic->name, log_buf); + temp = strstr(map, "log_buf_len"); + if (!temp) { + mpsslog("%s: log_buf_len not found: %d\n", mic->name, errno); + munmap(map, len); + close(fd); + return; + } + strncpy(log_buf, temp - 19, 16); + setsysfs(mic->name, "log_buf_len", log_buf); + mpsslog("%s: log_buf_len: %s\n", mic->name, log_buf); + munmap(map, len); + close(fd); +} + +static void init_mic(struct mic_info *mic); + +static void +change_virtblk_backend(int x, siginfo_t *siginfo, void *p) +{ + struct mic_info *mic; + + for (mic = mic_list.next; mic != NULL; mic = mic->next) + mic->mic_virtblk.signaled = 1/* true */; +} + +static void +init_mic(struct mic_info *mic) +{ + struct sigaction ignore = { + .sa_flags = 0, + .sa_handler = SIG_IGN + }; + struct sigaction act = { + .sa_flags = SA_SIGINFO, + .sa_sigaction = change_virtblk_backend, + }; + char buffer[PATH_MAX]; + int err; + + /* + * Currently, one virtio block device is supported for each MIC card + * at a time. Any user (or test) can send a SIGUSR1 to the MIC daemon. + * The signal informs the virtio block backend about a change in the + * configuration file which specifies the virtio backend file name on + * the host. Virtio block backend then re-reads the configuration file + * and switches to the new block device. This signalling mechanism may + * not be required once multiple virtio block devices are supported by + * the MIC daemon. + */ + sigaction(SIGUSR1, &ignore, NULL); + + mic->pid = fork(); + switch (mic->pid) { + case 0: + set_log_buf_info(mic); + set_cmdline(mic); + add_virtio_device(mic, &virtcons_dev_page.dd); + add_virtio_device(mic, &virtnet_dev_page.dd); + err = pthread_create(&mic->mic_console.console_thread, NULL, + virtio_console, mic); + if (err) + mpsslog("%s virtcons pthread_create failed %s\n", + mic->name, strerror(err)); + err = pthread_create(&mic->mic_net.net_thread, NULL, + virtio_net, mic); + if (err) + mpsslog("%s virtnet pthread_create failed %s\n", + mic->name, strerror(err)); + err = pthread_create(&mic->mic_virtblk.block_thread, NULL, + virtio_block, mic); + if (err) + mpsslog("%s virtblk pthread_create failed %s\n", + mic->name, strerror(err)); + sigemptyset(&act.sa_mask); + err = sigaction(SIGUSR1, &act, NULL); + if (err) + mpsslog("%s sigaction SIGUSR1 failed %s\n", + mic->name, strerror(errno)); + while (1) + sleep(60); + case -1: + mpsslog("fork failed MIC name %s id %d errno %d\n", + mic->name, mic->id, errno); + break; + default: + if (mic->restart) { + snprintf(buffer, PATH_MAX, "boot"); + setsysfs(mic->name, "state", buffer); + mpsslog("%s restarting mic %d\n", + mic->name, mic->restart); + mic->restart = 0; + } + pthread_create(&mic->config_thread, NULL, mic_config, mic); + } +} + +static void +start_daemon(void) +{ + struct mic_info *mic; + + for (mic = mic_list.next; mic != NULL; mic = mic->next) + init_mic(mic); + + while (1) + sleep(60); +} + +static int +init_mic_list(void) +{ + struct mic_info *mic = &mic_list; + struct dirent *file; + DIR *dp; + int cnt = 0; + + dp = opendir(MICSYSFSDIR); + if (!dp) + return 0; + + while ((file = readdir(dp)) != NULL) { + if (!strncmp(file->d_name, "mic", 3)) { + mic->next = malloc(sizeof(struct mic_info)); + if (mic->next) { + mic = mic->next; + mic->next = NULL; + memset(mic, 0, sizeof(struct mic_info)); + mic->id = atoi(&file->d_name[3]); + mic->name = malloc(strlen(file->d_name) + 16); + if (mic->name) + strcpy(mic->name, file->d_name); + mpsslog("MIC name %s id %d\n", mic->name, + mic->id); + cnt++; + } + } + } + + closedir(dp); + return cnt; +} + +void +mpsslog(char *format, ...) +{ + va_list args; + char buffer[4096]; + char ts[52], *ts1; + time_t t; + + if (logfp == NULL) + return; + + va_start(args, format); + vsprintf(buffer, format, args); + va_end(args); + + time(&t); + ts1 = ctime_r(&t, ts); + ts1[strlen(ts1) - 1] = '\0'; + fprintf(logfp, "%s: %s", ts1, buffer); + + fflush(logfp); +} + +int +main(int argc, char *argv[]) +{ + int cnt; + pid_t pid; + + myname = argv[0]; + + logfp = fopen(LOGFILE_NAME, "a+"); + if (!logfp) { + fprintf(stderr, "cannot open logfile '%s'\n", LOGFILE_NAME); + exit(1); + } + pid = fork(); + switch (pid) { + case 0: + break; + case -1: + exit(2); + default: + exit(0); + } + + mpsslog("MIC Daemon start\n"); + + cnt = init_mic_list(); + if (cnt == 0) { + mpsslog("MIC module not loaded\n"); + exit(3); + } + mpsslog("MIC found %d devices\n", cnt); + + start_daemon(); + + exit(0); +} diff --git a/Documentation/mic/mpssd/mpssd.h b/Documentation/mic/mpssd/mpssd.h new file mode 100644 index 0000000..b6dee38 --- /dev/null +++ b/Documentation/mic/mpssd/mpssd.h @@ -0,0 +1,100 @@ +/* + * Intel MIC Platform Software Stack (MPSS) + * + * Copyright(c) 2013 Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, version 2, as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * The full GNU General Public License is included in this distribution in + * the file called "COPYING". + * + * Intel MIC User Space Tools. + */ +#ifndef _MPSSD_H_ +#define _MPSSD_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define MICSYSFSDIR "/sys/class/mic" +#define LOGFILE_NAME "/var/log/mpssd" +#define PAGE_SIZE 4096 + +struct mic_console_info { + pthread_t console_thread; + int virtio_console_fd; + void *console_dp; +}; + +struct mic_net_info { + pthread_t net_thread; + int virtio_net_fd; + int tap_fd; + void *net_dp; +}; + +struct mic_virtblk_info { + pthread_t block_thread; + int virtio_block_fd; + void *block_dp; + volatile sig_atomic_t signaled; + char *backend_file; + int backend; + void *backend_addr; + long backend_size; +}; + +struct mic_info { + int id; + char *name; + pthread_t config_thread; + pid_t pid; + struct mic_console_info mic_console; + struct mic_net_info mic_net; + struct mic_virtblk_info mic_virtblk; + int restart; + struct mic_info *next; +}; + +void mpsslog(char *format, ...); +char *readsysfs(char *dir, char *entry); +int setsysfs(char *dir, char *entry, char *value); +#endif diff --git a/Documentation/mic/mpssd/sysfs.c b/Documentation/mic/mpssd/sysfs.c new file mode 100644 index 0000000..11de72b --- /dev/null +++ b/Documentation/mic/mpssd/sysfs.c @@ -0,0 +1,102 @@ +/* + * Intel MIC Platform Software Stack (MPSS) + * + * Copyright(c) 2013 Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, version 2, as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * The full GNU General Public License is included in this distribution in + * the file called "COPYING". + * + * Intel MIC User Space Tools. + */ + +#include "mpssd.h" + +#define PAGE_SIZE 4096 + +char * +readsysfs(char *dir, char *entry) +{ + char filename[PATH_MAX]; + char value[PAGE_SIZE]; + char *string = NULL; + int fd; + int len; + + if (dir == NULL) + snprintf(filename, PATH_MAX, "%s/%s", MICSYSFSDIR, entry); + else + snprintf(filename, PATH_MAX, + "%s/%s/%s", MICSYSFSDIR, dir, entry); + + fd = open(filename, O_RDONLY); + if (fd < 0) { + mpsslog("Failed to open sysfs entry '%s': %s\n", + filename, strerror(errno)); + return NULL; + } + + len = read(fd, value, sizeof(value)); + if (len < 0) { + mpsslog("Failed to read sysfs entry '%s': %s\n", + filename, strerror(errno)); + goto readsys_ret; + } + if (len == 0) + goto readsys_ret; + + value[len - 1] = '\0'; + + string = malloc(strlen(value) + 1); + if (string) + strcpy(string, value); + +readsys_ret: + close(fd); + return string; +} + +int +setsysfs(char *dir, char *entry, char *value) +{ + char filename[PATH_MAX]; + char *oldvalue; + int fd, ret = 0; + + if (dir == NULL) + snprintf(filename, PATH_MAX, "%s/%s", MICSYSFSDIR, entry); + else + snprintf(filename, PATH_MAX, "%s/%s/%s", + MICSYSFSDIR, dir, entry); + + oldvalue = readsysfs(dir, entry); + + fd = open(filename, O_RDWR); + if (fd < 0) { + ret = errno; + mpsslog("Failed to open sysfs entry '%s': %s\n", + filename, strerror(errno)); + goto done; + } + + if (!oldvalue || strcmp(value, oldvalue)) { + if (write(fd, value, strlen(value)) < 0) { + ret = errno; + mpsslog("Failed to write new sysfs entry '%s': %s\n", + filename, strerror(errno)); + } + } + close(fd); +done: + if (oldvalue) + free(oldvalue); + return ret; +} -- cgit v0.10.2 From 0a335b6d47f014c41b8a0c8cbdc6d4886c10f5d5 Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Tue, 24 Sep 2013 10:40:57 +0800 Subject: mmc: cb710: drop free_irq for devm_request_irq allocated irq irq allocated with devm_request_irq should not be freed using free_irq, because doing so causes a dangling pointer, and a subsequent double free. Signed-off-by: Wei Yongjun Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/misc/cb710/core.c b/drivers/misc/cb710/core.c index 2e50f81..fb397e7 100644 --- a/drivers/misc/cb710/core.c +++ b/drivers/misc/cb710/core.c @@ -176,7 +176,7 @@ static int cb710_suspend(struct pci_dev *pdev, pm_message_t state) { struct cb710_chip *chip = pci_get_drvdata(pdev); - free_irq(pdev->irq, chip); + devm_free_irq(&pdev->dev, pdev->irq, chip); pci_save_state(pdev); pci_disable_device(pdev); if (state.event & PM_EVENT_SLEEP) -- cgit v0.10.2 From 33b06938cf81939135448ed448ee5aa95fa86d04 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Tue, 17 Sep 2013 00:58:36 +0300 Subject: hv: vmbus: fix vmbus_recvpacket_raw() return code Don't return success if the buffer has not been initialized. Signed-off-by: Dan Carpenter Signed-off-by: K. Y. Srinivasan Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/hv/channel.c b/drivers/hv/channel.c index 94d5459..cea623c 100644 --- a/drivers/hv/channel.c +++ b/drivers/hv/channel.c @@ -811,6 +811,6 @@ int vmbus_recvpacket_raw(struct vmbus_channel *channel, void *buffer, if (signal) vmbus_setevent(channel); - return 0; + return ret; } EXPORT_SYMBOL_GPL(vmbus_recvpacket_raw); -- cgit v0.10.2 From ad07d8b489b07510d98773472d2a3023701f88b6 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Mon, 5 Aug 2013 14:32:03 +0530 Subject: extcon: max8997: Fix checkpatch warning Fixes the following warning: WARNING: space prohibited before semicolon Signed-off-by: Sachin Kamat Signed-off-by: Chanwoo Choi diff --git a/drivers/extcon/extcon-max8997.c b/drivers/extcon/extcon-max8997.c index 67d6738..e55ec38 100644 --- a/drivers/extcon/extcon-max8997.c +++ b/drivers/extcon/extcon-max8997.c @@ -537,7 +537,7 @@ static void max8997_muic_irq_work(struct work_struct *work) mutex_lock(&info->mutex); - for (i = 0 ; i < ARRAY_SIZE(muic_irqs) ; i++) + for (i = 0; i < ARRAY_SIZE(muic_irqs); i++) if (info->irq == muic_irqs[i].virq) irq_type = muic_irqs[i].irq; -- cgit v0.10.2 From cb9005d7dacb83ba92f39fe6d2a9c0c06ca50cb3 Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Wed, 7 Aug 2013 12:17:14 +0100 Subject: extcon: arizona: Clear trig_sts bits on all paths We want the trig_sts bits to be cleared in all cases where we consider the jack detection interrupt to have been handled. Specifically, if a duplicate detection event was suppressed these bits were not cleared causing the CODEC to not enter a low power state. This patch clears the bits on the duplicate detection code path. Reported-by: Ryo Tsutsui Signed-off-by: Charles Keepax Signed-off-by: Chanwoo Choi diff --git a/drivers/extcon/extcon-arizona.c b/drivers/extcon/extcon-arizona.c index e557130..4696df0 100644 --- a/drivers/extcon/extcon-arizona.c +++ b/drivers/extcon/extcon-arizona.c @@ -1039,6 +1039,7 @@ static irqreturn_t arizona_jackdet(int irq, void *data) else info->micd_timeout = DEFAULT_MICD_TIMEOUT; +out: /* Clear trig_sts to make sure DCVDD is not forced up */ regmap_write(arizona->regmap, ARIZONA_AOD_WKUP_AND_TRIG, ARIZONA_MICD_CLAMP_FALL_TRIG_STS | @@ -1046,7 +1047,6 @@ static irqreturn_t arizona_jackdet(int irq, void *data) ARIZONA_JD1_FALL_TRIG_STS | ARIZONA_JD1_RISE_TRIG_STS); -out: mutex_unlock(&info->lock); pm_runtime_mark_last_busy(info->dev); -- cgit v0.10.2 From d97abdde1753d4608766a8b7a3661d3b70117054 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Mon, 5 Aug 2013 14:30:46 +0530 Subject: extcon: arizona: Fix a typo in a comment Fixes an incomplete comment introduced by commit 9c2ba270 ("extcon: arizona: Simplify HPDET based identification"). Signed-off-by: Sachin Kamat Signed-off-by: Chanwoo Choi diff --git a/drivers/extcon/extcon-arizona.c b/drivers/extcon/extcon-arizona.c index 4696df0..2064eac 100644 --- a/drivers/extcon/extcon-arizona.c +++ b/drivers/extcon/extcon-arizona.c @@ -514,7 +514,7 @@ static int arizona_hpdet_do_id(struct arizona_extcon_info *info, int *reading, } /* - * If we measure the mic as + * If we measure the mic as high impedance */ if (!id_gpio || info->hpdet_res[1] > 50) { dev_dbg(arizona->dev, "Detected mic\n"); -- cgit v0.10.2 From a33411b26e43d67d361298a822ecfd76a177183f Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Mon, 5 Aug 2013 14:32:04 +0530 Subject: extcon: max77693: Fix checkpatch warning Fixes the following warning: WARNING: space prohibited before semicolon Signed-off-by: Sachin Kamat Signed-off-by: Chanwoo Choi diff --git a/drivers/extcon/extcon-max77693.c b/drivers/extcon/extcon-max77693.c index b56bdaa..4849ea1 100644 --- a/drivers/extcon/extcon-max77693.c +++ b/drivers/extcon/extcon-max77693.c @@ -953,7 +953,7 @@ static void max77693_muic_irq_work(struct work_struct *work) mutex_lock(&info->mutex); - for (i = 0 ; i < ARRAY_SIZE(muic_irqs) ; i++) + for (i = 0; i < ARRAY_SIZE(muic_irqs); i++) if (info->irq == muic_irqs[i].virq) irq_type = muic_irqs[i].irq; @@ -1188,7 +1188,7 @@ static int max77693_muic_probe(struct platform_device *pdev) num_init_data = ARRAY_SIZE(default_init_data); } - for (i = 0 ; i < num_init_data ; i++) { + for (i = 0; i < num_init_data; i++) { enum max77693_irq_source irq_src = MAX77693_IRQ_GROUP_NR; -- cgit v0.10.2 From a75e1c73a46eed0332d036e371f714e76d167c07 Mon Sep 17 00:00:00 2001 From: Chanwoo Choi Date: Sat, 31 Aug 2013 13:16:49 +0900 Subject: extcon: Fix indentation coding style to improve readability Signed-off-by: Chanwoo Choi Signed-off-by: Myungjoo Ham diff --git a/drivers/extcon/extcon-adc-jack.c b/drivers/extcon/extcon-adc-jack.c index 5985807..5d16428 100644 --- a/drivers/extcon/extcon-adc-jack.c +++ b/drivers/extcon/extcon-adc-jack.c @@ -27,16 +27,16 @@ /** * struct adc_jack_data - internal data for adc_jack device driver - * @edev - extcon device. - * @cable_names - list of supported cables. - * @num_cables - size of cable_names. - * @adc_conditions - list of adc value conditions. - * @num_conditions - size of adc_conditions. - * @irq - irq number of attach/detach event (0 if not exist). - * @handling_delay - interrupt handler will schedule extcon event - * handling at handling_delay jiffies. - * @handler - extcon event handler called by interrupt handler. - * @chan - iio channel being queried. + * @edev: extcon device. + * @cable_names: list of supported cables. + * @num_cables: size of cable_names. + * @adc_conditions: list of adc value conditions. + * @num_conditions: size of adc_conditions. + * @irq: irq number of attach/detach event (0 if not exist). + * @handling_delay: interrupt handler will schedule extcon event + * handling at handling_delay jiffies. + * @handler: extcon event handler called by interrupt handler. + * @chan: iio channel being queried. */ struct adc_jack_data { struct extcon_dev edev; diff --git a/drivers/extcon/extcon-class.c b/drivers/extcon/extcon-class.c index 148382f..a297208 100644 --- a/drivers/extcon/extcon-class.c +++ b/drivers/extcon/extcon-class.c @@ -74,7 +74,7 @@ static DEFINE_MUTEX(extcon_dev_list_lock); /** * check_mutually_exclusive - Check if new_state violates mutually_exclusive - * condition. + * condition. * @edev: the extcon device * @new_state: new cable attach status for @edev * @@ -189,7 +189,7 @@ static ssize_t cable_state_show(struct device *dev, /** * extcon_update_state() - Update the cable attach states of the extcon device - * only for the masked bits. + * only for the masked bits. * @edev: the extcon device * @mask: the bit mask to designate updated bits. * @state: new cable attach status for @edev @@ -227,7 +227,6 @@ int extcon_update_state(struct extcon_dev *edev, u32 mask, u32 state) edev->state |= state & mask; raw_notifier_call_chain(&edev->nh, old_state, edev); - /* This could be in interrupt handler */ prop_buf = (char *)get_zeroed_page(GFP_ATOMIC); if (prop_buf) { @@ -339,8 +338,9 @@ EXPORT_SYMBOL_GPL(extcon_get_cable_state); /** * extcon_set_cable_state_() - Set the status of a specific cable. - * @edev: the extcon device that has the cable. - * @index: cable index that can be retrieved by extcon_find_cable_index(). + * @edev: the extcon device that has the cable. + * @index: cable index that can be retrieved by + * extcon_find_cable_index(). * @cable_state: the new cable status. The default semantics is * true: attached / false: detached. */ @@ -359,8 +359,8 @@ EXPORT_SYMBOL_GPL(extcon_set_cable_state_); /** * extcon_set_cable_state() - Set the status of a specific cable. - * @edev: the extcon device that has the cable. - * @cable_name: cable name. + * @edev: the extcon device that has the cable. + * @cable_name: cable name. * @cable_state: the new cable status. The default semantics is * true: attached / false: detached. * @@ -419,14 +419,14 @@ static int _call_per_cable(struct notifier_block *nb, unsigned long val, /** * extcon_register_interest() - Register a notifier for a state change of a - * specific cable, not an entier set of cables of a - * extcon device. - * @obj: an empty extcon_specific_cable_nb object to be returned. + * specific cable, not an entier set of cables of a + * extcon device. + * @obj: an empty extcon_specific_cable_nb object to be returned. * @extcon_name: the name of extcon device. * if NULL, extcon_register_interest will register * every cable with the target cable_name given. * @cable_name: the target cable name. - * @nb: the notifier block to get notified. + * @nb: the notifier block to get notified. * * Provide an empty extcon_specific_cable_nb. extcon_register_interest() sets * the struct for you. @@ -487,7 +487,7 @@ EXPORT_SYMBOL_GPL(extcon_register_interest); /** * extcon_unregister_interest() - Unregister the notifier registered by - * extcon_register_interest(). + * extcon_register_interest(). * @obj: the extcon_specific_cable_nb object returned by * extcon_register_interest(). */ @@ -502,7 +502,7 @@ EXPORT_SYMBOL_GPL(extcon_unregister_interest); /** * extcon_register_notifier() - Register a notifiee to get notified by - * any attach status changes from the extcon. + * any attach status changes from the extcon. * @edev: the extcon device. * @nb: a notifier block to be registered. * diff --git a/include/linux/extcon.h b/include/linux/extcon.h index fcb51c8..c2b652dd 100644 --- a/include/linux/extcon.h +++ b/include/linux/extcon.h @@ -51,10 +51,10 @@ enum extcon_cable_name { EXTCON_USB = 0, EXTCON_USB_HOST, - EXTCON_TA, /* Travel Adaptor */ + EXTCON_TA, /* Travel Adaptor */ EXTCON_FAST_CHARGER, EXTCON_SLOW_CHARGER, - EXTCON_CHARGE_DOWNSTREAM, /* Charging an external device */ + EXTCON_CHARGE_DOWNSTREAM, /* Charging an external device */ EXTCON_HDMI, EXTCON_MHL, EXTCON_DVI, @@ -76,8 +76,8 @@ struct extcon_cable; /** * struct extcon_dev - An extcon device represents one external connector. - * @name: The name of this extcon device. Parent device name is used - * if NULL. + * @name: The name of this extcon device. Parent device name is + * used if NULL. * @supported_cable: Array of supported cable names ending with NULL. * If supported_cable is NULL, cable name related APIs * are disabled. @@ -89,21 +89,21 @@ struct extcon_cable; * be attached simulataneously. {0x7, 0} is equivalent to * {0x3, 0x6, 0x5, 0}. If it is {0xFFFFFFFF, 0}, there * can be no simultaneous connections. - * @print_name: An optional callback to override the method to print the - * name of the extcon device. + * @print_name: An optional callback to override the method to print the + * name of the extcon device. * @print_state: An optional callback to override the method to print the - * status of the extcon device. - * @dev: Device of this extcon. Do not provide at register-time. - * @state: Attach/detach state of this extcon. Do not provide at - * register-time - * @nh: Notifier for the state change events from this extcon - * @entry: To support list of extcon devices so that users can search - * for extcon devices based on the extcon name. + * status of the extcon device. + * @dev: Device of this extcon. Do not provide at register-time. + * @state: Attach/detach state of this extcon. Do not provide at + * register-time. + * @nh: Notifier for the state change events from this extcon + * @entry: To support list of extcon devices so that users can search + * for extcon devices based on the extcon name. * @lock: * @max_supported: Internal value to store the number of cables. * @extcon_dev_type: Device_type struct to provide attribute_groups * customized for each extcon device. - * @cables: Sysfs subdirectories. Each represents one cable. + * @cables: Sysfs subdirectories. Each represents one cable. * * In most cases, users only need to provide "User initializing data" of * this struct when registering an extcon. In some exceptional cases, @@ -111,26 +111,27 @@ struct extcon_cable; * are overwritten by register function. */ struct extcon_dev { - /* --- Optional user initializing data --- */ - const char *name; + /* Optional user initializing data */ + const char *name; const char **supported_cable; - const u32 *mutually_exclusive; + const u32 *mutually_exclusive; - /* --- Optional callbacks to override class functions --- */ + /* Optional callbacks to override class functions */ ssize_t (*print_name)(struct extcon_dev *edev, char *buf); ssize_t (*print_state)(struct extcon_dev *edev, char *buf); - /* --- Internal data. Please do not set. --- */ - struct device *dev; - u32 state; + /* Internal data. Please do not set. */ + struct device *dev; struct raw_notifier_head nh; struct list_head entry; - spinlock_t lock; /* could be called by irq handler */ int max_supported; + spinlock_t lock; /* could be called by irq handler */ + u32 state; /* /sys/class/extcon/.../cable.n/... */ struct device_type extcon_dev_type; struct extcon_cable *cables; + /* /sys/class/extcon/.../mutually_exclusive/... */ struct attribute_group attr_g_muex; struct attribute **attrs_muex; @@ -138,13 +139,13 @@ struct extcon_dev { }; /** - * struct extcon_cable - An internal data for each cable of extcon device. - * @edev: The extcon device + * struct extcon_cable - An internal data for each cable of extcon device. + * @edev: The extcon device * @cable_index: Index of this cable in the edev - * @attr_g: Attribute group for the cable - * @attr_name: "name" sysfs entry - * @attr_state: "state" sysfs entry - * @attrs: Array pointing to attr_name and attr_state for attr_g + * @attr_g: Attribute group for the cable + * @attr_name: "name" sysfs entry + * @attr_state: "state" sysfs entry + * @attrs: Array pointing to attr_name and attr_state for attr_g */ struct extcon_cable { struct extcon_dev *edev; @@ -159,11 +160,13 @@ struct extcon_cable { /** * struct extcon_specific_cable_nb - An internal data for - * extcon_register_interest(). - * @internal_nb: a notifier block bridging extcon notifier and cable notifier. - * @user_nb: user provided notifier block for events from a specific cable. + * extcon_register_interest(). + * @internal_nb: A notifier block bridging extcon notifier + * and cable notifier. + * @user_nb: user provided notifier block for events from + * a specific cable. * @cable_index: the target cable. - * @edev: the target extcon device. + * @edev: the target extcon device. * @previous_value: the saved previous event value. */ struct extcon_specific_cable_nb { diff --git a/include/linux/extcon/extcon-adc-jack.h b/include/linux/extcon/extcon-adc-jack.h index 20e9eef..9ca958c 100644 --- a/include/linux/extcon/extcon-adc-jack.h +++ b/include/linux/extcon/extcon-adc-jack.h @@ -20,10 +20,10 @@ /** * struct adc_jack_cond - condition to use an extcon state - * @state - the corresponding extcon state (if 0, this struct denotes - * the last adc_jack_cond element among the array) - * @min_adc - min adc value for this condition - * @max_adc - max adc value for this condition + * @state: the corresponding extcon state (if 0, this struct + * denotes the last adc_jack_cond element among the array) + * @min_adc: min adc value for this condition + * @max_adc: max adc value for this condition * * For example, if { .state = 0x3, .min_adc = 100, .max_adc = 200}, it means * that if ADC value is between (inclusive) 100 and 200, than the cable 0 and @@ -33,34 +33,34 @@ * because when no adc_jack_cond is met, state = 0 is automatically chosen. */ struct adc_jack_cond { - u32 state; /* extcon state value. 0 if invalid */ + u32 state; /* extcon state value. 0 if invalid */ u32 min_adc; u32 max_adc; }; /** * struct adc_jack_pdata - platform data for adc jack device. - * @name - name of the extcon device. If null, "adc-jack" is used. - * @consumer_channel - Unique name to identify the channel on the consumer - * side. This typically describes the channels used within - * the consumer. E.g. 'battery_voltage' - * @cable_names - array of cable names ending with null. - * @adc_contitions - array of struct adc_jack_cond conditions ending - * with .state = 0 entry. This describes how to decode - * adc values into extcon state. - * @irq_flags - irq flags used for the @irq - * @handling_delay_ms - in some devices, we need to read ADC value some - * milli-seconds after the interrupt occurs. You may - * describe such delays with @handling_delay_ms, which - * is rounded-off by jiffies. + * @name: name of the extcon device. If null, "adc-jack" is used. + * @consumer_channel: Unique name to identify the channel on the consumer + * side. This typically describes the channels used within + * the consumer. E.g. 'battery_voltage' + * @cable_names: array of cable names ending with null. + * @adc_contitions: array of struct adc_jack_cond conditions ending + * with .state = 0 entry. This describes how to decode + * adc values into extcon state. + * @irq_flags: irq flags used for the @irq + * @handling_delay_ms: in some devices, we need to read ADC value some + * milli-seconds after the interrupt occurs. You may + * describe such delays with @handling_delay_ms, which + * is rounded-off by jiffies. */ struct adc_jack_pdata { const char *name; const char *consumer_channel; - /* - * The last entry should be NULL - */ + + /* The last entry should be NULL */ const char **cable_names; + /* The last entry's state should be 0 */ struct adc_jack_cond *adc_conditions; diff --git a/include/linux/extcon/extcon-gpio.h b/include/linux/extcon/extcon-gpio.h index 2d8307f..4ce1aa7 100644 --- a/include/linux/extcon/extcon-gpio.h +++ b/include/linux/extcon/extcon-gpio.h @@ -25,14 +25,14 @@ /** * struct gpio_extcon_platform_data - A simple GPIO-controlled extcon device. - * @name The name of this GPIO extcon device. - * @gpio Corresponding GPIO. - * @debounce Debounce time for GPIO IRQ in ms. - * @irq_flags IRQ Flags (e.g., IRQF_TRIGGER_LOW). - * @state_on print_state is overriden with state_on if attached. If Null, - * default method of extcon class is used. - * @state_off print_state is overriden with state_on if detached. If Null, - * default method of extcon class is used. + * @name: The name of this GPIO extcon device. + * @gpio: Corresponding GPIO. + * @debounce: Debounce time for GPIO IRQ in ms. + * @irq_flags: IRQ Flags (e.g., IRQF_TRIGGER_LOW). + * @state_on: print_state is overriden with state_on if attached. + * If NULL, default method of extcon class is used. + * @state_off: print_state is overriden with state_on if detached. + * If NUll, default method of extcon class is used. * * Note that in order for state_on or state_off to be valid, both state_on * and state_off should be not NULL. If at least one of them is NULL, -- cgit v0.10.2 From cb8bb3a77252bd431997a61456689c778510447c Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Mon, 9 Sep 2013 14:33:32 +0900 Subject: extcon: Remove casting the return value which is a void pointer Casting the return value which is a void pointer is redundant. The conversion from void pointer to any other pointer type is guaranteed by the C programming language. Signed-off-by: Jingoo Han Signed-off-by: Chanwoo Choi diff --git a/drivers/extcon/extcon-class.c b/drivers/extcon/extcon-class.c index a297208..2f2d374 100644 --- a/drivers/extcon/extcon-class.c +++ b/drivers/extcon/extcon-class.c @@ -105,7 +105,7 @@ static ssize_t state_show(struct device *dev, struct device_attribute *attr, char *buf) { int i, count = 0; - struct extcon_dev *edev = (struct extcon_dev *) dev_get_drvdata(dev); + struct extcon_dev *edev = dev_get_drvdata(dev); if (edev->print_state) { int ret = edev->print_state(edev, buf); @@ -135,7 +135,7 @@ static ssize_t state_store(struct device *dev, struct device_attribute *attr, { u32 state; ssize_t ret = 0; - struct extcon_dev *edev = (struct extcon_dev *) dev_get_drvdata(dev); + struct extcon_dev *edev = dev_get_drvdata(dev); ret = sscanf(buf, "0x%x", &state); if (ret == 0) @@ -153,7 +153,7 @@ static DEVICE_ATTR_RW(state); static ssize_t name_show(struct device *dev, struct device_attribute *attr, char *buf) { - struct extcon_dev *edev = (struct extcon_dev *) dev_get_drvdata(dev); + struct extcon_dev *edev = dev_get_drvdata(dev); /* Optional callback given by the user */ if (edev->print_name) { @@ -470,7 +470,7 @@ int extcon_register_interest(struct extcon_specific_cable_nb *obj, return -ENODEV; class_dev_iter_init(&iter, extcon_class, NULL, NULL); while ((dev = class_dev_iter_next(&iter))) { - extd = (struct extcon_dev *)dev_get_drvdata(dev); + extd = dev_get_drvdata(dev); if (extcon_find_cable_index(extd, cable_name) < 0) continue; -- cgit v0.10.2 From 1073514b11c221081c7c1f4a14b3eee5365385fa Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Thu, 29 Aug 2013 21:29:33 -0700 Subject: extcon: gpio: Do not unnecessarily initialize variables Signed-off-by: Guenter Roeck Signed-off-by: Chanwoo Choi diff --git a/drivers/extcon/extcon-gpio.c b/drivers/extcon/extcon-gpio.c index f874c30..6368a0f 100644 --- a/drivers/extcon/extcon-gpio.c +++ b/drivers/extcon/extcon-gpio.c @@ -80,7 +80,7 @@ static int gpio_extcon_probe(struct platform_device *pdev) { struct gpio_extcon_platform_data *pdata = pdev->dev.platform_data; struct gpio_extcon_data *extcon_data; - int ret = 0; + int ret; if (!pdata) return -EBUSY; -- cgit v0.10.2 From 338de0ca682ca95d3f6185459e11540c37dd8a2a Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Tue, 10 Sep 2013 19:16:18 -0700 Subject: extcon: gpio: Use gpio driver/chip debounce if supported This patch use gpio_set_debounce() API provided from gpiolib if SoC or device driver with gpio support gpio_set_debounce() function. Signed-off-by: Guenter Roeck Signed-off-by: Chanwoo Choi diff --git a/drivers/extcon/extcon-gpio.c b/drivers/extcon/extcon-gpio.c index 6368a0f..862743b 100644 --- a/drivers/extcon/extcon-gpio.c +++ b/drivers/extcon/extcon-gpio.c @@ -100,7 +100,13 @@ static int gpio_extcon_probe(struct platform_device *pdev) extcon_data->state_off = pdata->state_off; if (pdata->state_on && pdata->state_off) extcon_data->edev.print_state = extcon_gpio_print_state; - extcon_data->debounce_jiffies = msecs_to_jiffies(pdata->debounce); + if (pdata->debounce) { + ret = gpio_set_debounce(extcon_data->gpio, + pdata->debounce * 1000); + if (ret < 0) + extcon_data->debounce_jiffies = + msecs_to_jiffies(pdata->debounce); + } ret = extcon_dev_register(&extcon_data->edev, &pdev->dev); if (ret < 0) -- cgit v0.10.2 From 7c0f6558f893eb5eddb52ee0fa84c2d43b2c199a Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Wed, 11 Sep 2013 13:22:18 +0900 Subject: extcon: use dev_get_platdata() Use the wrapper function for retrieving the platform data instead of accessing dev->platform_data directly. This is a cosmetic change to make the code simpler and enhance the readability. Signed-off-by: Jingoo Han Signed-off-by: Chanwoo Choi diff --git a/drivers/extcon/extcon-adc-jack.c b/drivers/extcon/extcon-adc-jack.c index 5d16428..5694c46 100644 --- a/drivers/extcon/extcon-adc-jack.c +++ b/drivers/extcon/extcon-adc-jack.c @@ -95,7 +95,7 @@ static irqreturn_t adc_jack_irq_thread(int irq, void *_data) static int adc_jack_probe(struct platform_device *pdev) { struct adc_jack_data *data; - struct adc_jack_pdata *pdata = pdev->dev.platform_data; + struct adc_jack_pdata *pdata = dev_get_platdata(&pdev->dev); int i, err = 0; data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); diff --git a/drivers/extcon/extcon-gpio.c b/drivers/extcon/extcon-gpio.c index 862743b..cd7dbb60 100644 --- a/drivers/extcon/extcon-gpio.c +++ b/drivers/extcon/extcon-gpio.c @@ -78,7 +78,7 @@ static ssize_t extcon_gpio_print_state(struct extcon_dev *edev, char *buf) static int gpio_extcon_probe(struct platform_device *pdev) { - struct gpio_extcon_platform_data *pdata = pdev->dev.platform_data; + struct gpio_extcon_platform_data *pdata = dev_get_platdata(&pdev->dev); struct gpio_extcon_data *extcon_data; int ret; diff --git a/drivers/extcon/extcon-palmas.c b/drivers/extcon/extcon-palmas.c index 89fdd05..0c2fb68 100644 --- a/drivers/extcon/extcon-palmas.c +++ b/drivers/extcon/extcon-palmas.c @@ -135,7 +135,7 @@ static void palmas_enable_irq(struct palmas_usb *palmas_usb) static int palmas_usb_probe(struct platform_device *pdev) { struct palmas *palmas = dev_get_drvdata(pdev->dev.parent); - struct palmas_usb_platform_data *pdata = pdev->dev.platform_data; + struct palmas_usb_platform_data *pdata = dev_get_platdata(&pdev->dev); struct device_node *node = pdev->dev.of_node; struct palmas_usb *palmas_usb; int status; -- cgit v0.10.2 From 5bfbdc9caa7e16b2a77a62a9f9a63b5693e23716 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Thu, 12 Sep 2013 08:49:54 +0900 Subject: extcon: gpio: Add support for active-low presence to detect pins This patch add 'gpio_active_low' field to 'struct gpio_extcon_data' to check whether gpio active state is 1(high) or 0(low). Signed-off-by: Guenter Roeck Signed-off-by: Chanwoo Choi diff --git a/drivers/extcon/extcon-gpio.c b/drivers/extcon/extcon-gpio.c index cd7dbb60..b02c670e 100644 --- a/drivers/extcon/extcon-gpio.c +++ b/drivers/extcon/extcon-gpio.c @@ -34,6 +34,7 @@ struct gpio_extcon_data { struct extcon_dev edev; unsigned gpio; + bool gpio_active_low; const char *state_on; const char *state_off; int irq; @@ -49,6 +50,8 @@ static void gpio_extcon_work(struct work_struct *work) work); state = gpio_get_value(data->gpio); + if (data->gpio_active_low) + state = !state; extcon_set_state(&data->edev, state); } @@ -96,6 +99,7 @@ static int gpio_extcon_probe(struct platform_device *pdev) extcon_data->edev.name = pdata->name; extcon_data->gpio = pdata->gpio; + extcon_data->gpio_active_low = pdata->gpio_active_low; extcon_data->state_on = pdata->state_on; extcon_data->state_off = pdata->state_off; if (pdata->state_on && pdata->state_off) diff --git a/include/linux/extcon/extcon-gpio.h b/include/linux/extcon/extcon-gpio.h index 4ce1aa7..4195810 100644 --- a/include/linux/extcon/extcon-gpio.h +++ b/include/linux/extcon/extcon-gpio.h @@ -27,6 +27,9 @@ * struct gpio_extcon_platform_data - A simple GPIO-controlled extcon device. * @name: The name of this GPIO extcon device. * @gpio: Corresponding GPIO. + * @gpio_active_low: Boolean describing whether gpio active state is 1 or 0 + * If true, low state of gpio means active. + * If false, high state of gpio means active. * @debounce: Debounce time for GPIO IRQ in ms. * @irq_flags: IRQ Flags (e.g., IRQF_TRIGGER_LOW). * @state_on: print_state is overriden with state_on if attached. @@ -41,6 +44,7 @@ struct gpio_extcon_platform_data { const char *name; unsigned gpio; + bool gpio_active_low; unsigned long debounce; unsigned long irq_flags; -- cgit v0.10.2 From 4ba1a9ff5955df443a0ed6c956501f796a18abfa Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Mon, 23 Sep 2013 14:33:58 +0100 Subject: extcon: arizona: Correct typo in headphone detect range transitions We should move range when the measured value is greater than or equal to the max value not when greater than. Signed-off-by: Charles Keepax Signed-off-by: Chanwoo Choi diff --git a/drivers/extcon/extcon-arizona.c b/drivers/extcon/extcon-arizona.c index 2064eac..1878415 100644 --- a/drivers/extcon/extcon-arizona.c +++ b/drivers/extcon/extcon-arizona.c @@ -388,7 +388,7 @@ static int arizona_hpdet_read(struct arizona_extcon_info *info) >> ARIZONA_HP_IMPEDANCE_RANGE_SHIFT; if (range < ARRAY_SIZE(arizona_hpdet_b_ranges) - 1 && - (val < 100 || val > 0x3fb)) { + (val < 100 || val >= 0x3fb)) { range++; dev_dbg(arizona->dev, "Moving to HPDET range %d\n", range); @@ -401,7 +401,7 @@ static int arizona_hpdet_read(struct arizona_extcon_info *info) } /* If we go out of range report top of range */ - if (val < 100 || val > 0x3fb) { + if (val < 100 || val >= 0x3fb) { dev_dbg(arizona->dev, "Measurement out of range\n"); return ARIZONA_HPDET_MAX; } -- cgit v0.10.2 From 4102424302b313516d11a325e2ba614deec526a2 Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Mon, 23 Sep 2013 14:33:59 +0100 Subject: extcon: arizona: Don't require micbias to be shifted in pdata Every other pdata field is specified unshifted the patch handles shifting for the MICBIAS from the microphone detection polarity configurations in the extcon driver rather than demanding it in pdata to match other fields. Signed-off-by: Charles Keepax Signed-off-by: Chanwoo Choi diff --git a/drivers/extcon/extcon-arizona.c b/drivers/extcon/extcon-arizona.c index 1878415..e54ce08 100644 --- a/drivers/extcon/extcon-arizona.c +++ b/drivers/extcon/extcon-arizona.c @@ -86,8 +86,8 @@ struct arizona_extcon_info { }; static const struct arizona_micd_config micd_default_modes[] = { - { ARIZONA_ACCDET_SRC, 1 << ARIZONA_MICD_BIAS_SRC_SHIFT, 0 }, - { 0, 2 << ARIZONA_MICD_BIAS_SRC_SHIFT, 1 }, + { ARIZONA_ACCDET_SRC, 1, 0 }, + { 0, 2, 1 }, }; static const struct arizona_micd_range micd_default_ranges[] = { @@ -182,7 +182,8 @@ static void arizona_extcon_set_mode(struct arizona_extcon_info *info, int mode) info->micd_modes[mode].gpio); regmap_update_bits(arizona->regmap, ARIZONA_MIC_DETECT_1, ARIZONA_MICD_BIAS_SRC_MASK, - info->micd_modes[mode].bias); + info->micd_modes[mode].bias << + ARIZONA_MICD_BIAS_SRC_SHIFT); regmap_update_bits(arizona->regmap, ARIZONA_ACCESSORY_DETECT_MODE_1, ARIZONA_ACCDET_SRC, info->micd_modes[mode].src); @@ -193,7 +194,7 @@ static void arizona_extcon_set_mode(struct arizona_extcon_info *info, int mode) static const char *arizona_extcon_get_micbias(struct arizona_extcon_info *info) { - switch (info->micd_modes[0].bias >> ARIZONA_MICD_BIAS_SRC_SHIFT) { + switch (info->micd_modes[0].bias) { case 1: return "MICBIAS1"; case 2: -- cgit v0.10.2 From dae616512476024aa61d2a598461ab6eff8c0709 Mon Sep 17 00:00:00 2001 From: Chanwoo Choi Date: Fri, 27 Sep 2013 09:19:40 +0900 Subject: extcon: Change field type of 'dev' in extcon_dev structure The extcon device must always need 'struct device' so this patch change field type of 'dev' instead of allocating memory for 'struct device' on extcon_dev_register() function. Signed-off-by: Chanwoo Choi Signed-off-by: Myungjoo Ham diff --git a/drivers/extcon/extcon-adc-jack.c b/drivers/extcon/extcon-adc-jack.c index 5694c46..dcbade6 100644 --- a/drivers/extcon/extcon-adc-jack.c +++ b/drivers/extcon/extcon-adc-jack.c @@ -64,7 +64,7 @@ static void adc_jack_handler(struct work_struct *work) ret = iio_read_channel_raw(data->chan, &adc_val); if (ret < 0) { - dev_err(data->edev.dev, "read channel() error: %d\n", ret); + dev_err(&data->edev.dev, "read channel() error: %d\n", ret); return; } diff --git a/drivers/extcon/extcon-class.c b/drivers/extcon/extcon-class.c index 2f2d374..2801c14 100644 --- a/drivers/extcon/extcon-class.c +++ b/drivers/extcon/extcon-class.c @@ -162,7 +162,7 @@ static ssize_t name_show(struct device *dev, struct device_attribute *attr, return ret; } - return sprintf(buf, "%s\n", dev_name(edev->dev)); + return sprintf(buf, "%s\n", dev_name(&edev->dev)); } static DEVICE_ATTR_RO(name); @@ -230,7 +230,7 @@ int extcon_update_state(struct extcon_dev *edev, u32 mask, u32 state) /* This could be in interrupt handler */ prop_buf = (char *)get_zeroed_page(GFP_ATOMIC); if (prop_buf) { - length = name_show(edev->dev, NULL, prop_buf); + length = name_show(&edev->dev, NULL, prop_buf); if (length > 0) { if (prop_buf[length - 1] == '\n') prop_buf[length - 1] = 0; @@ -238,7 +238,7 @@ int extcon_update_state(struct extcon_dev *edev, u32 mask, u32 state) "NAME=%s", prop_buf); envp[env_offset++] = name_buf; } - length = state_show(edev->dev, NULL, prop_buf); + length = state_show(&edev->dev, NULL, prop_buf); if (length > 0) { if (prop_buf[length - 1] == '\n') prop_buf[length - 1] = 0; @@ -250,14 +250,14 @@ int extcon_update_state(struct extcon_dev *edev, u32 mask, u32 state) /* Unlock early before uevent */ spin_unlock_irqrestore(&edev->lock, flags); - kobject_uevent_env(&edev->dev->kobj, KOBJ_CHANGE, envp); + kobject_uevent_env(&edev->dev.kobj, KOBJ_CHANGE, envp); free_page((unsigned long)prop_buf); } else { /* Unlock early before uevent */ spin_unlock_irqrestore(&edev->lock, flags); - dev_err(edev->dev, "out of memory in extcon_set_state\n"); - kobject_uevent(&edev->dev->kobj, KOBJ_CHANGE); + dev_err(&edev->dev, "out of memory in extcon_set_state\n"); + kobject_uevent(&edev->dev.kobj, KOBJ_CHANGE); } } else { /* No changes */ @@ -556,7 +556,6 @@ static int create_extcon_class(void) static void extcon_dev_release(struct device *dev) { - kfree(dev); } static const char *muex_name = "mutually_exclusive"; @@ -594,19 +593,16 @@ int extcon_dev_register(struct extcon_dev *edev, struct device *dev) } if (index > SUPPORTED_CABLE_MAX) { - dev_err(edev->dev, "extcon: maximum number of supported cables exceeded.\n"); + dev_err(&edev->dev, "extcon: maximum number of supported cables exceeded.\n"); return -EINVAL; } - edev->dev = kzalloc(sizeof(struct device), GFP_KERNEL); - if (!edev->dev) - return -ENOMEM; - edev->dev->parent = dev; - edev->dev->class = extcon_class; - edev->dev->release = extcon_dev_release; + edev->dev.parent = dev; + edev->dev.class = extcon_class; + edev->dev.release = extcon_dev_release; edev->name = edev->name ? edev->name : dev_name(dev); - dev_set_name(edev->dev, "%s", edev->name); + dev_set_name(&edev->dev, "%s", edev->name); if (edev->max_supported) { char buf[10]; @@ -714,7 +710,7 @@ int extcon_dev_register(struct extcon_dev *edev, struct device *dev) goto err_alloc_groups; } - edev->extcon_dev_type.name = dev_name(edev->dev); + edev->extcon_dev_type.name = dev_name(&edev->dev); edev->extcon_dev_type.release = dummy_sysfs_dev_release; for (index = 0; index < edev->max_supported; index++) @@ -724,25 +720,24 @@ int extcon_dev_register(struct extcon_dev *edev, struct device *dev) edev->extcon_dev_type.groups[index] = &edev->attr_g_muex; - edev->dev->type = &edev->extcon_dev_type; + edev->dev.type = &edev->extcon_dev_type; } - ret = device_register(edev->dev); + ret = device_register(&edev->dev); if (ret) { - put_device(edev->dev); + put_device(&edev->dev); goto err_dev; } #if defined(CONFIG_ANDROID) if (switch_class) - ret = class_compat_create_link(switch_class, edev->dev, - NULL); + ret = class_compat_create_link(switch_class, &edev->dev, NULL); #endif /* CONFIG_ANDROID */ spin_lock_init(&edev->lock); RAW_INIT_NOTIFIER_HEAD(&edev->nh); - dev_set_drvdata(edev->dev, edev); + dev_set_drvdata(&edev->dev, edev); edev->state = 0; mutex_lock(&extcon_dev_list_lock); @@ -768,7 +763,6 @@ err_alloc_cables: if (edev->max_supported) kfree(edev->cables); err_sysfs_alloc: - kfree(edev->dev); return ret; } EXPORT_SYMBOL_GPL(extcon_dev_register); @@ -788,9 +782,9 @@ void extcon_dev_unregister(struct extcon_dev *edev) list_del(&edev->entry); mutex_unlock(&extcon_dev_list_lock); - if (IS_ERR_OR_NULL(get_device(edev->dev))) { - dev_err(edev->dev, "Failed to unregister extcon_dev (%s)\n", - dev_name(edev->dev)); + if (IS_ERR_OR_NULL(get_device(&edev->dev))) { + dev_err(&edev->dev, "Failed to unregister extcon_dev (%s)\n", + dev_name(&edev->dev)); return; } @@ -812,10 +806,10 @@ void extcon_dev_unregister(struct extcon_dev *edev) #if defined(CONFIG_ANDROID) if (switch_class) - class_compat_remove_link(switch_class, edev->dev, NULL); + class_compat_remove_link(switch_class, &edev->dev, NULL); #endif - device_unregister(edev->dev); - put_device(edev->dev); + device_unregister(&edev->dev); + put_device(&edev->dev); } EXPORT_SYMBOL_GPL(extcon_dev_unregister); diff --git a/include/linux/extcon.h b/include/linux/extcon.h index c2b652dd..0269baf 100644 --- a/include/linux/extcon.h +++ b/include/linux/extcon.h @@ -93,7 +93,7 @@ struct extcon_cable; * name of the extcon device. * @print_state: An optional callback to override the method to print the * status of the extcon device. - * @dev: Device of this extcon. Do not provide at register-time. + * @dev: Device of this extcon. * @state: Attach/detach state of this extcon. Do not provide at * register-time. * @nh: Notifier for the state change events from this extcon @@ -121,7 +121,7 @@ struct extcon_dev { ssize_t (*print_state)(struct extcon_dev *edev, char *buf); /* Internal data. Please do not set. */ - struct device *dev; + struct device dev; struct raw_notifier_head nh; struct list_head entry; int max_supported; -- cgit v0.10.2 From 42d7d7539a7bcf1d493b989465283c464f4a0525 Mon Sep 17 00:00:00 2001 From: Chanwoo Choi Date: Fri, 27 Sep 2013 09:20:26 +0900 Subject: extcon: Simplify extcon_dev_register() prototype by removing unnecessary parameter This patch remove extcon_dev_register()'s second parameter which means the pointer of parent device to simplify prototype of this function. So, if extcon device has the parent device, it should set the pointer of parent device to edev.dev.parent in extcon device driver instead of in extcon_dev_register(). Cc: Graeme Gregory Cc: Kishon Vijay Abraham I Cc: Charles Keepax Cc: Mark Brown Signed-off-by: Chanwoo Choi Signed-off-by: Myungjoo Ham diff --git a/Documentation/extcon/porting-android-switch-class b/Documentation/extcon/porting-android-switch-class index eb0fa5f..5377f63 100644 --- a/Documentation/extcon/porting-android-switch-class +++ b/Documentation/extcon/porting-android-switch-class @@ -25,8 +25,10 @@ MyungJoo Ham @print_state: no change but type change (switch_dev->extcon_dev) - switch_dev_register(sdev, dev) - => extcon_dev_register(edev, dev) - : no change but type change (sdev->edev) + => extcon_dev_register(edev) + : type change (sdev->edev) + : remove second param('dev'). if edev has parent device, should store + 'dev' to 'edev.dev.parent' before registering extcon device - switch_dev_unregister(sdev) => extcon_dev_unregister(edev) : no change but type change (sdev->edev) diff --git a/drivers/extcon/extcon-adc-jack.c b/drivers/extcon/extcon-adc-jack.c index dcbade6..e23f1c2 100644 --- a/drivers/extcon/extcon-adc-jack.c +++ b/drivers/extcon/extcon-adc-jack.c @@ -110,6 +110,7 @@ static int adc_jack_probe(struct platform_device *pdev) goto out; } + data->edev.dev.parent = &pdev->dev; data->edev.supported_cable = pdata->cable_names; /* Check the length of array and set num_cables */ @@ -148,7 +149,7 @@ static int adc_jack_probe(struct platform_device *pdev) platform_set_drvdata(pdev, data); - err = extcon_dev_register(&data->edev, &pdev->dev); + err = extcon_dev_register(&data->edev); if (err) goto out; diff --git a/drivers/extcon/extcon-arizona.c b/drivers/extcon/extcon-arizona.c index e54ce08..ec9a14e 100644 --- a/drivers/extcon/extcon-arizona.c +++ b/drivers/extcon/extcon-arizona.c @@ -1130,9 +1130,10 @@ static int arizona_extcon_probe(struct platform_device *pdev) } info->edev.name = "Headset Jack"; + info->edev.dev.parent = arizona->dev; info->edev.supported_cable = arizona_cable; - ret = extcon_dev_register(&info->edev, arizona->dev); + ret = extcon_dev_register(&info->edev); if (ret < 0) { dev_err(arizona->dev, "extcon_dev_register() failed: %d\n", ret); diff --git a/drivers/extcon/extcon-class.c b/drivers/extcon/extcon-class.c index 2801c14..84902d3 100644 --- a/drivers/extcon/extcon-class.c +++ b/drivers/extcon/extcon-class.c @@ -566,14 +566,13 @@ static void dummy_sysfs_dev_release(struct device *dev) /** * extcon_dev_register() - Register a new extcon device * @edev : the new extcon device (should be allocated before calling) - * @dev : the parent device for this extcon device. * * Among the members of edev struct, please set the "user initializing data" * in any case and set the "optional callbacks" if required. However, please * do not set the values of "internal data", which are initialized by * this function. */ -int extcon_dev_register(struct extcon_dev *edev, struct device *dev) +int extcon_dev_register(struct extcon_dev *edev) { int ret, index = 0; @@ -597,11 +596,15 @@ int extcon_dev_register(struct extcon_dev *edev, struct device *dev) return -EINVAL; } - edev->dev.parent = dev; edev->dev.class = extcon_class; edev->dev.release = extcon_dev_release; - edev->name = edev->name ? edev->name : dev_name(dev); + edev->name = edev->name ? edev->name : dev_name(edev->dev.parent); + if (IS_ERR_OR_NULL(edev->name)) { + dev_err(&edev->dev, + "extcon device name is null\n"); + return -EINVAL; + } dev_set_name(&edev->dev, "%s", edev->name); if (edev->max_supported) { diff --git a/drivers/extcon/extcon-gpio.c b/drivers/extcon/extcon-gpio.c index b02c670e..7e0dff5 100644 --- a/drivers/extcon/extcon-gpio.c +++ b/drivers/extcon/extcon-gpio.c @@ -98,6 +98,7 @@ static int gpio_extcon_probe(struct platform_device *pdev) return -ENOMEM; extcon_data->edev.name = pdata->name; + extcon_data->edev.dev.parent = &pdev->dev; extcon_data->gpio = pdata->gpio; extcon_data->gpio_active_low = pdata->gpio_active_low; extcon_data->state_on = pdata->state_on; @@ -112,7 +113,7 @@ static int gpio_extcon_probe(struct platform_device *pdev) msecs_to_jiffies(pdata->debounce); } - ret = extcon_dev_register(&extcon_data->edev, &pdev->dev); + ret = extcon_dev_register(&extcon_data->edev); if (ret < 0) return ret; diff --git a/drivers/extcon/extcon-max77693.c b/drivers/extcon/extcon-max77693.c index 4849ea1..ab9bc24 100644 --- a/drivers/extcon/extcon-max77693.c +++ b/drivers/extcon/extcon-max77693.c @@ -1171,8 +1171,9 @@ static int max77693_muic_probe(struct platform_device *pdev) goto err_irq; } info->edev->name = DEV_NAME; + info->edev->dev.parent = &pdev->dev; info->edev->supported_cable = max77693_extcon_cable; - ret = extcon_dev_register(info->edev, NULL); + ret = extcon_dev_register(info->edev); if (ret) { dev_err(&pdev->dev, "failed to register extcon device\n"); goto err_irq; diff --git a/drivers/extcon/extcon-max8997.c b/drivers/extcon/extcon-max8997.c index e55ec38..0b1cbb5 100644 --- a/drivers/extcon/extcon-max8997.c +++ b/drivers/extcon/extcon-max8997.c @@ -705,8 +705,9 @@ static int max8997_muic_probe(struct platform_device *pdev) goto err_irq; } info->edev->name = DEV_NAME; + info->edev->dev.parent = &pdev->dev; info->edev->supported_cable = max8997_extcon_cable; - ret = extcon_dev_register(info->edev, NULL); + ret = extcon_dev_register(info->edev); if (ret) { dev_err(&pdev->dev, "failed to register extcon device\n"); goto err_irq; diff --git a/drivers/extcon/extcon-palmas.c b/drivers/extcon/extcon-palmas.c index 0c2fb68..6c91976 100644 --- a/drivers/extcon/extcon-palmas.c +++ b/drivers/extcon/extcon-palmas.c @@ -178,9 +178,10 @@ static int palmas_usb_probe(struct platform_device *pdev) platform_set_drvdata(pdev, palmas_usb); palmas_usb->edev.supported_cable = palmas_extcon_cable; + palmas_usb->edev.dev.parent = palmas_usb->dev; palmas_usb->edev.mutually_exclusive = mutually_exclusive; - status = extcon_dev_register(&palmas_usb->edev, palmas_usb->dev); + status = extcon_dev_register(&palmas_usb->edev); if (status) { dev_err(&pdev->dev, "failed to register extcon device\n"); return status; diff --git a/include/linux/extcon.h b/include/linux/extcon.h index 0269baf..21c59af 100644 --- a/include/linux/extcon.h +++ b/include/linux/extcon.h @@ -183,7 +183,7 @@ struct extcon_specific_cable_nb { * Following APIs are for notifiers or configurations. * Notifiers are the external port and connection devices. */ -extern int extcon_dev_register(struct extcon_dev *edev, struct device *dev); +extern int extcon_dev_register(struct extcon_dev *edev); extern void extcon_dev_unregister(struct extcon_dev *edev); extern struct extcon_dev *extcon_get_extcon_dev(const char *extcon_name); @@ -241,8 +241,7 @@ extern int extcon_register_notifier(struct extcon_dev *edev, extern int extcon_unregister_notifier(struct extcon_dev *edev, struct notifier_block *nb); #else /* CONFIG_EXTCON */ -static inline int extcon_dev_register(struct extcon_dev *edev, - struct device *dev) +static inline int extcon_dev_register(struct extcon_dev *edev) { return 0; } -- cgit v0.10.2 From 2ce585286d217c1c753bec29ea6f52080a05ced5 Mon Sep 17 00:00:00 2001 From: Sudeep Dutt Date: Fri, 27 Sep 2013 09:49:31 -0700 Subject: misc: mic: host driver sysfs cleanups. + Use DEVICE_ATTR_RO/RW instead of DEVICE_ATTR + Use ATTRIBUTE_GROUPS These changes were suggested by Greg Kroah-Hartman during the code review @ https://lkml.org/lkml/2013/9/6/13 Reported-by: Greg Kroah-Hartman Signed-off-by: Ashutosh Dixit Signed-off-by: Dasaratharaman Chandramouli Signed-off-by: Nikhil Rao Signed-off-by: Harshavardhan R Kharche Signed-off-by: Sudeep Dutt Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/misc/mic/host/mic_sysfs.c b/drivers/misc/mic/host/mic_sysfs.c index aaf8499..bebbae2 100644 --- a/drivers/misc/mic/host/mic_sysfs.c +++ b/drivers/misc/mic/host/mic_sysfs.c @@ -65,7 +65,7 @@ void mic_set_state(struct mic_device *mdev, u8 state) } static ssize_t -mic_show_family(struct device *dev, struct device_attribute *attr, char *buf) +family_show(struct device *dev, struct device_attribute *attr, char *buf) { static const char x100[] = "x100"; static const char unknown[] = "Unknown"; @@ -85,10 +85,10 @@ mic_show_family(struct device *dev, struct device_attribute *attr, char *buf) } return scnprintf(buf, PAGE_SIZE, "%s\n", card); } -static DEVICE_ATTR(family, S_IRUGO, mic_show_family, NULL); +static DEVICE_ATTR_RO(family); static ssize_t -mic_show_stepping(struct device *dev, struct device_attribute *attr, char *buf) +stepping_show(struct device *dev, struct device_attribute *attr, char *buf) { struct mic_device *mdev = dev_get_drvdata(dev->parent); char *string = "??"; @@ -114,10 +114,10 @@ mic_show_stepping(struct device *dev, struct device_attribute *attr, char *buf) } return scnprintf(buf, PAGE_SIZE, "%s\n", string); } -static DEVICE_ATTR(stepping, S_IRUGO, mic_show_stepping, NULL); +static DEVICE_ATTR_RO(stepping); static ssize_t -mic_show_state(struct device *dev, struct device_attribute *attr, char *buf) +state_show(struct device *dev, struct device_attribute *attr, char *buf) { struct mic_device *mdev = dev_get_drvdata(dev->parent); @@ -129,7 +129,7 @@ mic_show_state(struct device *dev, struct device_attribute *attr, char *buf) } static ssize_t -mic_store_state(struct device *dev, struct device_attribute *attr, +state_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { int rc = 0; @@ -160,9 +160,9 @@ mic_store_state(struct device *dev, struct device_attribute *attr, done: return count; } -static DEVICE_ATTR(state, S_IRUGO|S_IWUSR, mic_show_state, mic_store_state); +static DEVICE_ATTR_RW(state); -static ssize_t mic_show_shutdown_status(struct device *dev, +static ssize_t shutdown_status_show(struct device *dev, struct device_attribute *attr, char *buf) { struct mic_device *mdev = dev_get_drvdata(dev->parent); @@ -173,11 +173,10 @@ static ssize_t mic_show_shutdown_status(struct device *dev, return scnprintf(buf, PAGE_SIZE, "%s\n", mic_shutdown_status_string[mdev->shutdown_status]); } -static DEVICE_ATTR(shutdown_status, S_IRUGO|S_IWUSR, - mic_show_shutdown_status, NULL); +static DEVICE_ATTR_RO(shutdown_status); static ssize_t -mic_show_cmdline(struct device *dev, struct device_attribute *attr, char *buf) +cmdline_show(struct device *dev, struct device_attribute *attr, char *buf) { struct mic_device *mdev = dev_get_drvdata(dev->parent); char *cmdline; @@ -193,7 +192,7 @@ mic_show_cmdline(struct device *dev, struct device_attribute *attr, char *buf) } static ssize_t -mic_store_cmdline(struct device *dev, struct device_attribute *attr, +cmdline_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct mic_device *mdev = dev_get_drvdata(dev->parent); @@ -220,11 +219,10 @@ unlock: mutex_unlock(&mdev->mic_mutex); return count; } -static DEVICE_ATTR(cmdline, S_IRUGO | S_IWUSR, - mic_show_cmdline, mic_store_cmdline); +static DEVICE_ATTR_RW(cmdline); static ssize_t -mic_show_firmware(struct device *dev, struct device_attribute *attr, char *buf) +firmware_show(struct device *dev, struct device_attribute *attr, char *buf) { struct mic_device *mdev = dev_get_drvdata(dev->parent); char *firmware; @@ -240,7 +238,7 @@ mic_show_firmware(struct device *dev, struct device_attribute *attr, char *buf) } static ssize_t -mic_store_firmware(struct device *dev, struct device_attribute *attr, +firmware_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct mic_device *mdev = dev_get_drvdata(dev->parent); @@ -266,11 +264,10 @@ unlock: mutex_unlock(&mdev->mic_mutex); return count; } -static DEVICE_ATTR(firmware, S_IRUGO | S_IWUSR, - mic_show_firmware, mic_store_firmware); +static DEVICE_ATTR_RW(firmware); static ssize_t -mic_show_ramdisk(struct device *dev, struct device_attribute *attr, char *buf) +ramdisk_show(struct device *dev, struct device_attribute *attr, char *buf) { struct mic_device *mdev = dev_get_drvdata(dev->parent); char *ramdisk; @@ -286,7 +283,7 @@ mic_show_ramdisk(struct device *dev, struct device_attribute *attr, char *buf) } static ssize_t -mic_store_ramdisk(struct device *dev, struct device_attribute *attr, +ramdisk_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct mic_device *mdev = dev_get_drvdata(dev->parent); @@ -313,11 +310,10 @@ unlock: mutex_unlock(&mdev->mic_mutex); return count; } -static DEVICE_ATTR(ramdisk, S_IRUGO | S_IWUSR, - mic_show_ramdisk, mic_store_ramdisk); +static DEVICE_ATTR_RW(ramdisk); static ssize_t -mic_show_bootmode(struct device *dev, struct device_attribute *attr, char *buf) +bootmode_show(struct device *dev, struct device_attribute *attr, char *buf) { struct mic_device *mdev = dev_get_drvdata(dev->parent); char *bootmode; @@ -333,7 +329,7 @@ mic_show_bootmode(struct device *dev, struct device_attribute *attr, char *buf) } static ssize_t -mic_store_bootmode(struct device *dev, struct device_attribute *attr, +bootmode_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct mic_device *mdev = dev_get_drvdata(dev->parent); @@ -363,11 +359,10 @@ unlock: mutex_unlock(&mdev->mic_mutex); return count; } -static DEVICE_ATTR(bootmode, S_IRUGO | S_IWUSR, - mic_show_bootmode, mic_store_bootmode); +static DEVICE_ATTR_RW(bootmode); static ssize_t -mic_show_log_buf_addr(struct device *dev, struct device_attribute *attr, +log_buf_addr_show(struct device *dev, struct device_attribute *attr, char *buf) { struct mic_device *mdev = dev_get_drvdata(dev->parent); @@ -379,7 +374,7 @@ mic_show_log_buf_addr(struct device *dev, struct device_attribute *attr, } static ssize_t -mic_store_log_buf_addr(struct device *dev, struct device_attribute *attr, +log_buf_addr_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct mic_device *mdev = dev_get_drvdata(dev->parent); @@ -398,11 +393,10 @@ mic_store_log_buf_addr(struct device *dev, struct device_attribute *attr, exit: return ret; } -static DEVICE_ATTR(log_buf_addr, S_IRUGO | S_IWUSR, - mic_show_log_buf_addr, mic_store_log_buf_addr); +static DEVICE_ATTR_RW(log_buf_addr); static ssize_t -mic_show_log_buf_len(struct device *dev, struct device_attribute *attr, +log_buf_len_show(struct device *dev, struct device_attribute *attr, char *buf) { struct mic_device *mdev = dev_get_drvdata(dev->parent); @@ -414,7 +408,7 @@ mic_show_log_buf_len(struct device *dev, struct device_attribute *attr, } static ssize_t -mic_store_log_buf_len(struct device *dev, struct device_attribute *attr, +log_buf_len_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct mic_device *mdev = dev_get_drvdata(dev->parent); @@ -433,8 +427,7 @@ mic_store_log_buf_len(struct device *dev, struct device_attribute *attr, exit: return ret; } -static DEVICE_ATTR(log_buf_len, S_IRUGO | S_IWUSR, - mic_show_log_buf_len, mic_store_log_buf_len); +static DEVICE_ATTR_RW(log_buf_len); static struct attribute *mic_default_attrs[] = { &dev_attr_family.attr, @@ -451,16 +444,9 @@ static struct attribute *mic_default_attrs[] = { NULL }; -static struct attribute_group mic_attr_group = { - .attrs = mic_default_attrs, -}; - -static const struct attribute_group *__mic_attr_group[] = { - &mic_attr_group, - NULL -}; +ATTRIBUTE_GROUPS(mic_default); void mic_sysfs_init(struct mic_device *mdev) { - mdev->attr_group = __mic_attr_group; + mdev->attr_group = mic_default_groups; } -- cgit v0.10.2 From 4aa79961a50119d6112a160e97d5e6a77656b68c Mon Sep 17 00:00:00 2001 From: Sudeep Dutt Date: Fri, 27 Sep 2013 09:49:42 -0700 Subject: misc: mic: header file cleanups. Dont use same name for header files in different folders. These changes were suggested by Greg Kroah-Hartman during the code review @ https://lkml.org/lkml/2013/9/6/18 Reported-by: Greg Kroah-Hartman Signed-off-by: Ashutosh Dixit Signed-off-by: Dasaratharaman Chandramouli Signed-off-by: Nikhil Rao Signed-off-by: Harshavardhan R Kharche Signed-off-by: Sudeep Dutt Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/misc/mic/card/mic_debugfs.c b/drivers/misc/mic/card/mic_debugfs.c index 95cf186..421b3d7 100644 --- a/drivers/misc/mic/card/mic_debugfs.c +++ b/drivers/misc/mic/card/mic_debugfs.c @@ -30,7 +30,7 @@ #include #include -#include "../common/mic_device.h" +#include "../common/mic_dev.h" #include "mic_device.h" /* Debugfs parent dir */ diff --git a/drivers/misc/mic/card/mic_device.c b/drivers/misc/mic/card/mic_device.c index 4125217..175d114 100644 --- a/drivers/misc/mic/card/mic_device.c +++ b/drivers/misc/mic/card/mic_device.c @@ -30,7 +30,7 @@ #include #include -#include "../common/mic_device.h" +#include "../common/mic_dev.h" #include "mic_device.h" #include "mic_virtio.h" diff --git a/drivers/misc/mic/card/mic_virtio.c b/drivers/misc/mic/card/mic_virtio.c index 38275c1..7420c33 100644 --- a/drivers/misc/mic/card/mic_virtio.c +++ b/drivers/misc/mic/card/mic_virtio.c @@ -40,7 +40,7 @@ #include #include -#include "../common/mic_device.h" +#include "../common/mic_dev.h" #include "mic_virtio.h" #define VIRTIO_SUBCODE_64 0x0D00 diff --git a/drivers/misc/mic/card/mic_x100.c b/drivers/misc/mic/card/mic_x100.c index 7cb3469..f491c91 100644 --- a/drivers/misc/mic/card/mic_x100.c +++ b/drivers/misc/mic/card/mic_x100.c @@ -28,7 +28,7 @@ #include #include -#include "../common/mic_device.h" +#include "../common/mic_dev.h" #include "mic_device.h" #include "mic_x100.h" diff --git a/drivers/misc/mic/common/mic_dev.h b/drivers/misc/mic/common/mic_dev.h new file mode 100644 index 0000000..92999c2 --- /dev/null +++ b/drivers/misc/mic/common/mic_dev.h @@ -0,0 +1,51 @@ +/* + * Intel MIC Platform Software Stack (MPSS) + * + * Copyright(c) 2013 Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, version 2, as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * The full GNU General Public License is included in this distribution in + * the file called "COPYING". + * + * Intel MIC driver. + * + */ +#ifndef __MIC_DEV_H__ +#define __MIC_DEV_H__ + +/** + * struct mic_mw - MIC memory window + * + * @pa: Base physical address. + * @va: Base ioremap'd virtual address. + * @len: Size of the memory window. + */ +struct mic_mw { + phys_addr_t pa; + void __iomem *va; + resource_size_t len; +}; + +/* + * Scratch pad register offsets used by the host to communicate + * device page DMA address to the card. + */ +#define MIC_DPLO_SPAD 14 +#define MIC_DPHI_SPAD 15 + +/* + * These values are supposed to be in the config_change field of the + * device page when the host sends a config change interrupt to the card. + */ +#define MIC_VIRTIO_PARAM_DEV_REMOVE 0x1 +#define MIC_VIRTIO_PARAM_CONFIG_CHANGED 0x2 + +#endif diff --git a/drivers/misc/mic/common/mic_device.h b/drivers/misc/mic/common/mic_device.h deleted file mode 100644 index 01eb74f..0000000 --- a/drivers/misc/mic/common/mic_device.h +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Intel MIC Platform Software Stack (MPSS) - * - * Copyright(c) 2013 Intel Corporation. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License, version 2, as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * The full GNU General Public License is included in this distribution in - * the file called "COPYING". - * - * Intel MIC driver. - * - */ -#ifndef __MIC_COMMON_DEVICE_H_ -#define __MIC_COMMON_DEVICE_H_ - -/** - * struct mic_mw - MIC memory window - * - * @pa: Base physical address. - * @va: Base ioremap'd virtual address. - * @len: Size of the memory window. - */ -struct mic_mw { - phys_addr_t pa; - void __iomem *va; - resource_size_t len; -}; - -/* - * Scratch pad register offsets used by the host to communicate - * device page DMA address to the card. - */ -#define MIC_DPLO_SPAD 14 -#define MIC_DPHI_SPAD 15 - -/* - * These values are supposed to be in the config_change field of the - * device page when the host sends a config change interrupt to the card. - */ -#define MIC_VIRTIO_PARAM_DEV_REMOVE 0x1 -#define MIC_VIRTIO_PARAM_CONFIG_CHANGED 0x2 - -#endif diff --git a/drivers/misc/mic/host/mic_boot.c b/drivers/misc/mic/host/mic_boot.c index fd9ff6d..60c54d5 100644 --- a/drivers/misc/mic/host/mic_boot.c +++ b/drivers/misc/mic/host/mic_boot.c @@ -22,7 +22,7 @@ #include #include -#include "../common/mic_device.h" +#include "../common/mic_dev.h" #include "mic_device.h" #include "mic_smpt.h" #include "mic_virtio.h" diff --git a/drivers/misc/mic/host/mic_debugfs.c b/drivers/misc/mic/host/mic_debugfs.c index e22fb7b..98a61b4 100644 --- a/drivers/misc/mic/host/mic_debugfs.c +++ b/drivers/misc/mic/host/mic_debugfs.c @@ -23,7 +23,7 @@ #include #include -#include "../common/mic_device.h" +#include "../common/mic_dev.h" #include "mic_device.h" #include "mic_smpt.h" #include "mic_virtio.h" diff --git a/drivers/misc/mic/host/mic_fops.c b/drivers/misc/mic/host/mic_fops.c index 661469a..e699c80 100644 --- a/drivers/misc/mic/host/mic_fops.c +++ b/drivers/misc/mic/host/mic_fops.c @@ -21,7 +21,7 @@ #include #include -#include "../common/mic_device.h" +#include "../common/mic_dev.h" #include "mic_device.h" #include "mic_fops.h" #include "mic_virtio.h" diff --git a/drivers/misc/mic/host/mic_intr.c b/drivers/misc/mic/host/mic_intr.c index cdae314..71a7521 100644 --- a/drivers/misc/mic/host/mic_intr.c +++ b/drivers/misc/mic/host/mic_intr.c @@ -21,7 +21,7 @@ #include #include -#include "../common/mic_device.h" +#include "../common/mic_dev.h" #include "mic_device.h" /* diff --git a/drivers/misc/mic/host/mic_main.c b/drivers/misc/mic/host/mic_main.c index a8965d4..ca06aa9 100644 --- a/drivers/misc/mic/host/mic_main.c +++ b/drivers/misc/mic/host/mic_main.c @@ -28,7 +28,7 @@ #include #include -#include "../common/mic_device.h" +#include "../common/mic_dev.h" #include "mic_device.h" #include "mic_x100.h" #include "mic_smpt.h" diff --git a/drivers/misc/mic/host/mic_smpt.c b/drivers/misc/mic/host/mic_smpt.c index a346207..003d02b 100644 --- a/drivers/misc/mic/host/mic_smpt.c +++ b/drivers/misc/mic/host/mic_smpt.c @@ -20,7 +20,7 @@ */ #include -#include "../common/mic_device.h" +#include "../common/mic_dev.h" #include "mic_device.h" #include "mic_smpt.h" diff --git a/drivers/misc/mic/host/mic_sysfs.c b/drivers/misc/mic/host/mic_sysfs.c index bebbae2..029a4f3 100644 --- a/drivers/misc/mic/host/mic_sysfs.c +++ b/drivers/misc/mic/host/mic_sysfs.c @@ -21,7 +21,7 @@ #include #include -#include "../common/mic_device.h" +#include "../common/mic_dev.h" #include "mic_device.h" /* diff --git a/drivers/misc/mic/host/mic_virtio.c b/drivers/misc/mic/host/mic_virtio.c index be2a1f0..9e0456f 100644 --- a/drivers/misc/mic/host/mic_virtio.c +++ b/drivers/misc/mic/host/mic_virtio.c @@ -23,7 +23,7 @@ #include #include -#include "../common/mic_device.h" +#include "../common/mic_dev.h" #include "mic_device.h" #include "mic_smpt.h" #include "mic_virtio.h" diff --git a/drivers/misc/mic/host/mic_x100.c b/drivers/misc/mic/host/mic_x100.c index a12ae5c..3a0d660 100644 --- a/drivers/misc/mic/host/mic_x100.c +++ b/drivers/misc/mic/host/mic_x100.c @@ -24,7 +24,7 @@ #include #include -#include "../common/mic_device.h" +#include "../common/mic_dev.h" #include "mic_device.h" #include "mic_x100.h" #include "mic_smpt.h" -- cgit v0.10.2 From ced2c60fb5024a5cf5c33cb573b3d6a66d738f36 Mon Sep 17 00:00:00 2001 From: Ashutosh Dixit Date: Fri, 27 Sep 2013 09:49:53 -0700 Subject: misc: mic: cleanups for "--strict" checkpatch. These changes were mostly authored by Joe Perches @ https://lkml.org/lkml/2013/9/5/602 Reported-by: Joe Perches Signed-off-by: Ashutosh Dixit Signed-off-by: Dasaratharaman Chandramouli Signed-off-by: Nikhil Rao Signed-off-by: Harshavardhan R Kharche Signed-off-by: Sudeep Dutt Signed-off-by: Greg Kroah-Hartman diff --git a/Documentation/mic/mpssd/mpssd.c b/Documentation/mic/mpssd/mpssd.c index 8064804..f9327a2 100644 --- a/Documentation/mic/mpssd/mpssd.c +++ b/Documentation/mic/mpssd/mpssd.c @@ -246,7 +246,7 @@ static int tun_alloc(struct mic_info *mic, char *dev) if (*dev) strncpy(ifr.ifr_name, dev, IFNAMSIZ); - err = ioctl(fd, TUNSETIFF, (void *) &ifr); + err = ioctl(fd, TUNSETIFF, (void *)&ifr); if (err < 0) { mpsslog("%s %s %d TUNSETIFF failed %s\n", mic->name, __func__, __LINE__, strerror(errno)); @@ -363,7 +363,7 @@ static inline void verify_out_len(struct mic_info *mic, { if (copy->out_len != sum_iovec_len(copy)) { mpsslog("%s %s %d BUG copy->out_len 0x%x len 0x%x\n", - mic->name, __func__, __LINE__, + mic->name, __func__, __LINE__, copy->out_len, sum_iovec_len(copy)); assert(copy->out_len == sum_iovec_len(copy)); } @@ -372,7 +372,7 @@ static inline void verify_out_len(struct mic_info *mic, /* Display an iovec */ static void disp_iovec(struct mic_info *mic, struct mic_copy_desc *copy, - const char *s, int line) + const char *s, int line) { int i; @@ -401,7 +401,7 @@ static inline void txrx_prepare(int type, bool tx, struct mic_vring *vr, /* Central API which triggers the copies */ static int mic_virtio_copy(struct mic_info *mic, int fd, - struct mic_vring *vr, struct mic_copy_desc *copy) + struct mic_vring *vr, struct mic_copy_desc *copy) { int ret; @@ -440,7 +440,7 @@ init_vr(struct mic_info *mic, int fd, int type, vr0->info = vr0->va + vring_size(MIC_VRING_ENTRIES, MIC_VIRTIO_RING_ALIGN); vring_init(&vr0->vr, - MIC_VRING_ENTRIES, vr0->va, MIC_VIRTIO_RING_ALIGN); + MIC_VRING_ENTRIES, vr0->va, MIC_VIRTIO_RING_ALIGN); mpsslog("%s %s vr0 %p vr0->info %p vr_size 0x%x vring 0x%x ", __func__, mic->name, vr0->va, vr0->info, vr_size, vring_size(MIC_VRING_ENTRIES, MIC_VIRTIO_RING_ALIGN)); @@ -453,7 +453,7 @@ init_vr(struct mic_info *mic, int fd, int type, vr1->info = vr1->va + vring_size(MIC_VRING_ENTRIES, MIC_VIRTIO_RING_ALIGN); vring_init(&vr1->vr, - MIC_VRING_ENTRIES, vr1->va, MIC_VIRTIO_RING_ALIGN); + MIC_VRING_ENTRIES, vr1->va, MIC_VIRTIO_RING_ALIGN); mpsslog("%s %s vr1 %p vr1->info %p vr_size 0x%x vring 0x%x ", __func__, mic->name, vr1->va, vr1->info, vr_size, vring_size(MIC_VRING_ENTRIES, MIC_VIRTIO_RING_ALIGN)); @@ -551,7 +551,7 @@ virtio_net(void *arg) net_poll[NET_FD_TUN].events = POLLIN; if (MAP_FAILED == init_vr(mic, mic->mic_net.virtio_net_fd, - VIRTIO_ID_NET, &tx_vr, &rx_vr, + VIRTIO_ID_NET, &tx_vr, &rx_vr, virtnet_dev_page.dd.num_vq)) { mpsslog("%s init_vr failed %s\n", mic->name, strerror(errno)); @@ -576,7 +576,7 @@ virtio_net(void *arg) } if (!(desc->status & VIRTIO_CONFIG_S_DRIVER_OK)) wait_for_card_driver(mic, mic->mic_net.virtio_net_fd, - VIRTIO_ID_NET); + VIRTIO_ID_NET); /* * Check if there is data to be read from TUN and write to * virtio net fd if there is. @@ -587,7 +587,7 @@ virtio_net(void *arg) copy.iov, copy.iovcnt); if (len > 0) { struct virtio_net_hdr *hdr - = (struct virtio_net_hdr *) vnet_hdr[0]; + = (struct virtio_net_hdr *)vnet_hdr[0]; /* Disable checksums on the card since we are on a reliable PCIe link */ @@ -606,7 +606,7 @@ virtio_net(void *arg) #endif spin_for_descriptors(mic, &tx_vr); txrx_prepare(VIRTIO_ID_NET, 1, &tx_vr, ©, - len); + len); err = mic_virtio_copy(mic, mic->mic_net.virtio_net_fd, &tx_vr, @@ -644,7 +644,7 @@ virtio_net(void *arg) le16toh(rx_vr.vr.avail->idx)) { copy.iov = iov1; txrx_prepare(VIRTIO_ID_NET, 0, &rx_vr, ©, - MAX_NET_PKT_SIZE + MAX_NET_PKT_SIZE + sizeof(struct virtio_net_hdr)); err = mic_virtio_copy(mic, @@ -669,7 +669,7 @@ virtio_net(void *arg) verify_out_len(mic, ©); #ifdef DEBUG disp_iovec(mic, copy, __func__, - __LINE__); + __LINE__); mpsslog("%s %s %d ", mic->name, __func__, __LINE__); mpsslog("read from net 0x%lx\n", @@ -686,7 +686,7 @@ virtio_net(void *arg) } else { #ifdef DEBUG disp_iovec(mic, ©, __func__, - __LINE__); + __LINE__); mpsslog("%s %s %d ", mic->name, __func__, __LINE__); @@ -750,13 +750,13 @@ virtio_console(void *arg) err = grantpt(pty_fd); if (err < 0) { mpsslog("can't grant access: %s %s\n", - pts_name, strerror(errno)); + pts_name, strerror(errno)); goto _close_pty; } err = unlockpt(pty_fd); if (err < 0) { mpsslog("can't unlock a pseudoterminal: %s %s\n", - pts_name, strerror(errno)); + pts_name, strerror(errno)); goto _close_pty; } console_poll[MONITOR_FD].fd = pty_fd; @@ -766,7 +766,7 @@ virtio_console(void *arg) console_poll[VIRTIO_CONSOLE_FD].events = POLLIN; if (MAP_FAILED == init_vr(mic, mic->mic_console.virtio_console_fd, - VIRTIO_ID_CONSOLE, &tx_vr, &rx_vr, + VIRTIO_ID_CONSOLE, &tx_vr, &rx_vr, virtcons_dev_page.dd.num_vq)) { mpsslog("%s init_vr failed %s\n", mic->name, strerror(errno)); @@ -787,7 +787,7 @@ virtio_console(void *arg) } if (!(desc->status & VIRTIO_CONFIG_S_DRIVER_OK)) wait_for_card_driver(mic, - mic->mic_console.virtio_console_fd, + mic->mic_console.virtio_console_fd, VIRTIO_ID_CONSOLE); if (console_poll[MONITOR_FD].revents & POLLIN) { @@ -802,7 +802,7 @@ virtio_console(void *arg) #endif spin_for_descriptors(mic, &tx_vr); txrx_prepare(VIRTIO_ID_CONSOLE, 1, &tx_vr, - ©, len); + ©, len); err = mic_virtio_copy(mic, mic->mic_console.virtio_console_fd, @@ -837,7 +837,7 @@ virtio_console(void *arg) le16toh(rx_vr.vr.avail->idx)) { copy.iov = iov1; txrx_prepare(VIRTIO_ID_CONSOLE, 0, &rx_vr, - ©, PAGE_SIZE); + ©, PAGE_SIZE); err = mic_virtio_copy(mic, mic->mic_console.virtio_console_fd, @@ -848,7 +848,7 @@ virtio_console(void *arg) verify_out_len(mic, ©); #ifdef DEBUG disp_iovec(mic, copy, __func__, - __LINE__); + __LINE__); mpsslog("%s %s %d ", mic->name, __func__, __LINE__); mpsslog("read from net 0x%lx\n", @@ -865,7 +865,7 @@ virtio_console(void *arg) } else { #ifdef DEBUG disp_iovec(mic, copy, __func__, - __LINE__); + __LINE__); mpsslog("%s %s %d ", mic->name, __func__, __LINE__); @@ -1033,7 +1033,8 @@ start_virtblk(struct mic_info *mic, struct mic_vring *vring) } add_virtio_device(mic, &virtblk_dev_page.dd); if (MAP_FAILED == init_vr(mic, mic->mic_virtblk.virtio_block_fd, - VIRTIO_ID_BLOCK, vring, NULL, virtblk_dev_page.dd.num_vq)) { + VIRTIO_ID_BLOCK, vring, NULL, + virtblk_dev_page.dd.num_vq)) { mpsslog("%s init_vr failed %s\n", mic->name, strerror(errno)); return false; @@ -1060,7 +1061,7 @@ header_error_check(struct vring_desc *desc) { if (le32toh(desc->len) != sizeof(struct virtio_blk_outhdr)) { mpsslog("%s() %d: length is not sizeof(virtio_blk_outhd)\n", - __func__, __LINE__); + __func__, __LINE__); return -EIO; } if (!(le16toh(desc->flags) & VRING_DESC_F_NEXT)) { @@ -1132,7 +1133,7 @@ write_status(int fd, __u8 *status) static void * virtio_block(void *arg) { - struct mic_info *mic = (struct mic_info *) arg; + struct mic_info *mic = (struct mic_info *)arg; int ret; struct pollfd block_poll; struct mic_vring vring; @@ -1219,8 +1220,8 @@ virtio_block(void *arg) status = 0; fos = mic->mic_virtblk.backend_addr + (hdr.sector * SECTOR_SIZE); - buffer_desc_idx = desc_idx = - next_desc(desc); + buffer_desc_idx = next_desc(desc); + desc_idx = buffer_desc_idx; for (desc = &vring.vr.desc[buffer_desc_idx]; desc->flags & VRING_DESC_F_NEXT; desc_idx = next_desc(desc), @@ -1255,7 +1256,7 @@ virtio_block(void *arg) iovec, piov - iovec); if (ret < 0 && - status != 0) + status != 0) status = ret; } /* write status and update used pointer */ @@ -1391,7 +1392,7 @@ mic_config(void *arg) char value[4096]; snprintf(pathname, PATH_MAX - 1, "%s/%s/%s", - MICSYSFSDIR, mic->name, "state"); + MICSYSFSDIR, mic->name, "state"); fd = open(pathname, O_RDONLY); if (fd < 0) { @@ -1560,22 +1561,22 @@ init_mic(struct mic_info *mic) virtio_console, mic); if (err) mpsslog("%s virtcons pthread_create failed %s\n", - mic->name, strerror(err)); + mic->name, strerror(err)); err = pthread_create(&mic->mic_net.net_thread, NULL, virtio_net, mic); if (err) mpsslog("%s virtnet pthread_create failed %s\n", - mic->name, strerror(err)); + mic->name, strerror(err)); err = pthread_create(&mic->mic_virtblk.block_thread, NULL, virtio_block, mic); if (err) mpsslog("%s virtblk pthread_create failed %s\n", - mic->name, strerror(err)); + mic->name, strerror(err)); sigemptyset(&act.sa_mask); err = sigaction(SIGUSR1, &act, NULL); if (err) mpsslog("%s sigaction SIGUSR1 failed %s\n", - mic->name, strerror(errno)); + mic->name, strerror(errno)); while (1) sleep(60); case -1: diff --git a/Documentation/mic/mpssd/sysfs.c b/Documentation/mic/mpssd/sysfs.c index 11de72b..8dd3269 100644 --- a/Documentation/mic/mpssd/sysfs.c +++ b/Documentation/mic/mpssd/sysfs.c @@ -35,7 +35,7 @@ readsysfs(char *dir, char *entry) snprintf(filename, PATH_MAX, "%s/%s", MICSYSFSDIR, entry); else snprintf(filename, PATH_MAX, - "%s/%s/%s", MICSYSFSDIR, dir, entry); + "%s/%s/%s", MICSYSFSDIR, dir, entry); fd = open(filename, O_RDONLY); if (fd < 0) { @@ -75,7 +75,7 @@ setsysfs(char *dir, char *entry, char *value) snprintf(filename, PATH_MAX, "%s/%s", MICSYSFSDIR, entry); else snprintf(filename, PATH_MAX, "%s/%s/%s", - MICSYSFSDIR, dir, entry); + MICSYSFSDIR, dir, entry); oldvalue = readsysfs(dir, entry); diff --git a/drivers/misc/mic/card/mic_device.c b/drivers/misc/mic/card/mic_device.c index 175d114..d0980ff 100644 --- a/drivers/misc/mic/card/mic_device.c +++ b/drivers/misc/mic/card/mic_device.c @@ -167,7 +167,6 @@ struct mic_irq *mic_request_card_irq(irqreturn_t (*func)(int irq, void *data), return (struct mic_irq *)cookie; err: return ERR_PTR(rc); - } /** diff --git a/drivers/misc/mic/card/mic_virtio.c b/drivers/misc/mic/card/mic_virtio.c index 7420c33..914cc9b 100644 --- a/drivers/misc/mic/card/mic_virtio.c +++ b/drivers/misc/mic/card/mic_virtio.c @@ -103,7 +103,7 @@ static void mic_finalize_features(struct virtio_device *vdev) for (i = 0; i < bits; i++) { if (test_bit(i, vdev->features)) iowrite8(ioread8(&out_features[i / 8]) | (1 << (i % 8)), - &out_features[i / 8]); + &out_features[i / 8]); } } @@ -197,10 +197,9 @@ static void mic_notify(struct virtqueue *vq) static void mic_del_vq(struct virtqueue *vq, int n) { struct mic_vdev *mvdev = to_micvdev(vq->vdev); - struct vring *vr = (struct vring *) (vq + 1); + struct vring *vr = (struct vring *)(vq + 1); - free_pages((unsigned long) vr->used, - get_order(mvdev->used_size[n])); + free_pages((unsigned long) vr->used, get_order(mvdev->used_size[n])); vring_del_virtqueue(vq); mic_card_unmap(mvdev->mdev, mvdev->vr[n]); mvdev->vr[n] = NULL; @@ -274,8 +273,8 @@ static struct virtqueue *mic_find_vq(struct virtio_device *vdev, /* Allocate and reassign used ring now */ mvdev->used_size[index] = PAGE_ALIGN(sizeof(__u16) * 3 + sizeof(struct vring_used_elem) * config.num); - used = (void *) __get_free_pages(GFP_KERNEL | __GFP_ZERO, - get_order(mvdev->used_size[index])); + used = (void *)__get_free_pages(GFP_KERNEL | __GFP_ZERO, + get_order(mvdev->used_size[index])); if (!used) { err = -ENOMEM; dev_err(mic_dev(mvdev), "%s %d err %d\n", @@ -291,7 +290,7 @@ static struct virtqueue *mic_find_vq(struct virtio_device *vdev, * vring_new_virtqueue() would ensure that * (&vq->vring == (struct vring *) (&vq->vq + 1)); */ - vr = (struct vring *) (vq + 1); + vr = (struct vring *)(vq + 1); vr->used = used; vq->priv = mvdev; @@ -544,7 +543,7 @@ static void mic_scan_devices(struct mic_driver *mdrv, bool remove) if (dev) { if (remove) iowrite8(MIC_VIRTIO_PARAM_DEV_REMOVE, - &dc->config_change); + &dc->config_change); put_device(dev); mic_handle_config_change(d, i, mdrv); ret = mic_remove_device(d, i, mdrv); @@ -559,7 +558,7 @@ static void mic_scan_devices(struct mic_driver *mdrv, bool remove) /* new device */ dev_dbg(mdrv->dev, "%s %d Adding new virtio device %p\n", - __func__, __LINE__, d); + __func__, __LINE__, d); if (!remove) mic_add_device(d, i, mdrv); } diff --git a/drivers/misc/mic/card/mic_x100.c b/drivers/misc/mic/card/mic_x100.c index f491c91..2868945 100644 --- a/drivers/misc/mic/card/mic_x100.c +++ b/drivers/misc/mic/card/mic_x100.c @@ -66,8 +66,8 @@ void mic_send_intr(struct mic_device *mdev, int doorbell) /* Ensure that the interrupt is ordered w.r.t previous stores. */ wmb(); mic_mmio_write(mw, MIC_X100_SBOX_SDBIC0_DBREQ_BIT, - MIC_X100_SBOX_BASE_ADDRESS + - (MIC_X100_SBOX_SDBIC0 + (4 * doorbell))); + MIC_X100_SBOX_BASE_ADDRESS + + (MIC_X100_SBOX_SDBIC0 + (4 * doorbell))); } /** diff --git a/drivers/misc/mic/host/mic_debugfs.c b/drivers/misc/mic/host/mic_debugfs.c index 98a61b4..028ba5d6 100644 --- a/drivers/misc/mic/host/mic_debugfs.c +++ b/drivers/misc/mic/host/mic_debugfs.c @@ -103,7 +103,7 @@ static int mic_smpt_show(struct seq_file *s, void *pos) unsigned long flags; seq_printf(s, "MIC %-2d |%-10s| %-14s %-10s\n", - mdev->id, "SMPT entry", "SW DMA addr", "RefCount"); + mdev->id, "SMPT entry", "SW DMA addr", "RefCount"); seq_puts(s, "====================================================\n"); if (mdev->smpt) { @@ -111,8 +111,8 @@ static int mic_smpt_show(struct seq_file *s, void *pos) spin_lock_irqsave(&smpt_info->smpt_lock, flags); for (i = 0; i < smpt_info->info.num_reg; i++) { seq_printf(s, "%9s|%-10d| %-#14llx %-10lld\n", - " ", i, smpt_info->entry[i].dma_addr, - smpt_info->entry[i].ref_count); + " ", i, smpt_info->entry[i].dma_addr, + smpt_info->entry[i].ref_count); } spin_unlock_irqrestore(&smpt_info->smpt_lock, flags); } @@ -203,17 +203,17 @@ static int mic_dp_show(struct seq_file *s, void *pos) int i, j; seq_printf(s, "Bootparam: magic 0x%x\n", - bootparam->magic); + bootparam->magic); seq_printf(s, "Bootparam: h2c_shutdown_db %d\n", - bootparam->h2c_shutdown_db); + bootparam->h2c_shutdown_db); seq_printf(s, "Bootparam: h2c_config_db %d\n", - bootparam->h2c_config_db); + bootparam->h2c_config_db); seq_printf(s, "Bootparam: c2h_shutdown_db %d\n", - bootparam->c2h_shutdown_db); + bootparam->c2h_shutdown_db); seq_printf(s, "Bootparam: shutdown_status %d\n", - bootparam->shutdown_status); + bootparam->shutdown_status); seq_printf(s, "Bootparam: shutdown_card %d\n", - bootparam->shutdown_card); + bootparam->shutdown_card); for (i = sizeof(*bootparam); i < MIC_DP_SIZE; i += mic_total_desc_size(d)) { @@ -239,10 +239,10 @@ static int mic_dp_show(struct seq_file *s, void *pos) seq_printf(s, "address 0x%llx ", vqconfig->address); seq_printf(s, "num %d ", vqconfig->num); seq_printf(s, "used address 0x%llx\n", - vqconfig->used_address); + vqconfig->used_address); } - features = (__u32 *) mic_vq_features(d); + features = (__u32 *)mic_vq_features(d); seq_printf(s, "Features: Host 0x%x ", features[0]); seq_printf(s, "Guest 0x%x\n", features[1]); @@ -256,7 +256,7 @@ static int mic_dp_show(struct seq_file *s, void *pos) seq_printf(s, "Guest Ack %d ", dc->guest_ack); seq_printf(s, "Host ack %d\n", dc->host_ack); seq_printf(s, "Used address updated %d ", - dc->used_address_updated); + dc->used_address_updated); seq_printf(s, "Vdev 0x%llx\n", dc->vdev); seq_printf(s, "c2h doorbell %d ", dc->c2h_vdev_db); seq_printf(s, "h2c doorbell %d\n", dc->h2c_vdev_db); @@ -294,10 +294,10 @@ static int mic_vdev_info_show(struct seq_file *s, void *unused) list_for_each_safe(pos, tmp, &mdev->vdev_list) { mvdev = list_entry(pos, struct mic_vdev, list); seq_printf(s, "VDEV type %d state %s in %ld out %ld\n", - mvdev->virtio_id, - mic_vdevup(mvdev) ? "UP" : "DOWN", - mvdev->in_bytes, - mvdev->out_bytes); + mvdev->virtio_id, + mic_vdevup(mvdev) ? "UP" : "DOWN", + mvdev->in_bytes, + mvdev->out_bytes); for (i = 0; i < MIC_MAX_VRINGS; i++) { struct vring_desc *desc; struct vring_avail *avail; @@ -309,38 +309,38 @@ static int mic_vdev_info_show(struct seq_file *s, void *unused) continue; desc = vrh->vring.desc; seq_printf(s, "vring i %d avail_idx %d", - i, mvr->vring.info->avail_idx & (num - 1)); + i, mvr->vring.info->avail_idx & (num - 1)); seq_printf(s, " vring i %d avail_idx %d\n", - i, mvr->vring.info->avail_idx); + i, mvr->vring.info->avail_idx); seq_printf(s, "vrh i %d weak_barriers %d", - i, vrh->weak_barriers); + i, vrh->weak_barriers); seq_printf(s, " last_avail_idx %d last_used_idx %d", - vrh->last_avail_idx, vrh->last_used_idx); + vrh->last_avail_idx, vrh->last_used_idx); seq_printf(s, " completed %d\n", vrh->completed); for (j = 0; j < num; j++) { seq_printf(s, "desc[%d] addr 0x%llx len %d", - j, desc->addr, desc->len); + j, desc->addr, desc->len); seq_printf(s, " flags 0x%x next %d\n", - desc->flags, - desc->next); + desc->flags, desc->next); desc++; } avail = vrh->vring.avail; seq_printf(s, "avail flags 0x%x idx %d\n", - avail->flags, avail->idx & (num - 1)); + avail->flags, avail->idx & (num - 1)); seq_printf(s, "avail flags 0x%x idx %d\n", - avail->flags, avail->idx); + avail->flags, avail->idx); for (j = 0; j < num; j++) seq_printf(s, "avail ring[%d] %d\n", - j, avail->ring[j]); + j, avail->ring[j]); used = vrh->vring.used; seq_printf(s, "used flags 0x%x idx %d\n", - used->flags, used->idx & (num - 1)); + used->flags, used->idx & (num - 1)); seq_printf(s, "used flags 0x%x idx %d\n", - used->flags, used->idx); + used->flags, used->idx); for (j = 0; j < num; j++) seq_printf(s, "used ring[%d] id %d len %d\n", - j, used->ring[j].id, used->ring[j].len); + j, used->ring[j].id, + used->ring[j].len); } } mutex_unlock(&mdev->mic_mutex); @@ -389,7 +389,7 @@ static int mic_msi_irq_info_show(struct seq_file *s, void *pos) reg = mdev->intr_ops->read_msi_to_src_map(mdev, entry); seq_printf(s, "%s %-10d %s %-10d MXAR[%d]: %08X\n", - "IRQ:", vector, "Entry:", entry, i, reg); + "IRQ:", vector, "Entry:", entry, i, reg); seq_printf(s, "%-10s", "offset:"); for (j = (MIC_NUM_OFFSETS - 1); j >= 0; j--) @@ -400,8 +400,8 @@ static int mic_msi_irq_info_show(struct seq_file *s, void *pos) seq_printf(s, "%-10s", "count:"); for (j = (MIC_NUM_OFFSETS - 1); j >= 0; j--) seq_printf(s, "%4d ", - (mdev->irq_info.mic_msi_map[i] & BIT(j)) ? - 1 : 0); + (mdev->irq_info.mic_msi_map[i] & + BIT(j)) ? 1 : 0); seq_puts(s, "\n\n"); } } else { @@ -409,7 +409,6 @@ static int mic_msi_irq_info_show(struct seq_file *s, void *pos) } return 0; - } static int mic_msi_irq_info_debug_open(struct inode *inode, struct file *file) @@ -443,26 +442,23 @@ void mic_create_debug_dir(struct mic_device *mdev) if (!mdev->dbg_dir) return; - debugfs_create_file("log_buf", 0444, mdev->dbg_dir, - mdev, &log_buf_ops); + debugfs_create_file("log_buf", 0444, mdev->dbg_dir, mdev, &log_buf_ops); - debugfs_create_file("smpt", 0444, mdev->dbg_dir, - mdev, &smpt_file_ops); + debugfs_create_file("smpt", 0444, mdev->dbg_dir, mdev, &smpt_file_ops); - debugfs_create_file("soft_reset", 0444, mdev->dbg_dir, - mdev, &soft_reset_ops); + debugfs_create_file("soft_reset", 0444, mdev->dbg_dir, mdev, + &soft_reset_ops); - debugfs_create_file("post_code", 0444, mdev->dbg_dir, - mdev, &post_code_ops); + debugfs_create_file("post_code", 0444, mdev->dbg_dir, mdev, + &post_code_ops); - debugfs_create_file("dp", 0444, mdev->dbg_dir, - mdev, &dp_ops); + debugfs_create_file("dp", 0444, mdev->dbg_dir, mdev, &dp_ops); - debugfs_create_file("vdev_info", 0444, mdev->dbg_dir, - mdev, &vdev_info_ops); + debugfs_create_file("vdev_info", 0444, mdev->dbg_dir, mdev, + &vdev_info_ops); - debugfs_create_file("msi_irq_info", 0444, mdev->dbg_dir, - mdev, &msi_irq_info_ops); + debugfs_create_file("msi_irq_info", 0444, mdev->dbg_dir, mdev, + &msi_irq_info_ops); } /** diff --git a/drivers/misc/mic/host/mic_fops.c b/drivers/misc/mic/host/mic_fops.c index e699c80..8dc6ff1 100644 --- a/drivers/misc/mic/host/mic_fops.c +++ b/drivers/misc/mic/host/mic_fops.c @@ -140,9 +140,9 @@ unsigned int mic_poll(struct file *f, poll_table *wait) poll_wait(f, &mvdev->waitq, wait); - if (mic_vdev_inited(mvdev)) + if (mic_vdev_inited(mvdev)) { mask = POLLERR; - else if (mvdev->poll_wake) { + } else if (mvdev->poll_wake) { mvdev->poll_wake = 0; mask = POLLIN | POLLOUT; } @@ -152,7 +152,7 @@ unsigned int mic_poll(struct file *f, poll_table *wait) static inline int mic_query_offset(struct mic_vdev *mvdev, unsigned long offset, - unsigned long *size, unsigned long *pa) + unsigned long *size, unsigned long *pa) { struct mic_device *mdev = mvdev->mdev; unsigned long start = MIC_DP_SIZE; diff --git a/drivers/misc/mic/host/mic_intr.c b/drivers/misc/mic/host/mic_intr.c index 71a7521..f9c29bc 100644 --- a/drivers/misc/mic/host/mic_intr.c +++ b/drivers/misc/mic/host/mic_intr.c @@ -71,8 +71,8 @@ static irqreturn_t mic_interrupt(int irq, void *dev) /* Return the interrupt offset from the index. Index is 0 based. */ static u16 mic_map_src_to_offset(struct mic_device *mdev, - int intr_src, enum mic_intr_type type) { - + int intr_src, enum mic_intr_type type) +{ if (type >= MIC_NUM_INTR_TYPES) return MIC_NUM_OFFSETS; if (intr_src >= mdev->intr_info->intr_len[type]) @@ -112,7 +112,7 @@ static struct mic_intr_cb *mic_register_intr_callback(struct mic_device *mdev, struct mic_intr_cb *intr_cb; unsigned long flags; int rc; - intr_cb = kmalloc(sizeof(struct mic_intr_cb), GFP_KERNEL); + intr_cb = kmalloc(sizeof(*intr_cb), GFP_KERNEL); if (!intr_cb) return ERR_PTR(-ENOMEM); @@ -159,7 +159,7 @@ static u8 mic_unregister_intr_callback(struct mic_device *mdev, u32 idx) if (intr_cb->cb_id == idx) { list_del(pos); ida_simple_remove(&mdev->irq_info.cb_ida, - intr_cb->cb_id); + intr_cb->cb_id); kfree(intr_cb); spin_unlock_irqrestore( &mdev->irq_info.mic_intr_lock, flags); @@ -182,9 +182,10 @@ static u8 mic_unregister_intr_callback(struct mic_device *mdev, u32 idx) static int mic_setup_msix(struct mic_device *mdev, struct pci_dev *pdev) { int rc, i; + int entry_size = sizeof(*mdev->irq_info.msix_entries); - mdev->irq_info.msix_entries = kmalloc(sizeof(struct msix_entry) * - MIC_MIN_MSIX, GFP_KERNEL); + mdev->irq_info.msix_entries = kmalloc_array(MIC_MIN_MSIX, + entry_size, GFP_KERNEL); if (!mdev->irq_info.msix_entries) { rc = -ENOMEM; goto err_nomem1; @@ -231,8 +232,9 @@ static int mic_setup_callbacks(struct mic_device *mdev) { int i; - mdev->irq_info.cb_list = kmalloc(sizeof(struct list_head) * - MIC_NUM_OFFSETS, GFP_KERNEL); + mdev->irq_info.cb_list = kmalloc_array(MIC_NUM_OFFSETS, + sizeof(*mdev->irq_info.cb_list), + GFP_KERNEL); if (!mdev->irq_info.cb_list) return -ENOMEM; @@ -261,7 +263,7 @@ static void mic_release_callbacks(struct mic_device *mdev) if (list_empty(&mdev->irq_info.cb_list[i])) { spin_unlock_irqrestore(&mdev->irq_info.mic_intr_lock, - flags); + flags); break; } @@ -269,7 +271,7 @@ static void mic_release_callbacks(struct mic_device *mdev) intr_cb = list_entry(pos, struct mic_intr_cb, list); list_del(pos); ida_simple_remove(&mdev->irq_info.cb_ida, - intr_cb->cb_id); + intr_cb->cb_id); kfree(intr_cb); } spin_unlock_irqrestore(&mdev->irq_info.mic_intr_lock, flags); @@ -427,8 +429,8 @@ struct mic_irq *mic_request_irq(struct mic_device *mdev, offset = mic_map_src_to_offset(mdev, intr_src, type); if (offset >= MIC_NUM_OFFSETS) { dev_err(mdev->sdev->parent, - "Error mapping index %d to a valid source id.\n", - intr_src); + "Error mapping index %d to a valid source id.\n", + intr_src); rc = -EINVAL; goto err; } @@ -437,7 +439,7 @@ struct mic_irq *mic_request_irq(struct mic_device *mdev, msix = mic_get_available_vector(mdev); if (!msix) { dev_err(mdev->sdev->parent, - "No MSIx vectors available for use.\n"); + "No MSIx vectors available for use.\n"); rc = -ENOSPC; goto err; } @@ -460,7 +462,7 @@ struct mic_irq *mic_request_irq(struct mic_device *mdev, offset, func, data); if (IS_ERR(intr_cb)) { dev_err(mdev->sdev->parent, - "No available callback entries for use\n"); + "No available callback entries for use\n"); rc = PTR_ERR(intr_cb); goto err; } @@ -506,7 +508,7 @@ void mic_free_irq(struct mic_device *mdev, if (mdev->irq_info.num_vectors > 1) { if (entry >= mdev->irq_info.num_vectors) { dev_warn(mdev->sdev->parent, - "entry %d should be < num_irq %d\n", + "entry %d should be < num_irq %d\n", entry, mdev->irq_info.num_vectors); return; } @@ -581,7 +583,7 @@ void mic_free_interrupts(struct mic_device *mdev, struct pci_dev *pdev) for (i = 0; i < mdev->irq_info.num_vectors; i++) { if (mdev->irq_info.mic_msi_map[i]) dev_warn(&pdev->dev, "irq %d may still be in use.\n", - mdev->irq_info.msix_entries[i].vector); + mdev->irq_info.msix_entries[i].vector); } kfree(mdev->irq_info.mic_msi_map); kfree(mdev->irq_info.msix_entries); diff --git a/drivers/misc/mic/host/mic_smpt.c b/drivers/misc/mic/host/mic_smpt.c index 003d02b..fae474c 100644 --- a/drivers/misc/mic/host/mic_smpt.c +++ b/drivers/misc/mic/host/mic_smpt.c @@ -84,7 +84,7 @@ static void mic_add_smpt_entry(int spt, s64 *ref, u64 addr, for (i = spt; i < spt + entries; i++, addr += smpt_info->info.page_size) { if (!smpt_info->entry[i].ref_count && - (smpt_info->entry[i].dma_addr != addr)) { + (smpt_info->entry[i].dma_addr != addr)) { mdev->smpt_ops->set(mdev, addr, i); smpt_info->entry[i].dma_addr = addr; } @@ -183,7 +183,7 @@ mic_to_dma_addr(struct mic_device *mdev, dma_addr_t mic_addr) if (!mic_is_system_addr(mdev, mic_addr)) { dev_err(mdev->sdev->parent, - "mic_addr is invalid. mic_addr = 0x%llx\n", mic_addr); + "mic_addr is invalid. mic_addr = 0x%llx\n", mic_addr); return -EINVAL; } spt = mic_sys_addr_to_smpt(mdev, mic_addr); @@ -286,7 +286,7 @@ void mic_unmap(struct mic_device *mdev, dma_addr_t mic_addr, size_t size) smpt_info->entry[i].ref_count -= ref[i - spt]; if (smpt_info->entry[i].ref_count < 0) dev_warn(mdev->sdev->parent, - "ref count for entry %d is negative\n", i); + "ref count for entry %d is negative\n", i); } spin_unlock_irqrestore(&smpt_info->smpt_lock, flags); kfree(ref); @@ -320,7 +320,7 @@ dma_addr_t mic_map_single(struct mic_device *mdev, void *va, size_t size) "mic_map failed dma_addr 0x%llx size 0x%lx\n", dma_addr, size); pci_unmap_single(pdev, dma_addr, - size, PCI_DMA_BIDIRECTIONAL); + size, PCI_DMA_BIDIRECTIONAL); } } return mic_addr; @@ -366,8 +366,8 @@ int mic_smpt_init(struct mic_device *mdev) smpt_info = mdev->smpt; mdev->smpt_ops->init(mdev); - smpt_info->entry = kmalloc(sizeof(struct mic_smpt) - * smpt_info->info.num_reg, GFP_KERNEL); + smpt_info->entry = kmalloc_array(smpt_info->info.num_reg, + sizeof(*smpt_info->entry), GFP_KERNEL); if (!smpt_info->entry) { err = -ENOMEM; goto free_smpt; @@ -412,7 +412,7 @@ void mic_smpt_uninit(struct mic_device *mdev) smpt_info->entry[i].ref_count); if (smpt_info->entry[i].ref_count) dev_warn(mdev->sdev->parent, - "ref count for entry %d is not zero\n", i); + "ref count for entry %d is not zero\n", i); } kfree(smpt_info->entry); kfree(smpt_info); diff --git a/drivers/misc/mic/host/mic_sysfs.c b/drivers/misc/mic/host/mic_sysfs.c index 029a4f3..75746ad 100644 --- a/drivers/misc/mic/host/mic_sysfs.c +++ b/drivers/misc/mic/host/mic_sysfs.c @@ -130,7 +130,7 @@ state_show(struct device *dev, struct device_attribute *attr, char *buf) static ssize_t state_store(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) + const char *buf, size_t count) { int rc = 0; struct mic_device *mdev = dev_get_drvdata(dev->parent); @@ -163,7 +163,7 @@ done: static DEVICE_ATTR_RW(state); static ssize_t shutdown_status_show(struct device *dev, - struct device_attribute *attr, char *buf) + struct device_attribute *attr, char *buf) { struct mic_device *mdev = dev_get_drvdata(dev->parent); @@ -193,7 +193,7 @@ cmdline_show(struct device *dev, struct device_attribute *attr, char *buf) static ssize_t cmdline_store(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) + const char *buf, size_t count) { struct mic_device *mdev = dev_get_drvdata(dev->parent); @@ -239,7 +239,7 @@ firmware_show(struct device *dev, struct device_attribute *attr, char *buf) static ssize_t firmware_store(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) + const char *buf, size_t count) { struct mic_device *mdev = dev_get_drvdata(dev->parent); @@ -284,7 +284,7 @@ ramdisk_show(struct device *dev, struct device_attribute *attr, char *buf) static ssize_t ramdisk_store(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) + const char *buf, size_t count) { struct mic_device *mdev = dev_get_drvdata(dev->parent); @@ -330,7 +330,7 @@ bootmode_show(struct device *dev, struct device_attribute *attr, char *buf) static ssize_t bootmode_store(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) + const char *buf, size_t count) { struct mic_device *mdev = dev_get_drvdata(dev->parent); @@ -363,7 +363,7 @@ static DEVICE_ATTR_RW(bootmode); static ssize_t log_buf_addr_show(struct device *dev, struct device_attribute *attr, - char *buf) + char *buf) { struct mic_device *mdev = dev_get_drvdata(dev->parent); @@ -375,7 +375,7 @@ log_buf_addr_show(struct device *dev, struct device_attribute *attr, static ssize_t log_buf_addr_store(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) + const char *buf, size_t count) { struct mic_device *mdev = dev_get_drvdata(dev->parent); int ret; @@ -397,7 +397,7 @@ static DEVICE_ATTR_RW(log_buf_addr); static ssize_t log_buf_len_show(struct device *dev, struct device_attribute *attr, - char *buf) + char *buf) { struct mic_device *mdev = dev_get_drvdata(dev->parent); @@ -409,7 +409,7 @@ log_buf_len_show(struct device *dev, struct device_attribute *attr, static ssize_t log_buf_len_store(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) + const char *buf, size_t count) { struct mic_device *mdev = dev_get_drvdata(dev->parent); int ret; diff --git a/drivers/misc/mic/host/mic_virtio.c b/drivers/misc/mic/host/mic_virtio.c index 9e0456f..0c883cd 100644 --- a/drivers/misc/mic/host/mic_virtio.c +++ b/drivers/misc/mic/host/mic_virtio.c @@ -195,7 +195,7 @@ static int _mic_virtio_copy(struct mic_vdev *mvdev, MIC_VRINGH_READ, &out_len); if (ret) { dev_err(mic_dev(mvdev), "%s %d err %d\n", - __func__, __LINE__, ret); + __func__, __LINE__, ret); break; } len -= out_len; @@ -206,7 +206,7 @@ static int _mic_virtio_copy(struct mic_vdev *mvdev, !MIC_VRINGH_READ, &out_len); if (ret) { dev_err(mic_dev(mvdev), "%s %d err %d\n", - __func__, __LINE__, ret); + __func__, __LINE__, ret); break; } len -= out_len; @@ -225,8 +225,7 @@ static int _mic_virtio_copy(struct mic_vdev *mvdev, * Update the used ring if a descriptor was available and some data was * copied in/out and the user asked for a used ring update. */ - if (*head != USHRT_MAX && copy->out_len && - copy->update_used) { + if (*head != USHRT_MAX && copy->out_len && copy->update_used) { u32 total = 0; /* Determine the total data consumed */ @@ -367,7 +366,6 @@ void mic_bh_handler(struct work_struct *work) static irqreturn_t mic_virtio_intr_handler(int irq, void *data) { - struct mic_vdev *mvdev = data; struct mic_device *mdev = mvdev->mdev; @@ -394,7 +392,7 @@ int mic_virtio_config_change(struct mic_vdev *mvdev, } if (copy_from_user(mic_vq_configspace(mvdev->dd), - argp, mvdev->dd->config_len)) { + argp, mvdev->dd->config_len)) { dev_err(mic_dev(mvdev), "%s %d err %d\n", __func__, __LINE__, -EFAULT); ret = -EFAULT; @@ -438,8 +436,8 @@ static int mic_copy_dp_entry(struct mic_vdev *mvdev, return -EFAULT; } - if (mic_aligned_desc_size(&dd) > MIC_MAX_DESC_BLK_SIZE - || dd.num_vq > MIC_MAX_VRINGS) { + if (mic_aligned_desc_size(&dd) > MIC_MAX_DESC_BLK_SIZE || + dd.num_vq > MIC_MAX_VRINGS) { dev_err(mic_dev(mvdev), "%s %d err %d\n", __func__, __LINE__, -EINVAL); return -EINVAL; @@ -503,7 +501,7 @@ static void mic_init_device_ctrl(struct mic_vdev *mvdev, { struct mic_device_ctrl *dc; - dc = mvdev->dc = (void *)devpage + mic_aligned_desc_size(devpage); + dc = (void *)devpage + mic_aligned_desc_size(devpage); dc->config_change = 0; dc->guest_ack = 0; @@ -512,6 +510,7 @@ static void mic_init_device_ctrl(struct mic_vdev *mvdev, dc->used_address_updated = 0; dc->c2h_vdev_db = -1; dc->h2c_vdev_db = -1; + mvdev->dc = dc; } int mic_virtio_add_device(struct mic_vdev *mvdev, @@ -551,7 +550,7 @@ int mic_virtio_add_device(struct mic_vdev *mvdev, sizeof(struct _mic_vring_info)); vr->va = (void *) __get_free_pages(GFP_KERNEL | __GFP_ZERO, - get_order(vr_size)); + get_order(vr_size)); if (!vr->va) { ret = -ENOMEM; dev_err(mic_dev(mvdev), "%s %d err %d\n", @@ -564,8 +563,7 @@ int mic_virtio_add_device(struct mic_vdev *mvdev, vqconfig[i].address = mic_map_single(mdev, vr->va, vr_size); if (mic_map_error(vqconfig[i].address)) { - free_pages((unsigned long)vr->va, - get_order(vr_size)); + free_pages((unsigned long)vr->va, get_order(vr_size)); ret = -ENOMEM; dev_err(mic_dev(mvdev), "%s %d err %d\n", __func__, __LINE__, ret); @@ -573,8 +571,7 @@ int mic_virtio_add_device(struct mic_vdev *mvdev, } vqconfig[i].address = cpu_to_le64(vqconfig[i].address); - vring_init(&vr->vr, num, - vr->va, MIC_VIRTIO_RING_ALIGN); + vring_init(&vr->vr, num, vr->va, MIC_VIRTIO_RING_ALIGN); ret = vringh_init_kern(&mvr->vrh, *(u32 *)mic_vq_features(mvdev->dd), num, false, vr->vr.desc, vr->vr.avail, vr->vr.used); @@ -593,8 +590,8 @@ int mic_virtio_add_device(struct mic_vdev *mvdev, __func__, __LINE__, i, vr->va, vr->info, vr_size); } - snprintf(irqname, sizeof(irqname), - "mic%dvirtio%d", mdev->id, mvdev->virtio_id); + snprintf(irqname, sizeof(irqname), "mic%dvirtio%d", mdev->id, + mvdev->virtio_id); mvdev->virtio_db = mic_next_db(mdev); mvdev->virtio_cookie = mic_request_irq(mdev, mic_virtio_intr_handler, irqname, mvdev, mvdev->virtio_db, MIC_INTR_DB); @@ -628,9 +625,9 @@ err: for (j = 0; j < i; j++) { struct mic_vringh *mvr = &mvdev->mvr[j]; mic_unmap_single(mdev, le64_to_cpu(vqconfig[j].address), - mvr->vring.len); + mvr->vring.len); free_pages((unsigned long)mvr->vring.va, - get_order(mvr->vring.len)); + get_order(mvr->vring.len)); } mutex_unlock(&mdev->mic_mutex); return ret; @@ -676,9 +673,9 @@ skip_hot_remove: vringh_kiov_cleanup(&mvr->riov); vringh_kiov_cleanup(&mvr->wiov); mic_unmap_single(mdev, le64_to_cpu(vqconfig[i].address), - mvr->vring.len); + mvr->vring.len); free_pages((unsigned long)mvr->vring.va, - get_order(mvr->vring.len)); + get_order(mvr->vring.len)); } list_for_each_safe(pos, tmp, &mdev->vdev_list) { diff --git a/drivers/misc/mic/host/mic_x100.c b/drivers/misc/mic/host/mic_x100.c index 3a0d660..81e9541 100644 --- a/drivers/misc/mic/host/mic_x100.c +++ b/drivers/misc/mic/host/mic_x100.c @@ -46,8 +46,8 @@ mic_x100_write_spad(struct mic_device *mdev, unsigned int idx, u32 val) dev_dbg(mdev->sdev->parent, "Writing 0x%x to scratch pad index %d\n", val, idx); mic_mmio_write(&mdev->mmio, val, - MIC_X100_SBOX_BASE_ADDRESS + - MIC_X100_SBOX_SPAD0 + idx * 4); + MIC_X100_SBOX_BASE_ADDRESS + + MIC_X100_SBOX_SPAD0 + idx * 4); } /** @@ -130,8 +130,8 @@ static void mic_x100_send_sbox_intr(struct mic_device *mdev, { struct mic_mw *mw = &mdev->mmio; u64 apic_icr_offset = MIC_X100_SBOX_APICICR0 + doorbell * 8; - u32 apicicr_low = mic_mmio_read(mw, - MIC_X100_SBOX_BASE_ADDRESS + apic_icr_offset); + u32 apicicr_low = mic_mmio_read(mw, MIC_X100_SBOX_BASE_ADDRESS + + apic_icr_offset); /* for MIC we need to make sure we "hit" the send_icr bit (13) */ apicicr_low = (apicicr_low | (1 << 13)); @@ -139,7 +139,7 @@ static void mic_x100_send_sbox_intr(struct mic_device *mdev, /* Ensure that the interrupt is ordered w.r.t. previous stores. */ wmb(); mic_mmio_write(mw, apicicr_low, - MIC_X100_SBOX_BASE_ADDRESS + apic_icr_offset); + MIC_X100_SBOX_BASE_ADDRESS + apic_icr_offset); } /** @@ -153,7 +153,7 @@ static void mic_x100_send_rdmasr_intr(struct mic_device *mdev, /* Ensure that the interrupt is ordered w.r.t. previous stores. */ wmb(); mic_mmio_write(&mdev->mmio, 0, - MIC_X100_SBOX_BASE_ADDRESS + rdmasr_offset); + MIC_X100_SBOX_BASE_ADDRESS + rdmasr_offset); } /** @@ -212,7 +212,7 @@ done: */ static void mic_x100_hw_intr_init(struct mic_device *mdev) { - mdev->intr_info = (struct mic_intr_info *) mic_x100_intr_init; + mdev->intr_info = (struct mic_intr_info *)mic_x100_intr_init; } /** @@ -244,7 +244,7 @@ mic_x100_read_msi_to_src_map(struct mic_device *mdev, int idx) */ static void mic_x100_program_msi_to_src_map(struct mic_device *mdev, - int idx, int offset, bool set) + int idx, int offset, bool set) { unsigned long reg; struct mic_mw *mw = &mdev->mmio; @@ -308,12 +308,12 @@ static void mic_x100_send_firmware_intr(struct mic_device *mdev) apicicr_low = (vector | (1 << 13)); mic_mmio_write(mw, mic_x100_get_apic_id(mdev), - MIC_X100_SBOX_BASE_ADDRESS + apic_icr_offset + 4); + MIC_X100_SBOX_BASE_ADDRESS + apic_icr_offset + 4); /* Ensure that the interrupt is ordered w.r.t. previous stores. */ wmb(); mic_mmio_write(mw, apicicr_low, - MIC_X100_SBOX_BASE_ADDRESS + apic_icr_offset); + MIC_X100_SBOX_BASE_ADDRESS + apic_icr_offset); } /** @@ -365,8 +365,7 @@ mic_x100_load_command_line(struct mic_device *mdev, const struct firmware *fw) len += snprintf(buf, CMDLINE_SIZE - len, " mem=%dM", boot_mem); if (mdev->cmdline) - snprintf(buf + len, CMDLINE_SIZE - len, - " %s", mdev->cmdline); + snprintf(buf + len, CMDLINE_SIZE - len, " %s", mdev->cmdline); memcpy_toio(cmd_line_va, buf, strlen(buf) + 1); kfree(buf); return 0; @@ -397,8 +396,7 @@ mic_x100_load_ramdisk(struct mic_device *mdev) * Typically the bootaddr for card OS is 64M * so copy over the ramdisk @ 128M. */ - memcpy_toio(mdev->aper.va + (mdev->bootaddr << 1), - fw->data, fw->size); + memcpy_toio(mdev->aper.va + (mdev->bootaddr << 1), fw->data, fw->size); iowrite32(cpu_to_le32(mdev->bootaddr << 1), &bp->hdr.ramdisk_image); iowrite32(cpu_to_le32(fw->size), &bp->hdr.ramdisk_size); release_firmware(fw); @@ -484,8 +482,7 @@ mic_x100_load_firmware(struct mic_device *mdev, const char *buf) if (mdev->ramdisk) rc = mic_x100_load_ramdisk(mdev); error: - dev_dbg(mdev->sdev->parent, "%s %d rc %d\n", - __func__, __LINE__, rc); + dev_dbg(mdev->sdev->parent, "%s %d rc %d\n", __func__, __LINE__, rc); done: return rc; } @@ -524,8 +521,8 @@ mic_x100_smpt_set(struct mic_device *mdev, dma_addr_t dma_addr, u8 index) uint32_t smpt_reg_val = BUILD_SMPT(SNOOP_ON, dma_addr >> mdev->smpt->info.page_shift); mic_mmio_write(&mdev->mmio, smpt_reg_val, - MIC_X100_SBOX_BASE_ADDRESS + - MIC_X100_SBOX_SMPT00 + (4 * index)); + MIC_X100_SBOX_BASE_ADDRESS + + MIC_X100_SBOX_SMPT00 + (4 * index)); } /** -- cgit v0.10.2 From b019ba959fc4600a646bb9d732149b1b0056f88a Mon Sep 17 00:00:00 2001 From: Sudeep Dutt Date: Fri, 27 Sep 2013 09:50:06 -0700 Subject: misc: mic: fix a warning in the IOCTL header file. The following warning from mic_ioctl.h is fixed via this patch: found __[us]{8,16,32,64} type without #include Reported-by: Greg Kroah-Hartman Signed-off-by: Ashutosh Dixit Signed-off-by: Dasaratharaman Chandramouli Signed-off-by: Nikhil Rao Signed-off-by: Harshavardhan R Kharche Signed-off-by: Sudeep Dutt Signed-off-by: Greg Kroah-Hartman diff --git a/include/uapi/linux/mic_ioctl.h b/include/uapi/linux/mic_ioctl.h index 0e6cbf3..7fabba5 100644 --- a/include/uapi/linux/mic_ioctl.h +++ b/include/uapi/linux/mic_ioctl.h @@ -21,6 +21,8 @@ #ifndef _MIC_IOCTL_H_ #define _MIC_IOCTL_H_ +#include + /* * mic_copy - MIC virtio descriptor copy. * -- cgit v0.10.2 From 80d26236c7c583ea2f43b68008ab7e067102c62c Mon Sep 17 00:00:00 2001 From: Sudeep Dutt Date: Fri, 27 Sep 2013 09:50:16 -0700 Subject: misc: mic: depend on X86 for both host and card drivers. This fixes build failures seen on certain non X86 architectures. The card driver should correctly always depend on X86. The host driver can potentially work on non X86 architectures although it has never been built or validated in such configurations. The host driver dependency on X86 can be removed at some point in the future but this workaround is required for now. Reported-by: Fengguang Wu Reported-by: Stephen Rothwell Signed-off-by: Ashutosh Dixit Signed-off-by: Dasaratharaman Chandramouli Signed-off-by: Nikhil Rao Signed-off-by: Harshavardhan R Kharche Signed-off-by: Sudeep Dutt Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/misc/mic/Kconfig b/drivers/misc/mic/Kconfig index d453768..e42b331 100644 --- a/drivers/misc/mic/Kconfig +++ b/drivers/misc/mic/Kconfig @@ -2,7 +2,7 @@ comment "Intel MIC Host Driver" config INTEL_MIC_HOST tristate "Intel MIC Host Driver" - depends on 64BIT && PCI + depends on 64BIT && PCI && X86 select VHOST_RING default N help @@ -23,7 +23,7 @@ comment "Intel MIC Card Driver" config INTEL_MIC_CARD tristate "Intel MIC Card Driver" - depends on 64BIT + depends on 64BIT && X86 select VIRTIO default N help -- cgit v0.10.2 From e036cc5727eb6d471442d2a9218990aa11215400 Mon Sep 17 00:00:00 2001 From: Tomas Winkler Date: Mon, 16 Sep 2013 23:44:46 +0300 Subject: mei: simplify mei_open error handling 1. Perform simple checks first and only then attempt to allocate cl structure. 2. Remove open_handler_count test, this is already checked in mei_cl_link function 3. return -EMFILE instead of -ENOENT as expected by user space Signed-off-by: Tomas Winkler Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/misc/mei/client.c b/drivers/misc/mei/client.c index fbd319c..88770e0 100644 --- a/drivers/misc/mei/client.c +++ b/drivers/misc/mei/client.c @@ -288,7 +288,13 @@ int mei_cl_link(struct mei_cl *cl, int id) if (id >= MEI_CLIENTS_MAX) { dev_err(&dev->pdev->dev, "id exceded %d", MEI_CLIENTS_MAX) ; - return -ENOENT; + return -EMFILE; + } + + if (dev->open_handle_count >= MEI_MAX_OPEN_HANDLE_COUNT) { + dev_err(&dev->pdev->dev, "open_handle_count exceded %d", + MEI_MAX_OPEN_HANDLE_COUNT); + return -EMFILE; } if (dev->open_handle_count >= MEI_MAX_OPEN_HANDLE_COUNT) { diff --git a/drivers/misc/mei/main.c b/drivers/misc/mei/main.c index c71420e..87ab5ca 100644 --- a/drivers/misc/mei/main.c +++ b/drivers/misc/mei/main.c @@ -60,48 +60,45 @@ static int mei_open(struct inode *inode, struct file *file) int err; - err = -ENODEV; if (!misc->parent) - goto out; + return -ENODEV; pdev = container_of(misc->parent, struct pci_dev, dev); dev = pci_get_drvdata(pdev); if (!dev) - goto out; + return -ENODEV; mutex_lock(&dev->device_lock); - err = -ENOMEM; - cl = mei_cl_allocate(dev); - if (!cl) - goto out_unlock; + + cl = NULL; err = -ENODEV; if (dev->dev_state != MEI_DEV_ENABLED) { dev_dbg(&dev->pdev->dev, "dev_state != MEI_ENABLED dev_state = %s\n", mei_dev_state_str(dev->dev_state)); - goto out_unlock; - } - err = -EMFILE; - if (dev->open_handle_count >= MEI_MAX_OPEN_HANDLE_COUNT) { - dev_err(&dev->pdev->dev, "open_handle_count exceded %d", - MEI_MAX_OPEN_HANDLE_COUNT); - goto out_unlock; + goto err_unlock; } + err = -ENOMEM; + cl = mei_cl_allocate(dev); + if (!cl) + goto err_unlock; + + /* open_handle_count check is handled in the mei_cl_link */ err = mei_cl_link(cl, MEI_HOST_CLIENT_ID_ANY); if (err) - goto out_unlock; + goto err_unlock; file->private_data = cl; + mutex_unlock(&dev->device_lock); return nonseekable_open(inode, file); -out_unlock: +err_unlock: mutex_unlock(&dev->device_lock); kfree(cl); -out: return err; } -- cgit v0.10.2 From 22f96a0eb6c62b570621d77dacbf2589a6de2997 Mon Sep 17 00:00:00 2001 From: Tomas Winkler Date: Mon, 16 Sep 2013 23:44:47 +0300 Subject: mei: revamp open handler counts Make open counter to be incremented and decremented from mei_cl_link and mei_cl_unlik function respectively Nfc was assuming symmetric linking API and thus open handler count was never decreased. This patch fixes that. We need to add separate open hander count for amthif which is handled out of link/unlink functions and doesn't break the symmetric API. Last we do not waste clients slots if amthif or wd are not present in the device. we don't need to allocates slots ahead it is all covered by link/unlink before the devices is responding to user space connection and thus not racing on allocation Signed-off-by: Tomas Winkler Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/misc/mei/amthif.c b/drivers/misc/mei/amthif.c index 226c3f3..4f259d4 100644 --- a/drivers/misc/mei/amthif.c +++ b/drivers/misc/mei/amthif.c @@ -58,6 +58,7 @@ void mei_amthif_reset_params(struct mei_device *dev) dev->iamthif_state = MEI_IAMTHIF_IDLE; dev->iamthif_timer = 0; dev->iamthif_stall_timer = 0; + dev->iamthif_open_count = 0; } /** @@ -731,8 +732,8 @@ static bool mei_clear_lists(struct mei_device *dev, struct file *file) */ int mei_amthif_release(struct mei_device *dev, struct file *file) { - if (dev->open_handle_count > 0) - dev->open_handle_count--; + if (dev->iamthif_open_count > 0) + dev->iamthif_open_count--; if (dev->iamthif_file_object == file && dev->iamthif_state != MEI_IAMTHIF_IDLE) { diff --git a/drivers/misc/mei/client.c b/drivers/misc/mei/client.c index 88770e0..a48c0e7 100644 --- a/drivers/misc/mei/client.c +++ b/drivers/misc/mei/client.c @@ -275,6 +275,7 @@ struct mei_cl_cb *mei_cl_find_read_cb(struct mei_cl *cl) int mei_cl_link(struct mei_cl *cl, int id) { struct mei_device *dev; + long open_handle_count; if (WARN_ON(!cl || !cl->dev)) return -EINVAL; @@ -291,7 +292,8 @@ int mei_cl_link(struct mei_cl *cl, int id) return -EMFILE; } - if (dev->open_handle_count >= MEI_MAX_OPEN_HANDLE_COUNT) { + open_handle_count = dev->open_handle_count + dev->iamthif_open_count; + if (open_handle_count >= MEI_MAX_OPEN_HANDLE_COUNT) { dev_err(&dev->pdev->dev, "open_handle_count exceded %d", MEI_MAX_OPEN_HANDLE_COUNT); return -EMFILE; @@ -337,6 +339,17 @@ int mei_cl_unlink(struct mei_cl *cl) cl_dbg(dev, cl, "unlink client"); + if (dev->open_handle_count > 0) + dev->open_handle_count--; + + /* never clear the 0 bit */ + if (cl->host_client_id) + clear_bit(cl->host_client_id, dev->host_clients_map); + + list_del_init(&cl->link); + + cl->state = MEI_FILE_INITIALIZING; + list_del_init(&cl->link); return 0; @@ -358,10 +371,8 @@ void mei_host_client_init(struct work_struct *work) /* * Reserving the first three client IDs * 0: Reserved for MEI Bus Message communications - * 1: Reserved for Watchdog - * 2: Reserved for AMTHI */ - bitmap_set(dev->host_clients_map, 0, 3); + bitmap_set(dev->host_clients_map, 0, 1); for (i = 0; i < dev->me_clients_num; i++) { client_props = &dev->me_clients[i].props; diff --git a/drivers/misc/mei/init.c b/drivers/misc/mei/init.c index 6197018..a7d29a7 100644 --- a/drivers/misc/mei/init.c +++ b/drivers/misc/mei/init.c @@ -165,12 +165,7 @@ void mei_reset(struct mei_device *dev, int interrupts_enabled) /* remove entry if already in list */ dev_dbg(&dev->pdev->dev, "remove iamthif and wd from the file list.\n"); mei_cl_unlink(&dev->wd_cl); - if (dev->open_handle_count > 0) - dev->open_handle_count--; mei_cl_unlink(&dev->iamthif_cl); - if (dev->open_handle_count > 0) - dev->open_handle_count--; - mei_amthif_reset_params(dev); memset(&dev->wr_ext_msg, 0, sizeof(dev->wr_ext_msg)); } diff --git a/drivers/misc/mei/main.c b/drivers/misc/mei/main.c index 87ab5ca..9661a81 100644 --- a/drivers/misc/mei/main.c +++ b/drivers/misc/mei/main.c @@ -141,10 +141,6 @@ static int mei_release(struct inode *inode, struct file *file) cl->host_client_id, cl->me_client_id); - if (dev->open_handle_count > 0) { - clear_bit(cl->host_client_id, dev->host_clients_map); - dev->open_handle_count--; - } mei_cl_unlink(cl); @@ -498,11 +494,11 @@ static int mei_ioctl_connect_client(struct file *file, rets = -ENODEV; goto end; } - clear_bit(cl->host_client_id, dev->host_clients_map); mei_cl_unlink(cl); kfree(cl); cl = NULL; + dev->iamthif_open_count++; file->private_data = &dev->iamthif_cl; client = &data->out_client_properties; diff --git a/drivers/misc/mei/mei_dev.h b/drivers/misc/mei/mei_dev.h index 456b322..406f68e 100644 --- a/drivers/misc/mei/mei_dev.h +++ b/drivers/misc/mei/mei_dev.h @@ -414,6 +414,7 @@ struct mei_device { struct file *iamthif_file_object; struct mei_cl iamthif_cl; struct mei_cl_cb *iamthif_current_cb; + long iamthif_open_count; int iamthif_mtu; unsigned long iamthif_timer; u32 iamthif_stall_timer; -- cgit v0.10.2 From af68fb65557dee58918822a470cd64fd76ed837e Mon Sep 17 00:00:00 2001 From: Tomas Winkler Date: Mon, 16 Sep 2013 23:44:48 +0300 Subject: mei: amthif: mei_amthif_host_init: propagate errors from called functions propagate error codes from called functions, they are correct. Signed-off-by: Tomas Winkler Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/misc/mei/amthif.c b/drivers/misc/mei/amthif.c index 4f259d4..d22c686 100644 --- a/drivers/misc/mei/amthif.c +++ b/drivers/misc/mei/amthif.c @@ -79,8 +79,10 @@ int mei_amthif_host_init(struct mei_device *dev) i = mei_me_cl_by_uuid(dev, &mei_amthif_guid); if (i < 0) { - dev_info(&dev->pdev->dev, "amthif: failed to find the client\n"); - return -ENOENT; + ret = i; + dev_info(&dev->pdev->dev, + "amthif: failed to find the client %d\n", ret); + return ret; } cl->me_client_id = dev->me_clients[i].client_id; @@ -107,8 +109,9 @@ int mei_amthif_host_init(struct mei_device *dev) ret = mei_cl_link(cl, MEI_IAMTHIF_HOST_CLIENT_ID); if (ret < 0) { - dev_err(&dev->pdev->dev, "amthif: failed link client\n"); - return -ENOENT; + dev_err(&dev->pdev->dev, + "amthif: failed link client %d\n", ret); + return ret; } cl->state = MEI_FILE_CONNECTING; -- cgit v0.10.2 From 6078e0bef2236f323377c53e77e26288fb52ee23 Mon Sep 17 00:00:00 2001 From: Sudeep Dutt Date: Wed, 2 Oct 2013 16:09:00 -0700 Subject: misc: mic: Fix build issues in sample daemon. Specifying gcc format function attribute for mpsslog(..) and building on 32 bit systems exposed a few build issues in the sample MIC daemon which are fixed by this patch. Some of these changes were authored by Joe Perches @ https://lkml.org/lkml/2013/9/27/419 Reported-by: Joe Perches Signed-off-by: Ashutosh Dixit Signed-off-by: Caz Yokoyama Signed-off-by: Sudeep Dutt Signed-off-by: Greg Kroah-Hartman diff --git a/Documentation/mic/mpssd/mpssd.c b/Documentation/mic/mpssd/mpssd.c index f9327a2..82c6bc2 100644 --- a/Documentation/mic/mpssd/mpssd.c +++ b/Documentation/mic/mpssd/mpssd.c @@ -362,9 +362,9 @@ static inline void verify_out_len(struct mic_info *mic, struct mic_copy_desc *copy) { if (copy->out_len != sum_iovec_len(copy)) { - mpsslog("%s %s %d BUG copy->out_len 0x%x len 0x%x\n", + mpsslog("%s %s %d BUG copy->out_len 0x%x len 0x%zx\n", mic->name, __func__, __LINE__, - copy->out_len, sum_iovec_len(copy)); + copy->out_len, sum_iovec_len(copy)); assert(copy->out_len == sum_iovec_len(copy)); } } @@ -377,7 +377,7 @@ disp_iovec(struct mic_info *mic, struct mic_copy_desc *copy, int i; for (i = 0; i < copy->iovcnt; i++) - mpsslog("%s %s %d copy->iov[%d] addr %p len 0x%lx\n", + mpsslog("%s %s %d copy->iov[%d] addr %p len 0x%zx\n", mic->name, s, line, i, copy->iov[i].iov_base, copy->iov[i].iov_len); } @@ -630,7 +630,7 @@ virtio_net(void *arg) disp_iovec(mic, ©, __func__, __LINE__); mpsslog("%s %s %d read failed %s ", mic->name, __func__, __LINE__, strerror(errno)); - mpsslog("cnt %d sum %d\n", + mpsslog("cnt %d sum %zd\n", copy.iovcnt, sum_iovec_len(©)); } } @@ -680,8 +680,8 @@ virtio_net(void *arg) if (len != sum_iovec_len(©)) { mpsslog("Tun write failed %s ", strerror(errno)); - mpsslog("len 0x%x ", len); - mpsslog("read_len 0x%x\n", + mpsslog("len 0x%zx ", len); + mpsslog("read_len 0x%zx\n", sum_iovec_len(©)); } else { #ifdef DEBUG @@ -827,7 +827,7 @@ virtio_console(void *arg) mpsslog("%s %s %d read failed %s ", mic->name, __func__, __LINE__, strerror(errno)); - mpsslog("cnt %d sum %d\n", + mpsslog("cnt %d sum %zd\n", copy.iovcnt, sum_iovec_len(©)); } } @@ -859,8 +859,8 @@ virtio_console(void *arg) if (len != sum_iovec_len(©)) { mpsslog("Tun write failed %s ", strerror(errno)); - mpsslog("len 0x%x ", len); - mpsslog("read_len 0x%x\n", + mpsslog("len 0x%zx ", len); + mpsslog("read_len 0x%zx\n", sum_iovec_len(©)); } else { #ifdef DEBUG @@ -953,7 +953,7 @@ set_backend_file(struct mic_info *mic) return false; mic->mic_virtblk.backend_file = malloc(strlen(evv) + 1); if (mic->mic_virtblk.backend_file == NULL) { - mpsslog("can't allocate memory\n", mic->name, mic->id); + mpsslog("%s %d can't allocate memory\n", mic->name, mic->id); return false; } strcpy(mic->mic_virtblk.backend_file, evv + 1); @@ -1026,7 +1026,7 @@ close_backend(struct mic_info *mic) static bool start_virtblk(struct mic_info *mic, struct mic_vring *vring) { - if (((__u64)&virtblk_dev_page.blk_config % 8) != 0) { + if (((unsigned long)&virtblk_dev_page.blk_config % 8) != 0) { mpsslog("%s: blk_config is not 8 byte aligned.\n", mic->name); return false; diff --git a/Documentation/mic/mpssd/mpssd.h b/Documentation/mic/mpssd/mpssd.h index b6dee38..ccd589f 100644 --- a/Documentation/mic/mpssd/mpssd.h +++ b/Documentation/mic/mpssd/mpssd.h @@ -94,6 +94,7 @@ struct mic_info { struct mic_info *next; }; +__attribute__((format(printf, 1, 2))) void mpsslog(char *format, ...); char *readsysfs(char *dir, char *entry); int setsysfs(char *dir, char *entry, char *value); -- cgit v0.10.2 From bc3426897652c05825f8fd5fc1727aac83a2d8bd Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Wed, 2 Oct 2013 13:46:47 +0200 Subject: misc: bh1780: probe from compatible string Currently the BH1780GLI I2C driver relies on the device tree node having the right name, but this is fragile. Use the compatible string to probe the driver instead. Acked-by: Wolfram Sang Signed-off-by: Linus Walleij Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/misc/bh1780gli.c b/drivers/misc/bh1780gli.c index 057580e..48ea33d 100644 --- a/drivers/misc/bh1780gli.c +++ b/drivers/misc/bh1780gli.c @@ -23,6 +23,7 @@ #include #include #include +#include #define BH1780_REG_CONTROL 0x80 #define BH1780_REG_PARTID 0x8A @@ -244,6 +245,15 @@ static const struct i2c_device_id bh1780_id[] = { { }, }; +#ifdef CONFIG_OF +static const struct of_device_id of_bh1780_match[] = { + { .compatible = "rohm,bh1780gli", }, + {}, +}; + +MODULE_DEVICE_TABLE(of, of_bh1780_match); +#endif + static struct i2c_driver bh1780_driver = { .probe = bh1780_probe, .remove = bh1780_remove, @@ -251,6 +261,7 @@ static struct i2c_driver bh1780_driver = { .driver = { .name = "bh1780", .pm = &bh1780_pm, + .of_match_table = of_match_ptr(of_bh1780_match), }, }; -- cgit v0.10.2 From 12b3af3096cdfb0613da374021167868c6abc9ce Mon Sep 17 00:00:00 2001 From: Michal Simek Date: Mon, 30 Sep 2013 09:28:36 +0200 Subject: char: hwicap: Remove unnecessary dev_set_drvdata() Driver core clears the driver data to NULL after device_release or on probe failure, so just remove it from here. Signed-off-by: Michal Simek Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/char/xilinx_hwicap/xilinx_hwicap.c b/drivers/char/xilinx_hwicap/xilinx_hwicap.c index a7f65c2..f6345f9 100644 --- a/drivers/char/xilinx_hwicap/xilinx_hwicap.c +++ b/drivers/char/xilinx_hwicap/xilinx_hwicap.c @@ -731,7 +731,6 @@ static int hwicap_remove(struct device *dev) iounmap(drvdata->base_address); release_mem_region(drvdata->mem_start, drvdata->mem_size); kfree(drvdata); - dev_set_drvdata(dev, NULL); mutex_lock(&icap_sem); probed_devices[MINOR(dev->devt)-XHWICAP_MINOR] = 0; -- cgit v0.10.2 From af190494f9b2e1fb6e1c039e9626c3c334717da1 Mon Sep 17 00:00:00 2001 From: Dasaratharaman Chandramouli Date: Thu, 3 Oct 2013 18:06:23 -0700 Subject: misc: mic: Enable OSPM suspend and resume support. This patch enables support for OSPM suspend and resume in the MIC driver. During a host suspend event, the driver performs an orderly shutdown of the cards if they are online. Upon resume, any cards that were previously online before suspend are rebooted. The driver performs an orderly shutdown of the card primarily to ensure that applications in the card are terminated and mounted devices are safely un-mounted before the card is powered down in the event of an OSPM suspend. The driver makes use of the MIC daemon to accomplish OSPM suspend and resume. The driver registers a PM notifier per MIC device. The devices get notified synchronously during PM_SUSPEND_PREPARE and PM_POST_SUSPEND phases. During the PM_SUSPEND_PREPARE phase, the driver performs one of the following three tasks. 1) If the card is 'offline', the driver sets the card to a 'suspended' state and returns. 2) If the card is 'online', the driver initiates card shutdown by setting the card state to suspending. This notifies the MIC daemon which invokes shutdown and sets card state to 'suspended'. The driver returns after the shutdown is complete. 3) If the card is already being shutdown, possibly by a host user space application, the driver sets the card state to 'suspended' and returns after the shutdown is complete. During the PM_POST_SUSPEND phase, the driver simply notifies the daemon and returns. The daemon boots those cards that were previously online during the suspend phase. Signed-off-by: Ashutosh Dixit Signed-off-by: Nikhil Rao Signed-off-by: Harshavardhan R Kharche Signed-off-by: Sudeep Dutt Signed-off-by: Dasaratharaman Chandramouli Signed-off-by: Greg Kroah-Hartman diff --git a/Documentation/ABI/testing/sysfs-class-mic.txt b/Documentation/ABI/testing/sysfs-class-mic.txt index 82cdad3..13f48af 100644 --- a/Documentation/ABI/testing/sysfs-class-mic.txt +++ b/Documentation/ABI/testing/sysfs-class-mic.txt @@ -1,6 +1,6 @@ What: /sys/class/mic/ -Date: August 2013 -KernelVersion: 3.11 +Date: October 2013 +KernelVersion: 3.13 Contact: Sudeep Dutt Description: The mic class directory belongs to Intel MIC devices and @@ -9,8 +9,8 @@ Description: Integrated Core (MIC) architecture that runs a Linux OS. What: /sys/class/mic/mic(x) -Date: August 2013 -KernelVersion: 3.11 +Date: October 2013 +KernelVersion: 3.13 Contact: Sudeep Dutt Description: The directories /sys/class/mic/mic0, /sys/class/mic/mic1 etc., @@ -18,33 +18,41 @@ Description: information specific to that MIC device. What: /sys/class/mic/mic(x)/family -Date: August 2013 -KernelVersion: 3.11 +Date: October 2013 +KernelVersion: 3.13 Contact: Sudeep Dutt Description: Provides information about the Coprocessor family for an Intel MIC device. For example - "x100" What: /sys/class/mic/mic(x)/stepping -Date: August 2013 -KernelVersion: 3.11 +Date: October 2013 +KernelVersion: 3.13 Contact: Sudeep Dutt Description: Provides information about the silicon stepping for an Intel MIC device. For example - "A0" or "B0" What: /sys/class/mic/mic(x)/state -Date: August 2013 -KernelVersion: 3.11 +Date: October 2013 +KernelVersion: 3.13 Contact: Sudeep Dutt Description: When read, this entry provides the current state of an Intel MIC device in the context of the card OS. Possible values that will be read are: - "offline" - The MIC device is ready to boot the card OS. + "offline" - The MIC device is ready to boot the card OS. On + reading this entry after an OSPM resume, a "boot" has to be + written to this entry if the card was previously shutdown + during OSPM suspend. "online" - The MIC device has initiated booting a card OS. "shutting_down" - The card OS is shutting down. "reset_failed" - The MIC device has failed to reset. + "suspending" - The MIC device is currently being prepared for + suspend. On reading this entry, a "suspend" has to be written + to the state sysfs entry to ensure the card is shutdown during + OSPM suspend. + "suspended" - The MIC device has been suspended. When written, this sysfs entry triggers different state change operations depending upon the current state of the card OS. @@ -54,10 +62,12 @@ Description: sysfs entries. "reset" - Initiates device reset. "shutdown" - Initiates card OS shutdown. + "suspend" - Initiates card OS shutdown and also marks the card + as suspended. What: /sys/class/mic/mic(x)/shutdown_status -Date: August 2013 -KernelVersion: 3.11 +Date: October 2013 +KernelVersion: 3.13 Contact: Sudeep Dutt Description: An Intel MIC device runs a Linux OS during its operation. This @@ -72,8 +82,8 @@ Description: "restart" - Shutdown because of a restart command. What: /sys/class/mic/mic(x)/cmdline -Date: August 2013 -KernelVersion: 3.11 +Date: October 2013 +KernelVersion: 3.13 Contact: Sudeep Dutt Description: An Intel MIC device runs a Linux OS during its operation. Before @@ -88,8 +98,8 @@ Description: line back to this entry. What: /sys/class/mic/mic(x)/firmware -Date: August 2013 -KernelVersion: 3.11 +Date: October 2013 +KernelVersion: 3.13 Contact: Sudeep Dutt Description: When read, this sysfs entry provides the path name under @@ -98,8 +108,8 @@ Description: firmware image location under /lib/firmware/. What: /sys/class/mic/mic(x)/ramdisk -Date: August 2013 -KernelVersion: 3.11 +Date: October 2013 +KernelVersion: 3.13 Contact: Sudeep Dutt Description: When read, this sysfs entry provides the path name under @@ -108,8 +118,8 @@ Description: the ramdisk image location under /lib/firmware/. What: /sys/class/mic/mic(x)/bootmode -Date: August 2013 -KernelVersion: 3.11 +Date: October 2013 +KernelVersion: 3.13 Contact: Sudeep Dutt Description: When read, this sysfs entry provides the current bootmode for @@ -119,8 +129,8 @@ Description: b) elf - Boot an elf image for flash updates. What: /sys/class/mic/mic(x)/log_buf_addr -Date: August 2013 -KernelVersion: 3.11 +Date: October 2013 +KernelVersion: 3.13 Contact: Sudeep Dutt Description: An Intel MIC device runs a Linux OS during its operation. For @@ -133,8 +143,8 @@ Description: file of the card OS. What: /sys/class/mic/mic(x)/log_buf_len -Date: August 2013 -KernelVersion: 3.11 +Date: October 2013 +KernelVersion: 3.13 Contact: Sudeep Dutt Description: An Intel MIC device runs a Linux OS during its operation. For diff --git a/Documentation/mic/mic_overview.txt b/Documentation/mic/mic_overview.txt index c4424ed..b419292 100644 --- a/Documentation/mic/mic_overview.txt +++ b/Documentation/mic/mic_overview.txt @@ -4,7 +4,9 @@ that runs a Linux OS. It is a PCIe endpoint in a platform and therefore implements the three required standard address spaces i.e. configuration, memory and I/O. The host OS loads a device driver as is typical for PCIe devices. The card itself runs a bootstrap after reset that -transfers control to the card OS downloaded from the host driver. +transfers control to the card OS downloaded from the host driver. The +host driver supports OSPM suspend and resume operations. It shuts down +the card during suspend and reboots the card OS during resume. The card OS as shipped by Intel is a Linux kernel with modifications for the X100 devices. diff --git a/Documentation/mic/mpssd/mpssd.c b/Documentation/mic/mpssd/mpssd.c index 82c6bc2..0c980ad 100644 --- a/Documentation/mic/mpssd/mpssd.c +++ b/Documentation/mic/mpssd/mpssd.c @@ -1295,7 +1295,13 @@ reset(struct mic_info *mic) goto retry; mpsslog("%s: %s %d state %s\n", mic->name, __func__, __LINE__, state); - if ((!strcmp(state, "offline"))) { + + /* + * If the shutdown was initiated by OSPM, the state stays + * in "suspended" which is also a valid condition for reset. + */ + if ((!strcmp(state, "offline")) || + (!strcmp(state, "suspended"))) { free(state); break; } @@ -1334,6 +1340,10 @@ static int get_mic_state(struct mic_info *mic, char *state) return MIC_SHUTTING_DOWN; if (!strcmp(state, "reset_failed")) return MIC_RESET_FAILED; + if (!strcmp(state, "suspending")) + return MIC_SUSPENDING; + if (!strcmp(state, "suspended")) + return MIC_SUSPENDED; mpsslog("%s: BUG invalid state %s\n", mic->name, state); /* Invalid state */ assert(0); @@ -1418,6 +1428,17 @@ retry: case MIC_SHUTTING_DOWN: mic_handle_shutdown(mic); goto close_error; + case MIC_SUSPENDING: + mic->boot_on_resume = 1; + setsysfs(mic->name, "state", "suspend"); + mic_handle_shutdown(mic); + goto close_error; + case MIC_OFFLINE: + if (mic->boot_on_resume) { + setsysfs(mic->name, "state", "boot"); + mic->boot_on_resume = 0; + } + break; default: break; } @@ -1621,11 +1642,9 @@ init_mic_list(void) while ((file = readdir(dp)) != NULL) { if (!strncmp(file->d_name, "mic", 3)) { - mic->next = malloc(sizeof(struct mic_info)); + mic->next = calloc(1, sizeof(struct mic_info)); if (mic->next) { mic = mic->next; - mic->next = NULL; - memset(mic, 0, sizeof(struct mic_info)); mic->id = atoi(&file->d_name[3]); mic->name = malloc(strlen(file->d_name) + 16); if (mic->name) diff --git a/Documentation/mic/mpssd/mpssd.h b/Documentation/mic/mpssd/mpssd.h index ccd589f..f5f18b1 100644 --- a/Documentation/mic/mpssd/mpssd.h +++ b/Documentation/mic/mpssd/mpssd.h @@ -91,6 +91,7 @@ struct mic_info { struct mic_net_info mic_net; struct mic_virtblk_info mic_virtblk; int restart; + int boot_on_resume; struct mic_info *next; }; diff --git a/drivers/misc/mic/host/mic_boot.c b/drivers/misc/mic/host/mic_boot.c index 60c54d5..d56b8bf 100644 --- a/drivers/misc/mic/host/mic_boot.c +++ b/drivers/misc/mic/host/mic_boot.c @@ -37,12 +37,13 @@ static void mic_reset(struct mic_device *mdev) #define MIC_RESET_TO (45) + INIT_COMPLETION(mdev->reset_wait); mdev->ops->reset_fw_ready(mdev); mdev->ops->reset(mdev); for (i = 0; i < MIC_RESET_TO; i++) { if (mdev->ops->is_fw_ready(mdev)) - return; + goto done; /* * Resets typically take 10s of seconds to complete. * Since an MMIO read is required to check if the @@ -51,6 +52,8 @@ static void mic_reset(struct mic_device *mdev) msleep(1000); } mic_set_state(mdev, MIC_RESET_FAILED); +done: + complete_all(&mdev->reset_wait); } /* Initialize the MIC bootparams */ @@ -123,7 +126,8 @@ void mic_stop(struct mic_device *mdev, bool force) if (MIC_RESET_FAILED == mdev->state) goto unlock; mic_set_shutdown_status(mdev, MIC_NOP); - mic_set_state(mdev, MIC_OFFLINE); + if (MIC_SUSPENDED != mdev->state) + mic_set_state(mdev, MIC_OFFLINE); } unlock: mutex_unlock(&mdev->mic_mutex); @@ -165,7 +169,14 @@ void mic_shutdown_work(struct work_struct *work) mutex_lock(&mdev->mic_mutex); mic_set_shutdown_status(mdev, bootparam->shutdown_status); bootparam->shutdown_status = 0; - if (MIC_SHUTTING_DOWN != mdev->state) + + /* + * if state is MIC_SUSPENDED, OSPM suspend is in progress. We do not + * change the state here so as to prevent users from booting the card + * during and after the suspend operation. + */ + if (MIC_SHUTTING_DOWN != mdev->state && + MIC_SUSPENDED != mdev->state) mic_set_state(mdev, MIC_SHUTTING_DOWN); mutex_unlock(&mdev->mic_mutex); } @@ -183,3 +194,106 @@ void mic_reset_trigger_work(struct work_struct *work) mic_stop(mdev, false); } + +/** + * mic_complete_resume - Complete MIC Resume after an OSPM suspend/hibernate + * event. + * @mdev: pointer to mic_device instance + * + * RETURNS: None. + */ +void mic_complete_resume(struct mic_device *mdev) +{ + if (mdev->state != MIC_SUSPENDED) { + dev_warn(mdev->sdev->parent, "state %d should be %d\n", + mdev->state, MIC_SUSPENDED); + return; + } + + /* Make sure firmware is ready */ + if (!mdev->ops->is_fw_ready(mdev)) + mic_stop(mdev, true); + + mutex_lock(&mdev->mic_mutex); + mic_set_state(mdev, MIC_OFFLINE); + mutex_unlock(&mdev->mic_mutex); +} + +/** + * mic_prepare_suspend - Handle suspend notification for the MIC device. + * @mdev: pointer to mic_device instance + * + * RETURNS: None. + */ +void mic_prepare_suspend(struct mic_device *mdev) +{ + int rc; + +#define MIC_SUSPEND_TIMEOUT (60 * HZ) + + mutex_lock(&mdev->mic_mutex); + switch (mdev->state) { + case MIC_OFFLINE: + /* + * Card is already offline. Set state to MIC_SUSPENDED + * to prevent users from booting the card. + */ + mic_set_state(mdev, MIC_SUSPENDED); + mutex_unlock(&mdev->mic_mutex); + break; + case MIC_ONLINE: + /* + * Card is online. Set state to MIC_SUSPENDING and notify + * MIC user space daemon which will issue card + * shutdown and reset. + */ + mic_set_state(mdev, MIC_SUSPENDING); + mutex_unlock(&mdev->mic_mutex); + rc = wait_for_completion_timeout(&mdev->reset_wait, + MIC_SUSPEND_TIMEOUT); + /* Force reset the card if the shutdown completion timed out */ + if (!rc) { + mutex_lock(&mdev->mic_mutex); + mic_set_state(mdev, MIC_SUSPENDED); + mutex_unlock(&mdev->mic_mutex); + mic_stop(mdev, true); + } + break; + case MIC_SHUTTING_DOWN: + /* + * Card is shutting down. Set state to MIC_SUSPENDED + * to prevent further boot of the card. + */ + mic_set_state(mdev, MIC_SUSPENDED); + mutex_unlock(&mdev->mic_mutex); + rc = wait_for_completion_timeout(&mdev->reset_wait, + MIC_SUSPEND_TIMEOUT); + /* Force reset the card if the shutdown completion timed out */ + if (!rc) + mic_stop(mdev, true); + break; + default: + mutex_unlock(&mdev->mic_mutex); + break; + } +} + +/** + * mic_suspend - Initiate MIC suspend. Suspend merely issues card shutdown. + * @mdev: pointer to mic_device instance + * + * RETURNS: None. + */ +void mic_suspend(struct mic_device *mdev) +{ + struct mic_bootparam *bootparam = mdev->dp; + s8 db = bootparam->h2c_shutdown_db; + + mutex_lock(&mdev->mic_mutex); + if (MIC_SUSPENDING == mdev->state && db != -1) { + bootparam->shutdown_card = 1; + mdev->ops->send_intr(mdev, db); + mic_set_state(mdev, MIC_SUSPENDED); + } + mutex_unlock(&mdev->mic_mutex); +} diff --git a/drivers/misc/mic/host/mic_device.h b/drivers/misc/mic/host/mic_device.h index dcba2a5..3574cc3 100644 --- a/drivers/misc/mic/host/mic_device.h +++ b/drivers/misc/mic/host/mic_device.h @@ -23,6 +23,7 @@ #include #include +#include #include "mic_intr.h" @@ -75,6 +76,7 @@ enum mic_stepping { * @state: MIC state. * @shutdown_status: MIC status reported by card for shutdown/crashes. * @state_sysfs: Sysfs dirent for notifying ring 3 about MIC state changes. + * @reset_wait: Waitqueue for sleeping while reset completes. * @log_buf_addr: Log buffer address for MIC. * @log_buf_len: Log buffer length address for MIC. * @dp: virtio device page @@ -83,6 +85,7 @@ enum mic_stepping { * @shutdown_cookie: shutdown cookie. * @cdev: Character device for MIC. * @vdev_list: list of virtio devices. + * @pm_notifier: Handles PM notifications from the OS. */ struct mic_device { struct mic_mw mmio; @@ -110,6 +113,7 @@ struct mic_device { u8 state; u8 shutdown_status; struct sysfs_dirent *state_sysfs; + struct completion reset_wait; void *log_buf_addr; int *log_buf_len; void *dp; @@ -118,6 +122,7 @@ struct mic_device { struct mic_irq *shutdown_cookie; struct cdev cdev; struct list_head vdev_list; + struct notifier_block pm_notifier; }; /** @@ -192,4 +197,7 @@ void mic_create_debug_dir(struct mic_device *dev); void mic_delete_debug_dir(struct mic_device *dev); void __init mic_init_debugfs(void); void mic_exit_debugfs(void); +void mic_prepare_suspend(struct mic_device *mdev); +void mic_complete_resume(struct mic_device *mdev); +void mic_suspend(struct mic_device *mdev); #endif diff --git a/drivers/misc/mic/host/mic_main.c b/drivers/misc/mic/host/mic_main.c index ca06aa9..b352085 100644 --- a/drivers/misc/mic/host/mic_main.c +++ b/drivers/misc/mic/host/mic_main.c @@ -26,6 +26,7 @@ #include #include #include +#include #include #include "../common/mic_dev.h" @@ -187,6 +188,43 @@ static enum mic_hw_family mic_get_family(struct pci_dev *pdev) } /** +* mic_pm_notifier: Notifier callback function that handles +* PM notifications. +* +* @notifier_block: The notifier structure. +* @pm_event: The event for which the driver was notified. +* @unused: Meaningless. Always NULL. +* +* returns NOTIFY_DONE +*/ +static int mic_pm_notifier(struct notifier_block *notifier, + unsigned long pm_event, void *unused) +{ + struct mic_device *mdev = container_of(notifier, + struct mic_device, pm_notifier); + + switch (pm_event) { + case PM_HIBERNATION_PREPARE: + /* Fall through */ + case PM_SUSPEND_PREPARE: + mic_prepare_suspend(mdev); + break; + case PM_POST_HIBERNATION: + /* Fall through */ + case PM_POST_SUSPEND: + /* Fall through */ + case PM_POST_RESTORE: + mic_complete_resume(mdev); + break; + case PM_RESTORE_PREPARE: + break; + default: + break; + } + return NOTIFY_DONE; +} + +/** * mic_device_init - Allocates and initializes the MIC device structure * * @mdev: pointer to mic_device instance @@ -194,9 +232,11 @@ static enum mic_hw_family mic_get_family(struct pci_dev *pdev) * * returns none. */ -static void +static int mic_device_init(struct mic_device *mdev, struct pci_dev *pdev) { + int rc; + mdev->family = mic_get_family(pdev); mdev->stepping = pdev->revision; mic_ops_init(mdev); @@ -205,7 +245,20 @@ mic_device_init(struct mic_device *mdev, struct pci_dev *pdev) mdev->irq_info.next_avail_src = 0; INIT_WORK(&mdev->reset_trigger_work, mic_reset_trigger_work); INIT_WORK(&mdev->shutdown_work, mic_shutdown_work); + init_completion(&mdev->reset_wait); INIT_LIST_HEAD(&mdev->vdev_list); + mdev->pm_notifier.notifier_call = mic_pm_notifier; + rc = register_pm_notifier(&mdev->pm_notifier); + if (rc) { + dev_err(&pdev->dev, "register_pm_notifier failed rc %d\n", + rc); + goto register_pm_notifier_fail; + } + return 0; +register_pm_notifier_fail: + flush_work(&mdev->shutdown_work); + flush_work(&mdev->reset_trigger_work); + return rc; } /** @@ -224,6 +277,7 @@ static void mic_device_uninit(struct mic_device *mdev) kfree(mdev->bootmode); flush_work(&mdev->reset_trigger_work); flush_work(&mdev->shutdown_work); + unregister_pm_notifier(&mdev->pm_notifier); } /** @@ -253,7 +307,11 @@ static int mic_probe(struct pci_dev *pdev, goto ida_fail; } - mic_device_init(mdev, pdev); + rc = mic_device_init(mdev, pdev); + if (rc) { + dev_err(&pdev->dev, "mic_device_init failed rc %d\n", rc); + goto device_init_fail; + } rc = pci_enable_device(pdev); if (rc) { @@ -376,6 +434,7 @@ disable_device: pci_disable_device(pdev); uninit_device: mic_device_uninit(mdev); +device_init_fail: ida_simple_remove(&g_mic_ida, mdev->id); ida_fail: kfree(mdev); diff --git a/drivers/misc/mic/host/mic_sysfs.c b/drivers/misc/mic/host/mic_sysfs.c index 75746ad..6dd864e 100644 --- a/drivers/misc/mic/host/mic_sysfs.c +++ b/drivers/misc/mic/host/mic_sysfs.c @@ -33,6 +33,8 @@ static const char * const mic_state_string[] = { [MIC_ONLINE] = "online", [MIC_SHUTTING_DOWN] = "shutting_down", [MIC_RESET_FAILED] = "reset_failed", + [MIC_SUSPENDING] = "suspending", + [MIC_SUSPENDED] = "suspended", }; /* @@ -156,6 +158,11 @@ state_store(struct device *dev, struct device_attribute *attr, goto done; } + if (sysfs_streq(buf, "suspend")) { + mic_suspend(mdev); + goto done; + } + count = -EINVAL; done: return count; diff --git a/include/uapi/linux/mic_common.h b/include/uapi/linux/mic_common.h index 364ac75..17e7d95 100644 --- a/include/uapi/linux/mic_common.h +++ b/include/uapi/linux/mic_common.h @@ -219,6 +219,8 @@ enum mic_states { MIC_ONLINE, MIC_SHUTTING_DOWN, MIC_RESET_FAILED, + MIC_SUSPENDING, + MIC_SUSPENDED, MIC_LAST }; -- cgit v0.10.2 From 240ddd495a9e72073d11b1b7c2ec9ea14e7015cb Mon Sep 17 00:00:00 2001 From: Jan Kara Date: Wed, 2 Oct 2013 16:27:47 +0200 Subject: vmw_vmci: Convert driver to use get_user_pages_fast() Convert vmci_host_setup_notify() and qp_host_get_user_memory() to use get_user_pages_fast() instead of get_user_pages(). Note that qp_host_get_user_memory() was using mmap_sem for writing without an apparent reason. CC: Arnd Bergmann Signed-off-by: Jan Kara Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/misc/vmw_vmci/vmci_host.c b/drivers/misc/vmw_vmci/vmci_host.c index d4722b3..1723a6e 100644 --- a/drivers/misc/vmw_vmci/vmci_host.c +++ b/drivers/misc/vmw_vmci/vmci_host.c @@ -243,11 +243,7 @@ static int vmci_host_setup_notify(struct vmci_ctx *context, /* * Lock physical page backing a given user VA. */ - down_read(¤t->mm->mmap_sem); - retval = get_user_pages(current, current->mm, - PAGE_ALIGN(uva), - 1, 1, 0, &page, NULL); - up_read(¤t->mm->mmap_sem); + retval = get_user_pages_fast(PAGE_ALIGN(uva), 1, 1, &page); if (retval != 1) return VMCI_ERROR_GENERIC; diff --git a/drivers/misc/vmw_vmci/vmci_queue_pair.c b/drivers/misc/vmw_vmci/vmci_queue_pair.c index a0515a6..1b7b303 100644 --- a/drivers/misc/vmw_vmci/vmci_queue_pair.c +++ b/drivers/misc/vmw_vmci/vmci_queue_pair.c @@ -732,13 +732,9 @@ static int qp_host_get_user_memory(u64 produce_uva, int retval; int err = VMCI_SUCCESS; - down_write(¤t->mm->mmap_sem); - retval = get_user_pages(current, - current->mm, - (uintptr_t) produce_uva, - produce_q->kernel_if->num_pages, - 1, 0, - produce_q->kernel_if->u.h.header_page, NULL); + retval = get_user_pages_fast((uintptr_t) produce_uva, + produce_q->kernel_if->num_pages, 1, + produce_q->kernel_if->u.h.header_page); if (retval < produce_q->kernel_if->num_pages) { pr_warn("get_user_pages(produce) failed (retval=%d)", retval); qp_release_pages(produce_q->kernel_if->u.h.header_page, @@ -747,12 +743,9 @@ static int qp_host_get_user_memory(u64 produce_uva, goto out; } - retval = get_user_pages(current, - current->mm, - (uintptr_t) consume_uva, - consume_q->kernel_if->num_pages, - 1, 0, - consume_q->kernel_if->u.h.header_page, NULL); + retval = get_user_pages_fast((uintptr_t) consume_uva, + consume_q->kernel_if->num_pages, 1, + consume_q->kernel_if->u.h.header_page); if (retval < consume_q->kernel_if->num_pages) { pr_warn("get_user_pages(consume) failed (retval=%d)", retval); qp_release_pages(consume_q->kernel_if->u.h.header_page, @@ -763,8 +756,6 @@ static int qp_host_get_user_memory(u64 produce_uva, } out: - up_write(¤t->mm->mmap_sem); - return err; } -- cgit v0.10.2 From fbfdb6ed6265bf62b326b8bd254994ae98cfdb46 Mon Sep 17 00:00:00 2001 From: Jan Luebbe Date: Mon, 14 Oct 2013 17:14:59 +0200 Subject: misc/at25, dt: support probing at25 SPI EEPROM from DT The commit d6ae0d578d24303941c1424b049d2cae28277666 introduced devicetree binding documentation for this driver, but the driver itself does not yet support the documented compatible entry. Fix this by adding the documented entry to the driver. Signed-off-by: Jan Luebbe Signed-off-by: Markus Pargmann Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/misc/eeprom/at25.c b/drivers/misc/eeprom/at25.c index 840b359..4f3bca1 100644 --- a/drivers/misc/eeprom/at25.c +++ b/drivers/misc/eeprom/at25.c @@ -462,10 +462,17 @@ static int at25_remove(struct spi_device *spi) /*-------------------------------------------------------------------------*/ +static const struct of_device_id at25_of_match[] = { + { .compatible = "atmel,at25", }, + { } +}; +MODULE_DEVICE_TABLE(of, at25_of_match); + static struct spi_driver at25_driver = { .driver = { .name = "at25", .owner = THIS_MODULE, + .of_match_table = at25_of_match, }, .probe = at25_probe, .remove = at25_remove, -- cgit v0.10.2 From 838b3a6d62413b336f3dde15ecff161070358957 Mon Sep 17 00:00:00 2001 From: Tomas Winkler Date: Wed, 16 Oct 2013 12:09:43 +0300 Subject: mei: me: add Lynx Point Wellsburg work station device id add missing device id for LPT based work station Signed-off-by: Tomas Winkler Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/misc/mei/hw-me-regs.h b/drivers/misc/mei/hw-me-regs.h index 6a203b6..6c0fde5 100644 --- a/drivers/misc/mei/hw-me-regs.h +++ b/drivers/misc/mei/hw-me-regs.h @@ -110,6 +110,7 @@ #define MEI_DEV_ID_PPT_3 0x1DBA /* Panther Point */ #define MEI_DEV_ID_LPT 0x8C3A /* Lynx Point */ +#define MEI_DEV_ID_LPT_W 0x8D3A /* Lynx Point - Wellsburg */ #define MEI_DEV_ID_LPT_LP 0x9C3A /* Lynx Point LP */ /* * MEI HW Section diff --git a/drivers/misc/mei/pci-me.c b/drivers/misc/mei/pci-me.c index 1b8a4c6..d2ea9ff 100644 --- a/drivers/misc/mei/pci-me.c +++ b/drivers/misc/mei/pci-me.c @@ -77,6 +77,7 @@ static DEFINE_PCI_DEVICE_TABLE(mei_me_pci_tbl) = { {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_PPT_2)}, {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_PPT_3)}, {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_LPT)}, + {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_LPT_W)}, {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_LPT_LP)}, /* required last entry */ -- cgit v0.10.2 From 158f0bb005fc7fdb1a7a968eb61100989f5f7322 Mon Sep 17 00:00:00 2001 From: Michael Opdenacker Date: Sun, 13 Oct 2013 05:53:47 +0200 Subject: hpet: remove deprecated IRQF_DISABLED This patch proposes to remove the use of the IRQF_DISABLED flag It's a NOOP since 2.6.35 and it will be removed one day. Signed-off-by: Michael Opdenacker Acked-by: Arnd Bergmann Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/char/hpet.c b/drivers/char/hpet.c index c8f4329..dca5834 100644 --- a/drivers/char/hpet.c +++ b/drivers/char/hpet.c @@ -486,8 +486,7 @@ static int hpet_ioctl_ieon(struct hpet_dev *devp) } sprintf(devp->hd_name, "hpet%d", (int)(devp - hpetp->hp_dev)); - irq_flags = devp->hd_flags & HPET_SHARED_IRQ - ? IRQF_SHARED : IRQF_DISABLED; + irq_flags = devp->hd_flags & HPET_SHARED_IRQ ? IRQF_SHARED : 0; if (request_irq(irq, hpet_interrupt, irq_flags, devp->hd_name, (void *)devp)) { printk(KERN_ERR "hpet: IRQ %d is not free\n", irq); -- cgit v0.10.2 From d88ed628f83449bd6c1e9f37bfde08088823bde3 Mon Sep 17 00:00:00 2001 From: Michael Opdenacker Date: Sun, 13 Oct 2013 06:14:42 +0200 Subject: various char drivers: remove deprecated IRQF_DISABLED This patch proposes to remove the use of the IRQF_DISABLED flag It's a NOOP since 2.6.35 and it will be removed one day. Signed-off-by: Michael Opdenacker Acked-by: Arnd Bergmann Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/char/nwbutton.c b/drivers/char/nwbutton.c index cfdfe49..1fd00dc0 100644 --- a/drivers/char/nwbutton.c +++ b/drivers/char/nwbutton.c @@ -220,7 +220,7 @@ static int __init nwbutton_init(void) return -EBUSY; } - if (request_irq (IRQ_NETWINDER_BUTTON, button_handler, IRQF_DISABLED, + if (request_irq (IRQ_NETWINDER_BUTTON, button_handler, 0, "nwbutton", NULL)) { printk (KERN_WARNING "nwbutton: IRQ %d is not free.\n", IRQ_NETWINDER_BUTTON); diff --git a/drivers/char/rtc.c b/drivers/char/rtc.c index c0cbbd4..3525996 100644 --- a/drivers/char/rtc.c +++ b/drivers/char/rtc.c @@ -227,7 +227,7 @@ static inline unsigned char rtc_is_updating(void) #ifdef RTC_IRQ /* - * A very tiny interrupt handler. It runs with IRQF_DISABLED set, + * A very tiny interrupt handler. It runs with interrupts disabled, * but there is possibility of conflicting with the set_rtc_mmss() * call (the rtc irq and the timer irq can easily run at the same * time in two different CPUs). So we need to serialize @@ -1040,8 +1040,7 @@ no_irq: rtc_int_handler_ptr = rtc_interrupt; } - if (request_irq(RTC_IRQ, rtc_int_handler_ptr, IRQF_DISABLED, - "rtc", NULL)) { + if (request_irq(RTC_IRQ, rtc_int_handler_ptr, 0, "rtc", NULL)) { /* Yeah right, seeing as irq 8 doesn't even hit the bus. */ rtc_has_irq = 0; printk(KERN_ERR "rtc: IRQ %d is not free.\n", RTC_IRQ); diff --git a/drivers/char/snsc.c b/drivers/char/snsc.c index 5816b39..8bab592 100644 --- a/drivers/char/snsc.c +++ b/drivers/char/snsc.c @@ -108,8 +108,7 @@ scdrv_open(struct inode *inode, struct file *file) /* hook this subchannel up to the system controller interrupt */ mutex_lock(&scdrv_mutex); rv = request_irq(SGI_UART_VECTOR, scdrv_interrupt, - IRQF_SHARED | IRQF_DISABLED, - SYSCTL_BASENAME, sd); + IRQF_SHARED, SYSCTL_BASENAME, sd); if (rv) { ia64_sn_irtr_close(sd->sd_nasid, sd->sd_subch); kfree(sd); diff --git a/drivers/char/snsc_event.c b/drivers/char/snsc_event.c index ee15694..59bcefd 100644 --- a/drivers/char/snsc_event.c +++ b/drivers/char/snsc_event.c @@ -292,8 +292,7 @@ scdrv_event_init(struct sysctl_data_s *scd) /* hook event subchannel up to the system controller interrupt */ rv = request_irq(SGI_UART_VECTOR, scdrv_event_interrupt, - IRQF_SHARED | IRQF_DISABLED, - "system controller events", event_sd); + IRQF_SHARED, "system controller events", event_sd); if (rv) { printk(KERN_WARNING "%s: irq request failed (%d)\n", __func__, rv); -- cgit v0.10.2 From 9de14f1c45351b6681a9721142850bca1cd20372 Mon Sep 17 00:00:00 2001 From: Michael Opdenacker Date: Sun, 13 Oct 2013 06:17:25 +0200 Subject: tlclk: remove deprecated IRQF_DISABLED This patch proposes to remove the use of the IRQF_DISABLED flag It's a NOOP since 2.6.35 and it will be removed one day. Signed-off-by: Michael Opdenacker Acked-by: Arnd Bergmann Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/char/tlclk.c b/drivers/char/tlclk.c index e95e0ab..100cd1d 100644 --- a/drivers/char/tlclk.c +++ b/drivers/char/tlclk.c @@ -222,7 +222,7 @@ static int tlclk_open(struct inode *inode, struct file *filp) /* This device is wired through the FPGA IO space of the ATCA blade * we can't share this IRQ */ result = request_irq(telclk_interrupt, &tlclk_interrupt, - IRQF_DISABLED, "telco_clock", tlclk_interrupt); + 0, "telco_clock", tlclk_interrupt); if (result == -EBUSY) printk(KERN_ERR "tlclk: Interrupt can't be reserved.\n"); else -- cgit v0.10.2 From b09534d3d8d85082b3c5b5803607463bd7e40194 Mon Sep 17 00:00:00 2001 From: Michael Opdenacker Date: Sun, 13 Oct 2013 08:13:31 +0200 Subject: misc: arm-charlcd: remove deprecated IRQF_DISABLED This patch proposes to remove the use of the IRQF_DISABLED flag It's a NOOP since 2.6.35 and it will be removed one day. Signed-off-by: Michael Opdenacker Acked-by: Arnd Bergmann Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/misc/arm-charlcd.c b/drivers/misc/arm-charlcd.c index 1256a4b..b7ebf80 100644 --- a/drivers/misc/arm-charlcd.c +++ b/drivers/misc/arm-charlcd.c @@ -297,7 +297,7 @@ static int __init charlcd_probe(struct platform_device *pdev) lcd->irq = platform_get_irq(pdev, 0); /* If no IRQ is supplied, we'll survive without it */ if (lcd->irq >= 0) { - if (request_irq(lcd->irq, charlcd_interrupt, IRQF_DISABLED, + if (request_irq(lcd->irq, charlcd_interrupt, 0, DRIVERNAME, lcd)) { ret = -EIO; goto out_no_irq; -- cgit v0.10.2 From bb9da88d7ae2f28eafbe70b7e27721cc3f7b2d43 Mon Sep 17 00:00:00 2001 From: Michael Opdenacker Date: Sun, 13 Oct 2013 08:16:07 +0200 Subject: misc: phantom: remove deprecated IRQF_DISABLED This patch proposes to remove the use of the IRQF_DISABLED flag It's a NOOP since 2.6.35 and it will be removed one day. Signed-off-by: Michael Opdenacker Acked-by: Arnd Bergmann Acked-by: Jiri Slaby Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/misc/phantom.c b/drivers/misc/phantom.c index 68b7c77..3075492 100644 --- a/drivers/misc/phantom.c +++ b/drivers/misc/phantom.c @@ -395,7 +395,7 @@ static int phantom_probe(struct pci_dev *pdev, iowrite32(0, pht->caddr + PHN_IRQCTL); ioread32(pht->caddr + PHN_IRQCTL); /* PCI posting */ retval = request_irq(pdev->irq, phantom_isr, - IRQF_SHARED | IRQF_DISABLED, "phantom", pht); + IRQF_SHARED, "phantom", pht); if (retval) { dev_err(&pdev->dev, "can't establish ISR\n"); goto err_unmo; -- cgit v0.10.2 From 813b4650ddf0fae2e8f0faa140eb63b5265f3701 Mon Sep 17 00:00:00 2001 From: Michael Opdenacker Date: Sun, 13 Oct 2013 09:23:27 +0200 Subject: w1: ds1wm: remove deprecated IRQF_DISABLED This patch proposes to remove the use of the IRQF_DISABLED flag It's a NOOP since 2.6.35 and it will be removed one day. Signed-off-by: Michael Opdenacker Acked-by: Evgeniy Polyakov Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/w1/masters/ds1wm.c b/drivers/w1/masters/ds1wm.c index 96cab6a..41613f9 100644 --- a/drivers/w1/masters/ds1wm.c +++ b/drivers/w1/masters/ds1wm.c @@ -498,7 +498,7 @@ static int ds1wm_probe(struct platform_device *pdev) irq_set_irq_type(ds1wm_data->irq, IRQ_TYPE_EDGE_FALLING); ret = devm_request_irq(&pdev->dev, ds1wm_data->irq, ds1wm_isr, - IRQF_DISABLED | IRQF_SHARED, "ds1wm", ds1wm_data); + IRQF_SHARED, "ds1wm", ds1wm_data); if (ret) return ret; -- cgit v0.10.2 From fe576a580f2a62c43bb81b18700576a66b332c8f Mon Sep 17 00:00:00 2001 From: Michael Opdenacker Date: Sun, 13 Oct 2013 09:20:05 +0200 Subject: w1: omap-hdq: remove deprecated IRQF_DISABLED This patch proposes to remove the use of the IRQF_DISABLED flag It's a NOOP since 2.6.35 and it will be removed one day. Signed-off-by: Michael Opdenacker Acked-by: Evgeniy Polyakov Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/w1/masters/omap_hdq.c b/drivers/w1/masters/omap_hdq.c index 6e94d8dd..9900e8e 100644 --- a/drivers/w1/masters/omap_hdq.c +++ b/drivers/w1/masters/omap_hdq.c @@ -577,8 +577,7 @@ static int omap_hdq_probe(struct platform_device *pdev) goto err_irq; } - ret = devm_request_irq(dev, irq, hdq_isr, IRQF_DISABLED, - "omap_hdq", hdq_data); + ret = devm_request_irq(dev, irq, hdq_isr, 0, "omap_hdq", hdq_data); if (ret < 0) { dev_dbg(&pdev->dev, "could not request irq\n"); goto err_irq; -- cgit v0.10.2 From fdf91dae6f024c6dfee425fe754df6b1957f6c53 Mon Sep 17 00:00:00 2001 From: Felipe Pena Date: Tue, 15 Oct 2013 20:22:32 -0300 Subject: drivers: hv: Fix wrong check for synic_event_page The check for calling free_page() on hv_context.synic_event_page[cpu] is the same for hv_context.synic_message_page[cpu], like a copy-paste error. Signed-off-by: Felipe Pena Signed-off-by: K. Y. Srinivasan Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/hv/hv.c b/drivers/hv/hv.c index 88f4096..f0c5e07 100644 --- a/drivers/hv/hv.c +++ b/drivers/hv/hv.c @@ -304,7 +304,7 @@ err: void hv_synic_free_cpu(int cpu) { kfree(hv_context.event_dpc[cpu]); - if (hv_context.synic_message_page[cpu]) + if (hv_context.synic_event_page[cpu]) free_page((unsigned long)hv_context.synic_event_page[cpu]); if (hv_context.synic_message_page[cpu]) free_page((unsigned long)hv_context.synic_message_page[cpu]); -- cgit v0.10.2 From 565ce6422ff92f5af71e4d5a09f78215433b2695 Mon Sep 17 00:00:00 2001 From: "K. Y. Srinivasan" Date: Wed, 16 Oct 2013 19:27:19 -0700 Subject: Drivers: hv: vmbus: Fix a bug in channel rescind code Rescind of subchannels were not being correctly handled. Fix the bug. Signed-off-by: K. Y. Srinivasan Cc: [3.11+] Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/hv/channel_mgmt.c b/drivers/hv/channel_mgmt.c index bbff5f2..fa92046 100644 --- a/drivers/hv/channel_mgmt.c +++ b/drivers/hv/channel_mgmt.c @@ -203,7 +203,8 @@ static void vmbus_process_rescind_offer(struct work_struct *work) struct vmbus_channel *primary_channel; struct vmbus_channel_relid_released msg; - vmbus_device_unregister(channel->device_obj); + if (channel->device_obj) + vmbus_device_unregister(channel->device_obj); memset(&msg, 0, sizeof(struct vmbus_channel_relid_released)); msg.child_relid = channel->offermsg.child_relid; msg.header.msgtype = CHANNELMSG_RELID_RELEASED; @@ -216,7 +217,7 @@ static void vmbus_process_rescind_offer(struct work_struct *work) } else { primary_channel = channel->primary_channel; spin_lock_irqsave(&primary_channel->sc_lock, flags); - list_del(&channel->listentry); + list_del(&channel->sc_list); spin_unlock_irqrestore(&primary_channel->sc_lock, flags); } free_channel(channel); -- cgit v0.10.2 From ed6f7ac1dcae0b95ef2946067beb4ffaba415119 Mon Sep 17 00:00:00 2001 From: Paul Bolle Date: Thu, 17 Oct 2013 13:52:10 +0300 Subject: mei: me: downgrade two errors to debug level The mei_me driver prints "suspend" at error level at each suspend. It also prints "stop" at error level at driver unload. Downgrade these uninteresting messages to debug level. Cc: Adam Jackson Cc: Joe Perches Signed-off-by: Paul Bolle Signed-off-by: Tomas Winkler Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/misc/mei/pci-me.c b/drivers/misc/mei/pci-me.c index d2ea9ff..1bf300e 100644 --- a/drivers/misc/mei/pci-me.c +++ b/drivers/misc/mei/pci-me.c @@ -232,7 +232,7 @@ static void mei_me_remove(struct pci_dev *pdev) hw = to_me_hw(dev); - dev_err(&pdev->dev, "stop\n"); + dev_dbg(&pdev->dev, "stop\n"); mei_stop(dev); /* disable interrupts */ @@ -262,7 +262,7 @@ static int mei_me_pci_suspend(struct device *device) if (!dev) return -ENODEV; - dev_err(&pdev->dev, "suspend\n"); + dev_dbg(&pdev->dev, "suspend\n"); mei_stop(dev); -- cgit v0.10.2 From eec86b8e85309a2cb0b33bcb361c67d81ebed474 Mon Sep 17 00:00:00 2001 From: Alexander Usyskin Date: Thu, 17 Oct 2013 17:20:22 +0300 Subject: mei: move host_clients_map cleanup to device init Move host_clients_map cleanup from host client init to device init. This fixes bug where we cleaned up the bitmask that servers as pool for host client ids while file descriptors are kept open during suspend. On resume a new connection will be assigned id that is already taken by opened file descriptor. Consequently read/write will fail due to mismatched book keeping. Signed-off-by: Alexander Usyskin Signed-off-by: Tomas Winkler Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/misc/mei/client.c b/drivers/misc/mei/client.c index a48c0e7..0ccc22c 100644 --- a/drivers/misc/mei/client.c +++ b/drivers/misc/mei/client.c @@ -365,15 +365,6 @@ void mei_host_client_init(struct work_struct *work) mutex_lock(&dev->device_lock); - bitmap_zero(dev->host_clients_map, MEI_CLIENTS_MAX); - dev->open_handle_count = 0; - - /* - * Reserving the first three client IDs - * 0: Reserved for MEI Bus Message communications - */ - bitmap_set(dev->host_clients_map, 0, 1); - for (i = 0; i < dev->me_clients_num; i++) { client_props = &dev->me_clients[i].props; diff --git a/drivers/misc/mei/init.c b/drivers/misc/mei/init.c index a7d29a7..c32d452 100644 --- a/drivers/misc/mei/init.c +++ b/drivers/misc/mei/init.c @@ -68,6 +68,14 @@ void mei_device_init(struct mei_device *dev) mei_io_list_init(&dev->amthif_cmd_list); mei_io_list_init(&dev->amthif_rd_complete_list); + bitmap_zero(dev->host_clients_map, MEI_CLIENTS_MAX); + dev->open_handle_count = 0; + + /* + * Reserving the first client ID + * 0: Reserved for MEI Bus Message communications + */ + bitmap_set(dev->host_clients_map, 0, 1); } EXPORT_SYMBOL_GPL(mei_device_init); -- cgit v0.10.2 From 629c66a22c21b692b6e58b9c1d8fa56a60ccb52d Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Thu, 24 Oct 2013 18:05:42 -0700 Subject: lkdtm: isolate stack corruption test When tests were added to lkdtm that grew the stack frame, the stack corruption test stopped working. This isolates the test in its own function, and forces it not to be inlined. Signed-off-by: Kees Cook Fixes: cc33c537c12f ("lkdtm: add "EXEC_*" triggers") Cc: stable # 3.12 Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/misc/lkdtm.c b/drivers/misc/lkdtm.c index 2fc0586..9cbd037 100644 --- a/drivers/misc/lkdtm.c +++ b/drivers/misc/lkdtm.c @@ -297,6 +297,14 @@ static void do_nothing(void) return; } +static noinline void corrupt_stack(void) +{ + /* Use default char array length that triggers stack protection. */ + char data[8]; + + memset((void *)data, 0, 64); +} + static void execute_location(void *dst) { void (*func)(void) = dst; @@ -327,13 +335,9 @@ static void lkdtm_do_action(enum ctype which) case CT_OVERFLOW: (void) recursive_loop(0); break; - case CT_CORRUPT_STACK: { - /* Make sure the compiler creates and uses an 8 char array. */ - volatile char data[8]; - - memset((void *)data, 0, 64); + case CT_CORRUPT_STACK: + corrupt_stack(); break; - } case CT_UNALIGNED_LOAD_STORE_WRITE: { static u8 data[5] __attribute__((aligned(4))) = {1, 2, 3, 4, 5}; -- cgit v0.10.2 From 7d196ac303652588c60350f0a581d71e2e7b1a50 Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Thu, 24 Oct 2013 09:25:39 -0700 Subject: lkdtm: adjust recursion size to avoid warnings When CONFIG_FRAME_WARN is set low (e.g. some ARM builds), the hard-coded stack buffer size used for kernel stack over run testing triggers build warnings. Instead, avoid the warning by recalcuating the buffer size and recursion count needed to trigger the test. Also uses the recursion counter indirectly to avoid changing the parameter during the test. Signed-off-by: Kees Cook Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/misc/lkdtm.c b/drivers/misc/lkdtm.c index 9cbd037..4823442 100644 --- a/drivers/misc/lkdtm.c +++ b/drivers/misc/lkdtm.c @@ -49,8 +49,19 @@ #include #endif +/* + * Make sure our attempts to over run the kernel stack doesn't trigger + * a compiler warning when CONFIG_FRAME_WARN is set. Then make sure we + * recurse past the end of THREAD_SIZE by default. + */ +#if defined(CONFIG_FRAME_WARN) && (CONFIG_FRAME_WARN > 0) +#define REC_STACK_SIZE (CONFIG_FRAME_WARN / 2) +#else +#define REC_STACK_SIZE (THREAD_SIZE / 8) +#endif +#define REC_NUM_DEFAULT ((THREAD_SIZE / REC_STACK_SIZE) * 2) + #define DEFAULT_COUNT 10 -#define REC_NUM_DEFAULT 10 #define EXEC_SIZE 64 enum cname { @@ -140,8 +151,7 @@ static DEFINE_SPINLOCK(lock_me_up); static u8 data_area[EXEC_SIZE]; module_param(recur_count, int, 0644); -MODULE_PARM_DESC(recur_count, " Recursion level for the stack overflow test, "\ - "default is 10"); +MODULE_PARM_DESC(recur_count, " Recursion level for the stack overflow test"); module_param(cpoint_name, charp, 0444); MODULE_PARM_DESC(cpoint_name, " Crash Point, where kernel is to be crashed"); module_param(cpoint_type, charp, 0444); @@ -280,16 +290,16 @@ static int lkdtm_parse_commandline(void) return -EINVAL; } -static int recursive_loop(int a) +static int recursive_loop(int remaining) { - char buf[1024]; + char buf[REC_STACK_SIZE]; - memset(buf,0xFF,1024); - recur_count--; - if (!recur_count) + /* Make sure compiler does not optimize this away. */ + memset(buf, (remaining & 0xff) | 0x1, REC_STACK_SIZE); + if (!remaining) return 0; else - return recursive_loop(a); + return recursive_loop(remaining - 1); } static void do_nothing(void) @@ -333,7 +343,7 @@ static void lkdtm_do_action(enum ctype which) ; break; case CT_OVERFLOW: - (void) recursive_loop(0); + (void) recursive_loop(recur_count); break; case CT_CORRUPT_STACK: corrupt_stack(); -- cgit v0.10.2 From 9ae113ce5faf1c74af1ee71b5ef7d04b6b06b063 Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Thu, 24 Oct 2013 09:25:57 -0700 Subject: lkdtm: add tests for additional page permissions Testing execution and access of userspace from the kernel is needed for validating things like Intel's SMEP and SMAP protections. Additionally, add an explicit test for validating that RO page permissions have been set for the RO data area. Signed-off-by: Kees Cook Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/misc/lkdtm.c b/drivers/misc/lkdtm.c index 4823442..a2edb2e 100644 --- a/drivers/misc/lkdtm.c +++ b/drivers/misc/lkdtm.c @@ -44,6 +44,7 @@ #include #include #include +#include #ifdef CONFIG_IDE #include @@ -97,6 +98,9 @@ enum ctype { CT_EXEC_STACK, CT_EXEC_KMALLOC, CT_EXEC_VMALLOC, + CT_EXEC_USERSPACE, + CT_ACCESS_USERSPACE, + CT_WRITE_RO, }; static char* cp_name[] = { @@ -130,6 +134,9 @@ static char* cp_type[] = { "EXEC_STACK", "EXEC_KMALLOC", "EXEC_VMALLOC", + "EXEC_USERSPACE", + "ACCESS_USERSPACE", + "WRITE_RO", }; static struct jprobe lkdtm; @@ -150,6 +157,8 @@ static DEFINE_SPINLOCK(lock_me_up); static u8 data_area[EXEC_SIZE]; +static const unsigned long rodata = 0xAA55AA55; + module_param(recur_count, int, 0644); MODULE_PARM_DESC(recur_count, " Recursion level for the stack overflow test"); module_param(cpoint_name, charp, 0444); @@ -323,6 +332,15 @@ static void execute_location(void *dst) func(); } +static void execute_user_location(void *dst) +{ + void (*func)(void) = dst; + + if (copy_to_user(dst, do_nothing, EXEC_SIZE)) + return; + func(); +} + static void lkdtm_do_action(enum ctype which) { switch (which) { @@ -415,6 +433,49 @@ static void lkdtm_do_action(enum ctype which) vfree(vmalloc_area); break; } + case CT_EXEC_USERSPACE: { + unsigned long user_addr; + + user_addr = vm_mmap(NULL, 0, PAGE_SIZE, + PROT_READ | PROT_WRITE | PROT_EXEC, + MAP_ANONYMOUS | MAP_PRIVATE, 0); + if (user_addr >= TASK_SIZE) { + pr_warn("Failed to allocate user memory\n"); + return; + } + execute_user_location((void *)user_addr); + vm_munmap(user_addr, PAGE_SIZE); + break; + } + case CT_ACCESS_USERSPACE: { + unsigned long user_addr, tmp; + unsigned long *ptr; + + user_addr = vm_mmap(NULL, 0, PAGE_SIZE, + PROT_READ | PROT_WRITE | PROT_EXEC, + MAP_ANONYMOUS | MAP_PRIVATE, 0); + if (user_addr >= TASK_SIZE) { + pr_warn("Failed to allocate user memory\n"); + return; + } + + ptr = (unsigned long *)user_addr; + tmp = *ptr; + tmp += 0xc0dec0de; + *ptr = tmp; + + vm_munmap(user_addr, PAGE_SIZE); + + break; + } + case CT_WRITE_RO: { + unsigned long *ptr; + + ptr = (unsigned long *)&rodata; + *ptr ^= 0xabcd1234; + + break; + } case CT_NONE: default: break; -- cgit v0.10.2 From 4bff7208f332b2b1d7cf1338e50527441283a198 Mon Sep 17 00:00:00 2001 From: Tomas Winkler Date: Mon, 21 Oct 2013 22:05:38 +0300 Subject: mei: nfc: fix memory leak in error path The flow may reach the err label without freeing cl and cl_info cl and cl_info weren't assigned to ndev->cl and cl_info so they weren't freed in mei_nfc_free called on error path Cc: # 3.10+ Cc: Samuel Ortiz Signed-off-by: Tomas Winkler Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/misc/mei/nfc.c b/drivers/misc/mei/nfc.c index d0c6907..994ca4a 100644 --- a/drivers/misc/mei/nfc.c +++ b/drivers/misc/mei/nfc.c @@ -485,8 +485,11 @@ int mei_nfc_host_init(struct mei_device *dev) if (ndev->cl_info) return 0; - cl_info = mei_cl_allocate(dev); - cl = mei_cl_allocate(dev); + ndev->cl_info = mei_cl_allocate(dev); + ndev->cl = mei_cl_allocate(dev); + + cl = ndev->cl; + cl_info = ndev->cl_info; if (!cl || !cl_info) { ret = -ENOMEM; @@ -527,10 +530,9 @@ int mei_nfc_host_init(struct mei_device *dev) cl->device_uuid = mei_nfc_guid; + list_add_tail(&cl->device_link, &dev->device_list); - ndev->cl_info = cl_info; - ndev->cl = cl; ndev->req_id = 1; INIT_WORK(&ndev->init_work, mei_nfc_init); -- cgit v0.10.2 From f931f4f3f0e2be1a6c5278ab9488652f98ba2f9c Mon Sep 17 00:00:00 2001 From: Alexander Usyskin Date: Mon, 21 Oct 2013 22:05:43 +0300 Subject: mei: print correct device state during unexpected reset Move the unexpected state print to the beginning of mei_reset, thus printing right state. Signed-off-by: Alexander Usyskin Signed-off-by: Tomas Winkler Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/misc/mei/init.c b/drivers/misc/mei/init.c index c32d452..f7f3abb 100644 --- a/drivers/misc/mei/init.c +++ b/drivers/misc/mei/init.c @@ -147,6 +147,10 @@ void mei_reset(struct mei_device *dev, int interrupts_enabled) dev->dev_state != MEI_DEV_POWER_DOWN && dev->dev_state != MEI_DEV_POWER_UP); + if (unexpected) + dev_warn(&dev->pdev->dev, "unexpected reset: dev_state = %s\n", + mei_dev_state_str(dev->dev_state)); + ret = mei_hw_reset(dev, interrupts_enabled); if (ret) { dev_err(&dev->pdev->dev, "hw reset failed disabling the device\n"); @@ -185,10 +189,6 @@ void mei_reset(struct mei_device *dev, int interrupts_enabled) dev->rd_msg_hdr = 0; dev->wd_pending = false; - if (unexpected) - dev_warn(&dev->pdev->dev, "unexpected reset: dev_state = %s\n", - mei_dev_state_str(dev->dev_state)); - if (!interrupts_enabled) { dev_dbg(&dev->pdev->dev, "intr not enabled end of reset\n"); return; -- cgit v0.10.2 From df667a1a2c6bf828c5959a0ba15bcf987a9d17fe Mon Sep 17 00:00:00 2001 From: Tomas Winkler Date: Mon, 21 Oct 2013 22:05:41 +0300 Subject: mei: mei_cl_link remove duplicated check for open_handle_count Signed-off-by: Tomas Winkler Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/misc/mei/client.c b/drivers/misc/mei/client.c index 0ccc22c..87c96e4 100644 --- a/drivers/misc/mei/client.c +++ b/drivers/misc/mei/client.c @@ -299,12 +299,6 @@ int mei_cl_link(struct mei_cl *cl, int id) return -EMFILE; } - if (dev->open_handle_count >= MEI_MAX_OPEN_HANDLE_COUNT) { - dev_err(&dev->pdev->dev, "open_handle_count exceded %d", - MEI_MAX_OPEN_HANDLE_COUNT); - return -ENOENT; - } - dev->open_handle_count++; cl->host_client_id = id; -- cgit v0.10.2 From f9350129a09d8f55ba9322fa4327aeb499a91bbd Mon Sep 17 00:00:00 2001 From: Tomas Winkler Date: Mon, 21 Oct 2013 22:05:40 +0300 Subject: mei: bus: propagate error code returned by mei_me_cl_by_id no need to change error code value returned by mei_me_cl_by_id, just propagate it on Signed-off-by: Tomas Winkler Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/misc/mei/bus.c b/drivers/misc/mei/bus.c index cd2033c..4bc7d62 100644 --- a/drivers/misc/mei/bus.c +++ b/drivers/misc/mei/bus.c @@ -245,7 +245,7 @@ static int ___mei_cl_send(struct mei_cl *cl, u8 *buf, size_t length, /* Check if we have an ME client device */ id = mei_me_cl_by_id(dev, cl->me_client_id); if (id < 0) - return -ENODEV; + return id; if (length > dev->me_clients[id].props.max_msg_length) return -EINVAL; -- cgit v0.10.2 From c4e87b525936da188add18d53a0fe681c3c977ce Mon Sep 17 00:00:00 2001 From: Alexander Usyskin Date: Mon, 21 Oct 2013 22:05:42 +0300 Subject: mei: replace stray pr_debug with dev_dbg Driver better use dev_dbg, not pr_debug. Signed-off-by: Alexander Usyskin Signed-off-by: Tomas Winkler Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/misc/mei/pci-me.c b/drivers/misc/mei/pci-me.c index 1bf300e..b96205a 100644 --- a/drivers/misc/mei/pci-me.c +++ b/drivers/misc/mei/pci-me.c @@ -190,7 +190,7 @@ static int mei_me_probe(struct pci_dev *pdev, const struct pci_device_id *ent) schedule_delayed_work(&dev->timer_work, HZ); - pr_debug("initialization successful.\n"); + dev_dbg(&pdev->dev, "initialization successful.\n"); return 0; -- cgit v0.10.2 From 50f67a06713599e18cd9141e413d0e2653bb242b Mon Sep 17 00:00:00 2001 From: Tomas Winkler Date: Mon, 21 Oct 2013 22:05:39 +0300 Subject: mei: wd: host_init propagate error codes from called functions Propagate error codes from called functions, they are correct. Signed-off-by: Tomas Winkler Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/misc/mei/wd.c b/drivers/misc/mei/wd.c index b892143..9e35421 100644 --- a/drivers/misc/mei/wd.c +++ b/drivers/misc/mei/wd.c @@ -60,7 +60,7 @@ static void mei_wd_set_start_timeout(struct mei_device *dev, u16 timeout) int mei_wd_host_init(struct mei_device *dev) { struct mei_cl *cl = &dev->wd_cl; - int i; + int id; int ret; mei_cl_init(cl, dev); @@ -70,19 +70,19 @@ int mei_wd_host_init(struct mei_device *dev) /* check for valid client id */ - i = mei_me_cl_by_uuid(dev, &mei_wd_guid); - if (i < 0) { + id = mei_me_cl_by_uuid(dev, &mei_wd_guid); + if (id < 0) { dev_info(&dev->pdev->dev, "wd: failed to find the client\n"); - return -ENOENT; + return id; } - cl->me_client_id = dev->me_clients[i].client_id; + cl->me_client_id = dev->me_clients[id].client_id; ret = mei_cl_link(cl, MEI_WD_HOST_CLIENT_ID); if (ret < 0) { dev_info(&dev->pdev->dev, "wd: failed link client\n"); - return -ENOENT; + return ret; } cl->state = MEI_FILE_CONNECTING; -- cgit v0.10.2 From 5c6d6fd1564138ad048564e48639f842714a90c6 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 22 Oct 2013 18:32:39 +0200 Subject: misc: atmel_pwm: add deferred-probing support Two drivers (atmel-pwm-bl and leds-atmel-pwm) currently depend on the atmel_pwm driver to have bound to any pwm-device before their devices are probed. Support deferred probing of such devices by making sure to return -EPROBE_DEFER from pwm_channel_alloc when no pwm-device has yet been bound. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/misc/atmel_pwm.c b/drivers/misc/atmel_pwm.c index 494d050..a6dc56e 100644 --- a/drivers/misc/atmel_pwm.c +++ b/drivers/misc/atmel_pwm.c @@ -90,8 +90,10 @@ int pwm_channel_alloc(int index, struct pwm_channel *ch) unsigned long flags; int status = 0; - /* insist on PWM init, with this signal pinned out */ - if (!pwm || !(pwm->mask & 1 << index)) + if (!pwm) + return -EPROBE_DEFER; + + if (!(pwm->mask & 1 << index)) return -ENODEV; if (index < 0 || index >= PWM_NCHAN || !ch) -- cgit v0.10.2 From 95f774c5268843e78fdb732cd4f27e510bbbba83 Mon Sep 17 00:00:00 2001 From: Vladimir Zapolskiy Date: Mon, 28 Oct 2013 19:05:16 +0200 Subject: misc/93xx46: avoid infinite loop on write() This change fixes a problem of infinite zero byte write() without an error status, if there is an attempt to write a file bigger than EEPROM size over sysfs interface. Signed-off-by: Vladimir Zapolskiy Cc: Greg Kroah-Hartman Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/misc/eeprom/eeprom_93xx46.c b/drivers/misc/eeprom/eeprom_93xx46.c index 94cfc12..3a015ab 100644 --- a/drivers/misc/eeprom/eeprom_93xx46.c +++ b/drivers/misc/eeprom/eeprom_93xx46.c @@ -202,7 +202,7 @@ eeprom_93xx46_bin_write(struct file *filp, struct kobject *kobj, edev = dev_get_drvdata(dev); if (unlikely(off >= edev->bin.size)) - return 0; + return -EFBIG; if ((off + count) > edev->bin.size) count = edev->bin.size - off; if (unlikely(!count)) -- cgit v0.10.2 From 7c65e29250a3154cac18b527b6ccfc61f8f08c58 Mon Sep 17 00:00:00 2001 From: Vladimir Zapolskiy Date: Mon, 28 Oct 2013 19:05:15 +0200 Subject: misc/at24: avoid infinite loop on write() This change fixes a problem of infinite zero byte write() without an error status, if there is an attempt to write a file bigger than EEPROM size over sysfs interface. Signed-off-by: Vladimir Zapolskiy Cc: Wolfram Sang Cc: Greg Kroah-Hartman Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/misc/eeprom/at24.c b/drivers/misc/eeprom/at24.c index 5d4fd69..94b8a33 100644 --- a/drivers/misc/eeprom/at24.c +++ b/drivers/misc/eeprom/at24.c @@ -428,6 +428,9 @@ static ssize_t at24_bin_write(struct file *filp, struct kobject *kobj, { struct at24_data *at24; + if (unlikely(off >= attr->size)) + return -EFBIG; + at24 = dev_get_drvdata(container_of(kobj, struct device, kobj)); return at24_write(at24, buf, off, count); } -- cgit v0.10.2 From 75f0aef6220dc1e73bed238e57b20544ef4f38ec Mon Sep 17 00:00:00 2001 From: Cong Ding Date: Fri, 25 Oct 2013 00:25:53 -0700 Subject: uio: fix memory leak we have to call kobject_put() to clean up the kobject after function kobject_init(), kobject_add(), or kobject_uevent() is called. Signed-off-by: Cong Ding Acked-by: Sebastian Andrzej Siewior Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/uio/uio.c b/drivers/uio/uio.c index 11d4e0a..c8e45ad 100644 --- a/drivers/uio/uio.c +++ b/drivers/uio/uio.c @@ -288,13 +288,13 @@ static int uio_dev_add_attributes(struct uio_device *idev) } map = kzalloc(sizeof(*map), GFP_KERNEL); if (!map) - goto err_map; + goto err_map_kobj; kobject_init(&map->kobj, &map_attr_type); map->mem = mem; mem->map = map; ret = kobject_add(&map->kobj, idev->map_dir, "map%d", mi); if (ret) - goto err_map; + goto err_map_kobj; ret = kobject_uevent(&map->kobj, KOBJ_ADD); if (ret) goto err_map; @@ -313,14 +313,14 @@ static int uio_dev_add_attributes(struct uio_device *idev) } portio = kzalloc(sizeof(*portio), GFP_KERNEL); if (!portio) - goto err_portio; + goto err_portio_kobj; kobject_init(&portio->kobj, &portio_attr_type); portio->port = port; port->portio = portio; ret = kobject_add(&portio->kobj, idev->portio_dir, "port%d", pi); if (ret) - goto err_portio; + goto err_portio_kobj; ret = kobject_uevent(&portio->kobj, KOBJ_ADD); if (ret) goto err_portio; @@ -329,14 +329,18 @@ static int uio_dev_add_attributes(struct uio_device *idev) return 0; err_portio: - for (pi--; pi >= 0; pi--) { + pi--; +err_portio_kobj: + for (; pi >= 0; pi--) { port = &idev->info->port[pi]; portio = port->portio; kobject_put(&portio->kobj); } kobject_put(idev->portio_dir); err_map: - for (mi--; mi>=0; mi--) { + mi--; +err_map_kobj: + for (; mi >= 0; mi--) { mem = &idev->info->mem[mi]; map = mem->map; kobject_put(&map->kobj); -- cgit v0.10.2 From e6418fcc8adaa5c3911295cbe7ddd368b9788616 Mon Sep 17 00:00:00 2001 From: Ben Hutchings Date: Sun, 27 Oct 2013 21:53:40 +0000 Subject: uio: Pass pointers to virt_to_page(), not integers Most architectures define virt_to_page() as a macro that casts its argument such that an argument of type unsigned long will be accepted without complaint. However, the proper type is void *, and passing unsigned long results in a warning on MIPS. Compile-tested only. Signed-off-by: Ben Hutchings Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/uio/uio.c b/drivers/uio/uio.c index c8e45ad..e221f13 100644 --- a/drivers/uio/uio.c +++ b/drivers/uio/uio.c @@ -605,6 +605,7 @@ static int uio_vma_fault(struct vm_area_struct *vma, struct vm_fault *vmf) struct uio_device *idev = vma->vm_private_data; struct page *page; unsigned long offset; + void *addr; int mi = uio_find_mem_index(vma); if (mi < 0) @@ -616,10 +617,11 @@ static int uio_vma_fault(struct vm_area_struct *vma, struct vm_fault *vmf) */ offset = (vmf->pgoff - mi) << PAGE_SHIFT; + addr = (void *)(unsigned long)idev->info->mem[mi].addr + offset; if (idev->info->mem[mi].memtype == UIO_MEM_LOGICAL) - page = virt_to_page(idev->info->mem[mi].addr + offset); + page = virt_to_page(addr); else - page = vmalloc_to_page((void *)(unsigned long)idev->info->mem[mi].addr + offset); + page = vmalloc_to_page(addr); get_page(page); vmf->page = page; return 0; -- cgit v0.10.2 From 7cf1a122b2f3bda5af837a2dbd7564f695301af0 Mon Sep 17 00:00:00 2001 From: Markus Pargmann Date: Tue, 29 Oct 2013 09:19:23 +0100 Subject: w1-gpio: Detect of_gpio_error for first gpio The first DT gpio is necessary for this driver, but errors returned for of_get_gpio are ignored. This patch adds a return value check for the first of_get_gpio. Signed-off-by: Markus Pargmann Acked-by: Evgeniy Polyakov Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/w1/masters/w1-gpio.c b/drivers/w1/masters/w1-gpio.c index f54ece2..ae53e88 100644 --- a/drivers/w1/masters/w1-gpio.c +++ b/drivers/w1/masters/w1-gpio.c @@ -58,6 +58,7 @@ static int w1_gpio_probe_dt(struct platform_device *pdev) { struct w1_gpio_platform_data *pdata = pdev->dev.platform_data; struct device_node *np = pdev->dev.of_node; + int gpio; pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); if (!pdata) @@ -66,7 +67,11 @@ static int w1_gpio_probe_dt(struct platform_device *pdev) if (of_get_property(np, "linux,open-drain", NULL)) pdata->is_open_drain = 1; - pdata->pin = of_get_gpio(np, 0); + gpio = of_get_gpio(np, 0); + if (gpio < 0) + return gpio; + pdata->pin = gpio; + pdata->ext_pullup_enable_pin = of_get_gpio(np, 1); pdev->dev.platform_data = pdata; -- cgit v0.10.2 From d27f25c9c216dc9f9a320fe78f729aebdebcae50 Mon Sep 17 00:00:00 2001 From: Markus Pargmann Date: Tue, 29 Oct 2013 09:19:24 +0100 Subject: w1-gpio: Use devm_* functions Signed-off-by: Markus Pargmann Acked-by: Evgeniy Polyakov Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/w1/masters/w1-gpio.c b/drivers/w1/masters/w1-gpio.c index ae53e88..264ad1c 100644 --- a/drivers/w1/masters/w1-gpio.c +++ b/drivers/w1/masters/w1-gpio.c @@ -99,25 +99,27 @@ static int w1_gpio_probe(struct platform_device *pdev) return -ENXIO; } - master = kzalloc(sizeof(struct w1_bus_master), GFP_KERNEL); + master = devm_kzalloc(&pdev->dev, sizeof(struct w1_bus_master), + GFP_KERNEL); if (!master) { dev_err(&pdev->dev, "Out of memory\n"); return -ENOMEM; } - err = gpio_request(pdata->pin, "w1"); + err = devm_gpio_request(&pdev->dev, pdata->pin, "w1"); if (err) { dev_err(&pdev->dev, "gpio_request (pin) failed\n"); - goto free_master; + return err; } if (gpio_is_valid(pdata->ext_pullup_enable_pin)) { - err = gpio_request_one(pdata->ext_pullup_enable_pin, - GPIOF_INIT_LOW, "w1 pullup"); + err = devm_gpio_request_one(&pdev->dev, + pdata->ext_pullup_enable_pin, GPIOF_INIT_LOW, + "w1 pullup"); if (err < 0) { dev_err(&pdev->dev, "gpio_request_one " "(ext_pullup_enable_pin) failed\n"); - goto free_gpio; + return err; } } @@ -135,7 +137,7 @@ static int w1_gpio_probe(struct platform_device *pdev) err = w1_add_master_device(master); if (err) { dev_err(&pdev->dev, "w1_add_master device failed\n"); - goto free_gpio_ext_pu; + return err; } if (pdata->enable_external_pullup) @@ -147,16 +149,6 @@ static int w1_gpio_probe(struct platform_device *pdev) platform_set_drvdata(pdev, master); return 0; - - free_gpio_ext_pu: - if (gpio_is_valid(pdata->ext_pullup_enable_pin)) - gpio_free(pdata->ext_pullup_enable_pin); - free_gpio: - gpio_free(pdata->pin); - free_master: - kfree(master); - - return err; } static int w1_gpio_remove(struct platform_device *pdev) @@ -171,8 +163,6 @@ static int w1_gpio_remove(struct platform_device *pdev) gpio_set_value(pdata->ext_pullup_enable_pin, 0); w1_remove_master_device(master); - gpio_free(pdata->pin); - kfree(master); return 0; } -- cgit v0.10.2 From 86d6275a8f1b7ba64b79a46c7616264302f44410 Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Wed, 30 Oct 2013 13:35:20 +0800 Subject: tifm: fix error return code in tifm_7xx1_probe() Fix to return ENODEV in the pci ioremap error handling case instead of 0, as done elsewhere in this function. Signed-off-by: Wei Yongjun Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/misc/tifm_7xx1.c b/drivers/misc/tifm_7xx1.c index ae282a1..a606c89 100644 --- a/drivers/misc/tifm_7xx1.c +++ b/drivers/misc/tifm_7xx1.c @@ -356,8 +356,10 @@ static int tifm_7xx1_probe(struct pci_dev *dev, pci_set_drvdata(dev, fm); fm->addr = pci_ioremap_bar(dev, 0); - if (!fm->addr) + if (!fm->addr) { + rc = -ENODEV; goto err_out_free; + } rc = request_irq(dev->irq, tifm_7xx1_isr, IRQF_SHARED, DRIVER_NAME, fm); if (rc) -- cgit v0.10.2 From 425792266a40189e0b3fec02cb59a69935d8c58c Mon Sep 17 00:00:00 2001 From: Ashutosh Dixit Date: Tue, 29 Oct 2013 17:09:59 -0700 Subject: misc: mic: Fixes for randconfig build errors and warnings. This patch fixes the build errors and warnings reported at https://lkml.org/lkml/2013/10/29/421. Co-author: Dasaratharaman Chandramouli Reviewed-by: Sudeep Dutt Reported-by: Jim Davis Signed-off-by: Ashutosh Dixit Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/misc/mic/host/mic_boot.c b/drivers/misc/mic/host/mic_boot.c index d56b8bf..b079c65 100644 --- a/drivers/misc/mic/host/mic_boot.c +++ b/drivers/misc/mic/host/mic_boot.c @@ -20,6 +20,7 @@ */ #include #include +#include #include #include "../common/mic_dev.h" diff --git a/drivers/misc/mic/host/mic_fops.c b/drivers/misc/mic/host/mic_fops.c index 8dc6ff1..85776d7 100644 --- a/drivers/misc/mic/host/mic_fops.c +++ b/drivers/misc/mic/host/mic_fops.c @@ -19,6 +19,7 @@ * */ #include +#include #include #include "../common/mic_dev.h" diff --git a/drivers/misc/mic/host/mic_virtio.c b/drivers/misc/mic/host/mic_virtio.c index 0c883cd..5b8494b 100644 --- a/drivers/misc/mic/host/mic_virtio.c +++ b/drivers/misc/mic/host/mic_virtio.c @@ -517,10 +517,10 @@ int mic_virtio_add_device(struct mic_vdev *mvdev, void __user *argp) { struct mic_device *mdev = mvdev->mdev; - struct mic_device_desc *dd; + struct mic_device_desc *dd = NULL; struct mic_vqconfig *vqconfig; int vr_size, i, j, ret; - u8 type; + u8 type = 0; s8 db; char irqname[10]; struct mic_bootparam *bootparam = mdev->dp; -- cgit v0.10.2