*/
#define DS_TIMER_BUG_WAR 1
+/* Def to enable memory buffer debugging */
+#define DS_MEM_DEBUG 1
+
/*
* Def to enable ioctl to inject a PRI update
* event for testing purposes.
* could result in overloading console screen/logs.
*/
#define dprintk_lvl(lvl, fmt, args...) do {\
-if (lvl <= dsdbg_level)\
+if (dsdbg_level < 8 && lvl <= dsdbg_level)\
printk(KERN_ERR "%s: %s: " fmt, DRV_MODULE_NAME, __func__, ##args);\
} while (0)
#define dprintk1(fmt, args...) dprintk_lvl(1, fmt, ##args)
#define dprintk2(fmt, args...) dprintk_lvl(2, fmt, ##args)
#define dprintk3(fmt, args...) dprintk_lvl(3, fmt, ##args)
+#ifdef DS_MEM_DEBUG
+/* we use debug level 8 exclusively for low level mem buf debugging */
+#define dprintk_mem(fmt, args...) do {\
+if (dsdbg_level == 8)\
+ printk(KERN_ERR "%s: %s: " fmt, DRV_MODULE_NAME, __func__, ##args);\
+} while (0)
+#endif
/* existing dprintk() calls in the code will translate to level 2 */
#define dprintk(fmt, args...) dprintk2(fmt, ##args)
#define LDC_IRQ_NAME_MAX 32
-#define DS_DEFAULT_BUF_SIZE 4096
+/* The largest contiguous buffer the kernel seems to allow is 8MB. */
+#define DS_MAX_BUF_SIZE (8*1024*1024)
#define DS_DEFAULT_MTU 4096
-/*
- * The SP DS needs a huge buffer to handle PRI update messages.
- * The largest contiguous buffer the kernel seems to allow is 8MB.
- */
-#define DS_DEFAULT_SP_BUF_SIZE (8*1024*1024)
#define DS_PRIMARY_ID 0
u64 ds_local_ldom_handle;
bool ds_local_ldom_handle_set;
-/* Global driver data struct for data common to all ds devices. */
-struct ds_driver_data {
-
- /* list of all ds devices */
- struct list_head ds_dev_list;
- int num_ds_dev_list;
-
-};
-struct ds_driver_data ds_data;
-static DEFINE_SPINLOCK(ds_data_lock); /* protect ds_data */
-
/*
* For each DS port, a timer fires every DS_REG_TIMER_FREQ
* milliseconds to attempt to register services on that DS port.
*/
-#define DS_REG_TIMER_FREQ 100 /* in ms */
+#define DS_REG_TIMER_FREQ 2000 /* in ms */
/* Timeout to wait for responses for sp-token and var-config DS requests */
#define DS_RESPONSE_TIMEOUT 10 /* in seconds */
#define DS_LDC_READ_DELAY_CNT 10000 /* each CNT will wait 10ms */
+#define DS_LDC_WRITE_DELAY_CNT 10000 /* each CNT will wait 10ms */
#ifdef DS_TIMER_BUG_WAR
/*
/* negotiated DS version */
ds_ver_t neg_vers;
- /* LDC receive data buffer for this ds_dev */
+ /* LDC receive data buffer currently assigned to this ds_dev */
u8 *rcv_buf;
- u64 rcv_buf_len;
+
u32 mtu;
/* service registration timer */
#define DS_HS_START 0x01
#define DS_HS_COMPLETE 0x02
+/* list of all ds devices */
+struct list_head ds_dev_list;
+static DEFINE_SPINLOCK(ds_dev_list_lock);
+
/*
* LDC interrupts are not blocked by spin_lock_irqsave(). So, for any
* lock which the LDC interrupt handler (ds_event) obtains, we must
ldc_enable_hv_intr((ds)->lp); \
} while (0);
+
+/*
+ * We maintain a list of memory buffers which can be reused to hold
+ * event receive data, etc. This is more efficient than allocating from
+ * the kernel general memory pool on every event or allocating static
+ * max-size buffers for every DS dev since there is a DS dev for every
+ * ldom (when executing in the primary domain) and the data for some
+ * events can be large (>4K) - resulting in a potentially huge amount of
+ * statically allocated kernel memory which has low utilization.
+ */
+struct ds_mem_buf {
+ /* link into the global driver memory buffer list */
+ struct list_head list;
+
+ unsigned long bufsize;
+ void *buf;
+
+ /* flag to indicate if the buffer is free/available for use */
+ bool free;
+
+#ifdef DS_MEM_DEBUG
+ /* realloc_cnt and num are used for debugging only */
+ u64 realloc_cnt;
+ u64 num;
+#endif
+};
+
+/* list of all ds memory buffers */
+struct list_head ds_mem_buf_list;
+static DEFINE_SPINLOCK(ds_mem_buf_list_lock);
+#ifdef DS_MEM_DEBUG
+u64 ds_mem_buf_num; /* debugging only */
+#endif
+
/*
* Generic service info structure used to describe
* a provider service or local client service.
}
__setup(LDOMS_DEBUG_LEVEL_SETUP, ldoms_debug_level_setup);
-static void *ds_zalloc_pages(unsigned long size, gfp_t alloc_flags)
+#ifdef DS_MEM_DEBUG
+static void ds_dump_mem_buf_list(void)
+{
+ struct ds_mem_buf *dsbuf;
+ unsigned long flags;
+
+ dprintk_mem("DS MEM DEBUG: DUMP MEM BUF LIST\n");
+
+ spin_lock_irqsave(&ds_mem_buf_list_lock, flags);
+ list_for_each_entry(dsbuf, &ds_mem_buf_list, list) {
+ dprintk_mem("DS MEM DEBUG: Entry[%llu] size=%lu, "
+ "realloccnt=%llu free=%u\n",
+ dsbuf->num, dsbuf->bufsize, dsbuf->realloc_cnt,
+ dsbuf->free);
+ }
+ spin_unlock_irqrestore(&ds_mem_buf_list_lock, flags);
+}
+#endif /* DS_MEM_DEBUG */
+
+static void *ds_zalloc_mem_buf(unsigned long size, gfp_t alloc_flags)
{
+ unsigned long buf_alloc_size;
+ struct ds_mem_buf *dsbuf;
+ unsigned long flags;
unsigned long order;
void *buf;
+#ifdef DS_MEM_DEBUG
+ static u64 print_membuf_list_cnt;
+#endif
+
+#ifdef DS_MEM_DEBUG
+ dprintk_mem("DS MEM DEBUG: entered\n");
+ dprintk_mem("DS MEM DEBUG: size=%lu\n", size);
+
+ /* dump the full mem buf list every 100 allocs */
+ if (!(print_membuf_list_cnt++ % 100))
+ ds_dump_mem_buf_list();
+#endif
+
+ if (size > DS_MAX_BUF_SIZE) {
+ pr_err("%s: alloc size too large (%lu)\n", __func__, size);
+ return NULL;
+ }
+
+ /*
+ * See if there's an available buffer in the global list
+ * that matches the requested size.
+ * NOTE - we round the size up to the nearest power of 2
+ * to increase the likelihood of future matching/reuse.
+ */
+ for (buf_alloc_size = 1; buf_alloc_size < size; buf_alloc_size <<= 1)
+ ;
+
+ spin_lock_irqsave(&ds_mem_buf_list_lock, flags);
+ list_for_each_entry(dsbuf, &ds_mem_buf_list, list) {
+ if (dsbuf->free && buf_alloc_size == dsbuf->bufsize) {
+ dsbuf->free = false;
+#ifdef DS_MEM_DEBUG
+ dsbuf->realloc_cnt++;
+ dprintk_mem("DS MEM DEBUG: realloc mem buf %llu, "
+ "size=%lu, realloccnt=%llu\n", dsbuf->num,
+ dsbuf->bufsize, dsbuf->realloc_cnt);
+#endif
+ spin_unlock_irqrestore(&ds_mem_buf_list_lock, flags);
+ return dsbuf->buf;
+ }
+ }
+ spin_unlock_irqrestore(&ds_mem_buf_list_lock, flags);
+
+ /*
+ * Didn't find an available memory buffer in the list.
+ * Alloc a new buffer and add it to the global list.
+ */
- order = get_order(size);
- buf = (void *) __get_free_pages(alloc_flags, order);
+ /* First, alloc the ds_mem_buf for the memory buffer */
+ dsbuf = kzalloc(sizeof(struct ds_mem_buf), alloc_flags);
+ if (dsbuf == NULL) {
+ pr_err("%s: FAILED to alloc ds_mem_buf\n", __func__);
+ return NULL;
+ }
- if (buf)
- memset(buf, 0, PAGE_SIZE << order);
+ /*
+ * Alloc the buffer - using get_free_pages for large buffers
+ * (>=PAGE_SIZE) or kzalloc for smaller buffers.
+ */
+ if (buf_alloc_size >= PAGE_SIZE) {
+ order = get_order(buf_alloc_size);
+ buf = (void *) __get_free_pages(alloc_flags, order);
+ if (buf)
+ memset(buf, 0, PAGE_SIZE << order);
+ } else {
+ buf = kzalloc(buf_alloc_size, alloc_flags);
+ }
+ if (buf == NULL) {
+ pr_err("%s: FAILED to alloc mem buffer (%lu)\n",
+ __func__, buf_alloc_size);
+ kfree(dsbuf);
+ return NULL;
+ }
- return buf;
+ dsbuf->free = false;
+ dsbuf->buf = buf;
+ dsbuf->bufsize = buf_alloc_size;
+#ifdef DS_MEM_DEBUG
+ dsbuf->num = ds_mem_buf_num++;
+ dprintk_mem("DS MEM DEBUG: new mem buf %llu, size=%lu\n",
+ dsbuf->num, dsbuf->bufsize);
+#endif
+
+ /* Insert the new ds_mem_buf into the global list */
+ spin_lock_irqsave(&ds_mem_buf_list_lock, flags);
+ list_add(&dsbuf->list, &ds_mem_buf_list);
+ spin_unlock_irqrestore(&ds_mem_buf_list_lock, flags);
+
+ return dsbuf->buf;
}
-static void ds_free_pages(void *buf, unsigned long size)
+static void ds_free_mem_buf(void *buf)
{
- if (!buf)
- return;
+ struct ds_mem_buf *dsbuf;
+ unsigned long flags;
+ bool found;
+
+ found = false;
+
+ spin_lock_irqsave(&ds_mem_buf_list_lock, flags);
+ list_for_each_entry(dsbuf, &ds_mem_buf_list, list) {
+ if (dsbuf->buf == buf) {
+ /* mark it free, but keep it in the list for reuse */
+ dsbuf->free = true;
+ found = true;
+ break;
+ }
+ }
+ spin_unlock_irqrestore(&ds_mem_buf_list_lock, flags);
- free_pages((unsigned long)buf, get_order(size));
+ if (!found)
+ pr_err("%s: FAILED to find mem buf at %p\n", __func__, buf);
}
static void ds_reset(struct ds_dev *ds)
ldc_clr_reset(ds->lp);
}
-static int ds_ldc_send_msg(struct ldc_channel *lp, void *data, int len)
+static int ds_ldc_send_msg(struct ds_dev *ds, u8 *buf, int len)
{
- int rv, limit = 1000;
+ unsigned int bytes_left;
+ unsigned int bytes_written;
+ unsigned int write_size;
+ unsigned int delay_cnt;
+ int rv;
- rv = -EINVAL;
- while (limit-- > 0) {
- rv = ldc_write(lp, data, len);
- if (rv != -EAGAIN)
+ /* Validate that the channel is UP */
+ if (ldc_chan_state(ds->lp) != LDC_CHANNEL_UP) {
+ dprintk("ds-%llu: FAILED to send data, channel not UP\n",
+ ds->id);
+ return -EIO;
+ }
+
+ bytes_left = len;
+ bytes_written = 0;
+ delay_cnt = 0;
+ rv = 0;
+ while (bytes_left) {
+
+ write_size = min_t(int, bytes_left, ds->mtu);
+
+ rv = ldc_write(ds->lp, (void *) (buf + bytes_written),
+ write_size);
+
+ if ((rv == -EAGAIN || rv == 0) &&
+ delay_cnt++ < DS_LDC_WRITE_DELAY_CNT) {
+ /*
+ * For large messages, give the other
+ * end of the LDC a chance to
+ * read the data from the LDC.
+ */
+ mdelay(10);
+ continue;
+ }
+
+ if (rv <= 0)
break;
- udelay(1);
+
+ bytes_left -= rv;
+ bytes_written += rv;
}
- return rv;
+ if (rv < 0)
+ return rv;
+
+ return bytes_written;
}
-static int ds_ldc_send_payload(struct ldc_channel *lp, u32 type,
+static int ds_ldc_send_payload(struct ds_dev *ds, u32 type,
void *data, int len)
{
struct ds_msg *msg;
msg->tag.len = len;
memcpy(msg->payload, data, len);
- rv = ds_ldc_send_msg(lp, msg, msglen);
+ rv = ds_ldc_send_msg(ds, (u8 *)msg, msglen);
kfree(msg);
req.handle = handle;
req.result = result;
- rv = ds_ldc_send_payload(ds->lp, DS_NACK, &req, sizeof(req));
+ rv = ds_ldc_send_payload(ds, DS_NACK, &req, sizeof(req));
if (rv <= 0)
pr_err("ds-%llu: %s: ldc_send failed. (%d)\n ", ds->id,
__func__, rv);
* call back into this driver and attempt to re-acquire
* the lock(s) resulting in deadlock.
*/
- spin_lock_irqsave(&ds_data_lock, flags);
- list_for_each_entry(ds, &ds_data.ds_dev_list, list) {
+ spin_lock_irqsave(&ds_dev_list_lock, flags);
+ list_for_each_entry(ds, &ds_dev_list, list) {
LOCK_DS_DEV(ds, ds_flags)
list_splice_tail_init(&ds->callout_list, &todo);
UNLOCK_DS_DEV(ds, ds_flags)
}
- spin_unlock_irqrestore(&ds_data_lock, flags);
+ spin_unlock_irqrestore(&ds_dev_list_lock, flags);
list_for_each_entry_safe(qhdrp, tmp, &todo, list) {
kfree(qhdrp->ds);
list_del(&qhdrp->list);
- kfree(qhdrp);
+ ds_free_mem_buf(qhdrp);
continue;
}
if (unlikely(svc_info == NULL)) {
UNLOCK_DS_DEV(ds, ds_flags)
list_del(&qhdrp->list);
- kfree(qhdrp);
+ ds_free_mem_buf(qhdrp);
continue;
}
if (unlikely(svc_info == NULL)) {
UNLOCK_DS_DEV(ds, ds_flags)
list_del(&qhdrp->list);
- kfree(qhdrp);
+ ds_free_mem_buf(qhdrp);
continue;
}
/* done processing the entry, remove it from the list */
list_del(&qhdrp->list);
- kfree(qhdrp);
+ ds_free_mem_buf(qhdrp);
}
dprintk("ds: CPU[%d]: callout processing END\n", smp_processor_id());
prepare_to_wait(&ds_wait, &wait, TASK_INTERRUPTIBLE);
work_to_do = false;
- spin_lock_irqsave(&ds_data_lock, flags);
- list_for_each_entry(ds, &ds_data.ds_dev_list, list) {
+ spin_lock_irqsave(&ds_dev_list_lock, flags);
+ list_for_each_entry(ds, &ds_dev_list, list) {
if (!list_empty(&ds->callout_list)) {
work_to_do = true;
break;
}
}
- spin_unlock_irqrestore(&ds_data_lock, flags);
+ spin_unlock_irqrestore(&ds_dev_list_lock, flags);
if (!work_to_do)
schedule();
* This function is called with spinlocks held,
* so we must use GFP_ATOMIC.
*/
- rentry = kzalloc(sizeof(struct ds_callout_reg_entry), GFP_ATOMIC);
+ rentry = ds_zalloc_mem_buf(sizeof(struct ds_callout_reg_entry),
+ GFP_ATOMIC);
if (!rentry) {
pr_err("%s: FAILED to alloc callout entry!\n", __func__);
return -ENOMEM;
* This function is called with spinlocks held,
* so we must use GFP_ATOMIC.
*/
- dentry = kzalloc(sizeof(struct ds_callout_data_entry) + pktlen,
- GFP_ATOMIC);
+ dentry = ds_zalloc_mem_buf(sizeof(struct ds_callout_data_entry) +
+ pktlen, GFP_ATOMIC);
if (!dentry) {
pr_err("%s: FAILED to alloc callout entry!\n", __func__);
return -ENOMEM;
/* Find the ds_dev associated with domain_hdl. */
found = false;
- spin_lock_irqsave(&ds_data_lock, data_flags);
- list_for_each_entry(ds, &ds_data.ds_dev_list, list) {
+ spin_lock_irqsave(&ds_dev_list_lock, data_flags);
+ list_for_each_entry(ds, &ds_dev_list, list) {
LOCK_DS_DEV(ds, ds_flags)
UNLOCK_DS_DEV(ds, ds_flags)
}
- spin_unlock_irqrestore(&ds_data_lock, data_flags);
+ spin_unlock_irqrestore(&ds_dev_list_lock, data_flags);
if (!found) {
pr_err("%s: Error: dom_hdl %llu DS port not found\n",
/* Find and remove all services associated with hdl. */
- spin_lock_irqsave(&ds_data_lock, flags);
+ spin_lock_irqsave(&ds_dev_list_lock, flags);
- list_for_each_entry(ds, &ds_data.ds_dev_list, list) {
+ list_for_each_entry(ds, &ds_dev_list, list) {
LOCK_DS_DEV(ds, ds_flags)
UNLOCK_DS_DEV(ds, ds_flags)
}
- spin_unlock_irqrestore(&ds_data_lock, flags);
+ spin_unlock_irqrestore(&ds_dev_list_lock, flags);
return 0;
svc_info = NULL;
- spin_lock_irqsave(&ds_data_lock, flags);
- list_for_each_entry(ds, &ds_data.ds_dev_list, list) {
+ spin_lock_irqsave(&ds_dev_list_lock, flags);
+ list_for_each_entry(ds, &ds_dev_list, list) {
LOCK_DS_DEV(ds, ds_flags)
UNLOCK_DS_DEV(ds, ds_flags)
}
- spin_unlock_irqrestore(&ds_data_lock, flags);
+ spin_unlock_irqrestore(&ds_dev_list_lock, flags);
if (svc_info == NULL) {
pr_err("%s: Error: no service found "
ds->id, __func__);
} else {
/* send the data out to the LDC */
- rv = ds_ldc_send_msg(ds->lp, (void *)hdr, msglen);
+ rv = ds_ldc_send_msg(ds, (u8 *)hdr, msglen);
if (rv <= 0) {
pr_err("ds-%llu: %s: ldc_send failed.(%d)\n ",
ds->id, __func__, rv);
unsigned long ds_flags = 0;
struct ds_service_info *svc_info;
- spin_lock_irqsave(&ds_data_lock, flags);
+ spin_lock_irqsave(&ds_dev_list_lock, flags);
- list_for_each_entry(ds, &ds_data.ds_dev_list, list) {
+ list_for_each_entry(ds, &ds_dev_list, list) {
LOCK_DS_DEV(ds, ds_flags)
svc_info = ds_find_service_provider_id(ds, svc_id);
if (svc_info != NULL && svc_info->is_connected) {
UNLOCK_DS_DEV(ds, ds_flags)
- spin_unlock_irqrestore(&ds_data_lock, flags);
+ spin_unlock_irqrestore(&ds_dev_list_lock, flags);
return svc_info;
}
UNLOCK_DS_DEV(ds, ds_flags)
}
- spin_unlock_irqrestore(&ds_data_lock, flags);
+ spin_unlock_irqrestore(&ds_dev_list_lock, flags);
return NULL;
} else if (client_svc_info->reg_state == DS_REG_STATE_REGISTERED_LDC) {
rv = ds_service_unreg(ds, client_svc_info->con_handle);
if (rv != 0) {
- pr_err("ds-%llu: %s: failed to send UNREG_REQ for "
- "handle %llx (%d)\n", ds->id, __func__,
+ dprintk("ds-%llu: failed to send UNREG_REQ for "
+ "handle %llx (%d)\n", ds->id,
client_svc_info->con_handle, rv);
}
}
DS_REG_STATE_REGISTERED_LDC) {
rv = ds_service_unreg(ds, provider_svc_info->con_handle);
if (rv != 0) {
- pr_err("ds-%llu: %s: failed to send UNREG_REQ for "
- "handle %llx (%d)\n", ds->id, __func__,
+ dprintk("ds-%llu: failed to send UNREG_REQ for "
+ "handle %llx (%d)\n", ds->id,
provider_svc_info->con_handle, rv);
}
}
return;
}
- dprintk("%s: found %s client service\n", __func__, svc_info->id);
+ dprintk("found %s client service\n", svc_info->id);
memset(&payload, 0, sizeof(payload));
payload.msg.hdr.type = DS_VAR_SET_REQ;
* once to avoid flooding the console.
*/
printk_once("%s: sp-token service not registered.\n", __func__);
- dprintk3("%s: sp-token service not registered.\n", __func__);
+ dprintk3("sp-token service not registered.\n");
return -EIO;
}
payload->req_num = ds_sp_token_next_req_num;
- dprintk("%s: sizeof ds_sp_token_msg=%lu svclen=%d.\n",
- __func__, sizeof(struct ds_sp_token_msg), svc_len);
+ dprintk("sizeof ds_sp_token_msg=%lu svclen=%d.\n",
+ sizeof(struct ds_sp_token_msg), svc_len);
dprintk("req_num %llu: payload(%p): type[0x%llx] svc[%s].\n",
payload->req_num, payload, payload->type, payload->service);
pbuf.req.minor = svc_info->vers.minor;
strcpy(pbuf.req.svc_id, svc_info->id);
- rv = ds_ldc_send_payload(ds->lp, DS_REG_REQ, &pbuf, payload_len);
+ rv = ds_ldc_send_payload(ds, DS_REG_REQ, &pbuf, payload_len);
if (rv > 0)
dprintk3("ds-%llu: DS_REG_REQ sent for %s service (%llu.%llu), "
req.handle = handle;
- rv = ds_ldc_send_payload(ds->lp, DS_UNREG_REQ, &req, sizeof(req));
+ rv = ds_ldc_send_payload(ds, DS_UNREG_REQ, &req, sizeof(req));
return (rv <= 0);
}
req.handle = handle;
req.minor = minor;
- rv = ds_ldc_send_payload(ds->lp, DS_REG_ACK, &req, sizeof(req));
+ rv = ds_ldc_send_payload(ds, DS_REG_ACK, &req, sizeof(req));
if (rv <= 0)
pr_err("ds-%llu: %s: ldc_send failed. (%d)\n ", ds->id,
__func__, rv);
req.result = result;
req.major = major;
- rv = ds_ldc_send_payload(ds->lp, DS_REG_NACK, &req, sizeof(req));
+ rv = ds_ldc_send_payload(ds, DS_REG_NACK, &req, sizeof(req));
if (rv <= 0)
pr_err("ds-%llu: %s: ldc_send failed. (%d)\n ", ds->id,
__func__, rv);
req.handle = handle;
- rv = ds_ldc_send_payload(ds->lp, DS_UNREG_ACK, &req, sizeof(req));
+ rv = ds_ldc_send_payload(ds, DS_UNREG_ACK, &req, sizeof(req));
if (rv <= 0)
pr_err("ds-%llu: %s: ldc_send failed. (%d)\n ", ds->id,
__func__, rv);
reg_req->payload.svc_id);
if (svc_info == NULL) {
/* There is no registered service */
- dprintk1("ds-%llu: no service registered for "
+ dprintk3("ds-%llu: no service registered for "
"REG_REQ service %s (%llx)\n", ds->id,
reg_req->payload.svc_id,
reg_req->payload.handle);
req.ver.major = DS_MAJOR_VERSION;
req.ver.minor = DS_MINOR_VERSION;
- rv = ds_ldc_send_payload(ds->lp, DS_INIT_REQ, &req, sizeof(req));
+ rv = ds_ldc_send_payload(ds, DS_INIT_REQ, &req, sizeof(req));
return (rv <= 0);
}
req.minor = DS_MINOR_VERSION;
- rv = ds_ldc_send_payload(ds->lp, DS_INIT_ACK, &req, sizeof(req));
+ rv = ds_ldc_send_payload(ds, DS_INIT_ACK, &req, sizeof(req));
if (rv <= 0)
pr_err("ds-%llu: %s: ldc_send failed. (%d)\n ", ds->id,
__func__, rv);
req.major = major;
- rv = ds_ldc_send_payload(ds->lp, DS_INIT_NACK, &req, sizeof(req));
+ rv = ds_ldc_send_payload(ds, DS_INIT_NACK, &req, sizeof(req));
if (rv <= 0)
pr_err("ds-%llu: %s: ldc_send failed. (%d)\n ", ds->id,
__func__, rv);
rv = ldc_read(ds->lp, (void *)(buf + bytes_read), read_size);
- if (delay_cnt++ < DS_LDC_READ_DELAY_CNT &&
- (rv == -EAGAIN || rv == 0)) {
+ if ((rv == -EAGAIN || rv == 0) &&
+ delay_cnt++ < DS_LDC_READ_DELAY_CNT) {
/*
* For huge messages (such as PRI Update),
* give the other end of the LDC a chance to
if (rv < 0)
return rv;
- else
- return bytes_read;
+
+ return bytes_read;
}
static void ds_event(void *arg, int event)
struct ds_dev *ds = arg;
unsigned long flags;
unsigned long buf_size;
+ struct ds_msg_tag tag;
int rv;
/*
rv = 0;
while (1) {
- struct ds_msg_tag *tag;
-
- rv = ldc_read(ds->lp, ds->rcv_buf, sizeof(*tag));
+ rv = ldc_read(ds->lp, &tag, sizeof(tag));
- dprintk3("ds-%llu: ldc_read 1 returns rv=%d\n", ds->id, rv);
+ dprintk3("ds-%llu: ldc_read tag returns rv=%d\n", ds->id, rv);
if (unlikely(rv < 0)) {
if (rv == -ECONNRESET)
if (rv == 0)
break;
- tag = (struct ds_msg_tag *)ds->rcv_buf;
+ /* alloc a buffer large enough to hold the event data and tag */
+ buf_size = (sizeof(tag) + tag.len);
- /* Make sure the read won't overrun our buffer */
- if (ds->handle == DS_SP_DMN_HANDLE)
- buf_size = DS_DEFAULT_SP_BUF_SIZE;
- else
- buf_size = DS_DEFAULT_BUF_SIZE;
-
- if (tag->len > (buf_size - sizeof(struct ds_msg_tag))) {
- pr_err("ds-%llu: %s: received msg length %d too big.\n",
- ds->id, __func__, tag->len);
+ ds->rcv_buf = ds_zalloc_mem_buf(buf_size, GFP_ATOMIC);
+ if (!ds->rcv_buf) {
+ pr_err("ds-%llu: %s: failed to alloc rcv buf "
+ "(tag len=%d).\n", ds->id, __func__, tag.len);
ds_reset(ds);
break;
}
- rv = ds_read_ldc_msg(ds, (unsigned char *)(tag + 1),
- tag->len);
+ /* copy the tag into the buf */
+ (void) memcpy((void *)ds->rcv_buf, (void *)&tag, sizeof(tag));
- dprintk3("ds-%llu: ldc_read 2 returns rv=%d\n", ds->id, rv);
+ rv = ds_read_ldc_msg(ds, ((u8 *)ds->rcv_buf + sizeof(tag)),
+ tag.len);
+
+ dprintk3("ds-%llu: ldc_read data returns rv=%d\n", ds->id, rv);
if (unlikely(rv < 0)) {
if (rv == -ECONNRESET)
else
dprintk1("ds-%llu: ldc_read_ldc_msg "
"returned err=%d\n", ds->id, rv);
-
+ ds_free_mem_buf(ds->rcv_buf);
+ ds->rcv_buf = NULL;
break;
}
- if (rv < tag->len) {
+ if (rv < tag.len) {
dprintk1("ds-%llu: ldc_read returned %d bytes "
- "< taglen=%d\n", ds->id, rv, tag->len);
+ "< taglen=%d\n", ds->id, rv, tag.len);
+ ds_free_mem_buf(ds->rcv_buf);
+ ds->rcv_buf = NULL;
break;
}
- if (tag->type < DS_DATA) {
+ if (tag.type < DS_DATA) {
dprintk3("ds-%llu: hs data received (%d bytes)\n",
ds->id, rv);
rv = ds_handshake_msg(ds,
if (unlikely(rv < 0)) {
- if (rv == -ECONNRESET)
+ if (rv == -ECONNRESET) {
+ ds_free_mem_buf(ds->rcv_buf);
+ ds->rcv_buf = NULL;
break;
+ }
pr_err("ds-%llu: %s: failed process data "
"packet rv = %d\n", ds->id, __func__, rv);
}
+
+ ds_free_mem_buf(ds->rcv_buf);
+ ds->rcv_buf = NULL;
}
spin_unlock_irqrestore(&ds->ds_lock, flags);
int rv;
/* find the SP DS (if present ) */
- spin_lock_irqsave(&ds_data_lock, data_flags);
+ spin_lock_irqsave(&ds_dev_list_lock, data_flags);
found = false;
- list_for_each_entry(ds, &ds_data.ds_dev_list, list) {
+ list_for_each_entry(ds, &ds_dev_list, list) {
LOCK_DS_DEV(ds, ds_flags)
UNLOCK_DS_DEV(ds, ds_flags)
}
- spin_unlock_irqrestore(&ds_data_lock, data_flags);
+ spin_unlock_irqrestore(&ds_dev_list_lock, data_flags);
if (!found) {
pr_err("%s: failed to SP DS.\n", __func__);
switch (cmd) {
case DS_SPTOK_GET:
- dprintk("%s: Getting sp-token\n", __func__);
+ dprintk("Getting sp-token\n");
uarg = (ds_ioctl_sptok_data_t __user *)arg;
if (get_user(major_version, &uarg->major_version) != 0 ||
get_user(minor_version, &uarg->minor_version) != 0 ||
case DS_PRI_GET:
- dprintk("%s: Getting HV PRI\n", __func__);
+ dprintk("Getting HV PRI\n");
/* Only allow one thread at a time to access the HV PRI */
mutex_lock(&ds_ioctl_hv_pri_mutex);
#ifdef DS_PRI_TEST
case DS_PRI_SET:
- dprintk("%s: Initiating PRI Update\n", __func__);
+ dprintk("Initiating PRI Update\n");
rv = ds_set_pri();
mdesc_release(hp);
- /* allocate receive buffers */
- if (is_sp) {
- ds->rcv_buf = ds_zalloc_pages(DS_DEFAULT_SP_BUF_SIZE,
- GFP_KERNEL);
- if (unlikely(!ds->rcv_buf))
- goto out_free_ds;
-
- ds->rcv_buf_len = DS_DEFAULT_SP_BUF_SIZE;
- } else {
- ds->rcv_buf = kzalloc(DS_DEFAULT_BUF_SIZE,
- GFP_KERNEL);
- if (unlikely(!ds->rcv_buf))
- goto out_free_ds;
-
- ds->rcv_buf_len = DS_DEFAULT_BUF_SIZE;
- }
-
ds_cfg.mtu = DS_DEFAULT_MTU;
ds->mtu = ds_cfg.mtu;
lp = ldc_alloc(vdev->channel_id, &ds_cfg, ds, ds_irq_name);
if (IS_ERR(lp)) {
rv = PTR_ERR(lp);
- goto out_free_rcv_buf;
+ goto out_free_ds;
}
ds->lp = lp;
UNLOCK_DS_DEV(ds, ds_flags)
- /* add the ds_dev to the global ds_data device list */
- spin_lock_irqsave(&ds_data_lock, flags);
- list_add_tail(&ds->list, &ds_data.ds_dev_list);
- ds_data.num_ds_dev_list++;
- spin_unlock_irqrestore(&ds_data_lock, flags);
+ /* add the ds_dev to the global ds device list */
+ spin_lock_irqsave(&ds_dev_list_lock, flags);
+ list_add_tail(&ds->list, &ds_dev_list);
+ spin_unlock_irqrestore(&ds_dev_list_lock, flags);
return rv;
out_free_ldc:
ldc_free(ds->lp);
-out_free_rcv_buf:
- if (is_sp)
- ds_free_pages(ds->rcv_buf, DS_DEFAULT_SP_BUF_SIZE);
- else
- kfree(ds->rcv_buf);
-
out_free_ds:
kfree(ds);
* Lock the global ds_dev list to prevent another thread
* from finding the ds in the list while we are removing it.
*/
- spin_lock_irqsave(&ds_data_lock, flags);
+ spin_lock_irqsave(&ds_dev_list_lock, flags);
/*
* Lock down the ds_dev to prevent removing it
*/
LOCK_DS_DEV(ds, ds_flags)
- /* remove the ds_dev from the global ds_data device list */
+ /* remove the ds_dev from the global ds device list */
list_del(&ds->list);
- ds_data.num_ds_dev_list--;
del_timer_sync(&ds->ds_reg_tmr);
ldc_free(ds->lp);
- if (ds->handle == DS_SP_DMN_HANDLE)
- ds_free_pages(ds->rcv_buf, DS_DEFAULT_SP_BUF_SIZE);
- else
- kfree(ds->rcv_buf);
-
/* free any entries left on the callout list */
list_for_each_entry_safe(qhdrp, tmp, &ds->callout_list, list) {
list_del(&qhdrp->list);
- kfree(qhdrp);
+ ds_free_mem_buf(qhdrp);
ds->co_ref_cnt--;
}
UNLOCK_DS_DEV(ds, ds_flags)
}
- spin_unlock_irqrestore(&ds_data_lock, flags);
+ spin_unlock_irqrestore(&ds_dev_list_lock, flags);
return 0;
}
dprintk("%s", version);
- INIT_LIST_HEAD(&ds_data.ds_dev_list);
- ds_data.num_ds_dev_list = 0;
+ INIT_LIST_HEAD(&ds_dev_list);
+
+ INIT_LIST_HEAD(&ds_mem_buf_list);
err = misc_register(&ds_miscdev);
if (err)