From 5693ce6f9b9f08942e55e3825db014f8b1205772 Mon Sep 17 00:00:00 2001 From: Cornelia Huck Date: Fri, 10 Aug 2007 14:32:26 +0200 Subject: [S390] cio: avoid memory leak on error in css_alloc_subchannel(). sch->lock has been allocated in cio_validate_subchannel(), it must be freed if cio_modify() fails. Signed-off-by: Cornelia Huck Signed-off-by: Martin Schwidefsky --- drivers/s390/cio/css.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/s390/cio/css.c b/drivers/s390/cio/css.c index 1c27a5a06b4..5635e656c1a 100644 --- a/drivers/s390/cio/css.c +++ b/drivers/s390/cio/css.c @@ -79,6 +79,7 @@ css_alloc_subchannel(struct subchannel_id schid) sch->schib.pmcw.intparm = (__u32)(unsigned long)sch; ret = cio_modify(sch); if (ret) { + kfree(sch->lock); kfree(sch); return ERR_PTR(ret); } -- cgit v1.2.3 From c6d0e8014a59b641c0669cf5df151667144f220e Mon Sep 17 00:00:00 2001 From: Heiko Carstens Date: Fri, 10 Aug 2007 14:32:28 +0200 Subject: [S390] qdio: make sure data structures are correctly aligned. The slsb structure contained at the beginning of the qdio_q structure must start on a 256 byte boundary. To make sure this is the case even if slab debugging is turned on create an own slab cache for qdio_q structures. Besides that don't use the slab allocator to allocate whole pages. Use the page allocator instead. Also fix a few memory leaks in error handling code. Signed-off-by: Heiko Carstens Signed-off-by: Martin Schwidefsky --- drivers/s390/cio/qdio.c | 92 +++++++++++++++++++++++++------------------------ 1 file changed, 47 insertions(+), 45 deletions(-) (limited to 'drivers') diff --git a/drivers/s390/cio/qdio.c b/drivers/s390/cio/qdio.c index ed026a1dc32..03347aed2b3 100644 --- a/drivers/s390/cio/qdio.c +++ b/drivers/s390/cio/qdio.c @@ -81,6 +81,7 @@ static __u32 volatile spare_indicator; static atomic_t spare_indicator_usecount; #define QDIO_MEMPOOL_SCSSC_ELEMENTS 2 static mempool_t *qdio_mempool_scssc; +static struct kmem_cache *qdio_q_cache; static debug_info_t *qdio_dbf_setup; static debug_info_t *qdio_dbf_sbal; @@ -1617,23 +1618,21 @@ static void qdio_release_irq_memory(struct qdio_irq *irq_ptr) { int i; + struct qdio_q *q; - for (i=0;iinput_qs[i]) - goto next; - - kfree(irq_ptr->input_qs[i]->slib); - kfree(irq_ptr->input_qs[i]); - -next: - if (!irq_ptr->output_qs[i]) - continue; - - kfree(irq_ptr->output_qs[i]->slib); - kfree(irq_ptr->output_qs[i]); - + for (i = 0; i < QDIO_MAX_QUEUES_PER_IRQ; i++) { + q = irq_ptr->input_qs[i]; + if (q) { + free_page((unsigned long) q->slib); + kmem_cache_free(qdio_q_cache, q); + } + q = irq_ptr->output_qs[i]; + if (q) { + free_page((unsigned long) q->slib); + kmem_cache_free(qdio_q_cache, q); + } } - kfree(irq_ptr->qdr); + free_page((unsigned long) irq_ptr->qdr); free_page((unsigned long) irq_ptr); } @@ -1680,44 +1679,35 @@ qdio_alloc_qs(struct qdio_irq *irq_ptr, { int i; struct qdio_q *q; - int result=-ENOMEM; - - for (i=0;islib = kmalloc(PAGE_SIZE, GFP_KERNEL); + q->slib = (struct slib *) __get_free_page(GFP_KERNEL); if (!q->slib) { - QDIO_PRINT_ERR("kmalloc of slib failed!\n"); - goto out; + kmem_cache_free(qdio_q_cache, q); + return -ENOMEM; } - irq_ptr->input_qs[i]=q; } - for (i=0;islib=kmalloc(PAGE_SIZE,GFP_KERNEL); + q->slib = (struct slib *) __get_free_page(GFP_KERNEL); if (!q->slib) { - QDIO_PRINT_ERR("kmalloc of slib failed!\n"); - goto out; + kmem_cache_free(qdio_q_cache, q); + return -ENOMEM; } - irq_ptr->output_qs[i]=q; } - - result=0; -out: - return result; + return 0; } static void @@ -2985,17 +2975,17 @@ qdio_allocate(struct qdio_initialize *init_data) QDIO_DBF_HEX0(0,setup,&irq_ptr,sizeof(void*)); if (!irq_ptr) { - QDIO_PRINT_ERR("kmalloc of irq_ptr failed!\n"); + QDIO_PRINT_ERR("allocation of irq_ptr failed!\n"); return -ENOMEM; } init_MUTEX(&irq_ptr->setting_up_sema); /* QDR must be in DMA area since CCW data address is only 32 bit */ - irq_ptr->qdr=kmalloc(sizeof(struct qdr), GFP_KERNEL | GFP_DMA); + irq_ptr->qdr = (struct qdr *) __get_free_page(GFP_KERNEL | GFP_DMA); if (!(irq_ptr->qdr)) { free_page((unsigned long) irq_ptr); - QDIO_PRINT_ERR("kmalloc of irq_ptr->qdr failed!\n"); + QDIO_PRINT_ERR("allocation of irq_ptr->qdr failed!\n"); return -ENOMEM; } QDIO_DBF_TEXT0(0,setup,"qdr:"); @@ -3004,6 +2994,7 @@ qdio_allocate(struct qdio_initialize *init_data) if (qdio_alloc_qs(irq_ptr, init_data->no_input_qs, init_data->no_output_qs)) { + QDIO_PRINT_ERR("queue allocation failed!\n"); qdio_release_irq_memory(irq_ptr); return -ENOMEM; } @@ -3895,9 +3886,19 @@ init_QDIO(void) if (res) return res; + qdio_q_cache = kmem_cache_create("qdio_q", sizeof(struct qdio_q), + 256, 0, NULL); + if (!qdio_q_cache) { + qdio_release_qdio_memory(); + return -ENOMEM; + } + res = qdio_register_dbf_views(); - if (res) + if (res) { + kmem_cache_destroy(qdio_q_cache); + qdio_release_qdio_memory(); return res; + } QDIO_DBF_TEXT0(0,setup,"initQDIO"); res = bus_create_file(&ccw_bus_type, &bus_attr_qdio_performance_stats); @@ -3929,6 +3930,7 @@ cleanup_QDIO(void) qdio_release_qdio_memory(); qdio_unregister_dbf_views(); mempool_destroy(qdio_mempool_scssc); + kmem_cache_destroy(qdio_q_cache); bus_remove_file(&ccw_bus_type, &bus_attr_qdio_performance_stats); printk("qdio: %s: module removed\n",version); } -- cgit v1.2.3 From 1eade380c5f3e69348531ade5e9f9c5ae6485874 Mon Sep 17 00:00:00 2001 From: Michael Holzheu Date: Fri, 10 Aug 2007 14:32:30 +0200 Subject: [S390] vmur: allocate single record buffers instead of one big data buffer vmur allocates one contiguous kernel buffer to copy user data when creating ccw programs for punch or printer. If big block sizes are used, under memory pressure it can happen, that we do not get memory in one chunk. Now we allocate memory for each single record to avoid high order allocations. Signed-off-by: Michael Holzheu Signed-off-by: Martin Schwidefsky --- drivers/s390/char/vmur.c | 75 ++++++++++++++++++++++++------------------------ 1 file changed, 37 insertions(+), 38 deletions(-) (limited to 'drivers') diff --git a/drivers/s390/char/vmur.c b/drivers/s390/char/vmur.c index 161867cebd8..1b758b51d7e 100644 --- a/drivers/s390/char/vmur.c +++ b/drivers/s390/char/vmur.c @@ -119,10 +119,12 @@ static void urdev_put(struct urdev *urd) /* * Low-level functions to do I/O to a ur device. * alloc_chan_prog + * free_chan_prog * do_ur_io * ur_int_handler * * alloc_chan_prog allocates and builds the channel program + * free_chan_prog frees memory of the channel program * * do_ur_io issues the channel program to the device and blocks waiting * on a completion event it publishes at urd->io_done. The function @@ -137,6 +139,16 @@ static void urdev_put(struct urdev *urd) * address pointer that alloc_chan_prog returned. */ +static void free_chan_prog(struct ccw1 *cpa) +{ + struct ccw1 *ptr = cpa; + + while (ptr->cda) { + kfree((void *)(addr_t) ptr->cda); + ptr++; + } + kfree(cpa); +} /* * alloc_chan_prog @@ -144,44 +156,45 @@ static void urdev_put(struct urdev *urd) * with a final NOP CCW command-chained on (which ensures that CE and DE * are presented together in a single interrupt instead of as separate * interrupts unless an incorrect length indication kicks in first). The - * data length in each CCW is reclen. The caller must ensure that count - * is an integral multiple of reclen. - * The channel program pointer returned by this function must be freed - * with kfree. The caller is responsible for checking that - * count/reclen is not ridiculously large. + * data length in each CCW is reclen. */ -static struct ccw1 *alloc_chan_prog(char *buf, size_t count, size_t reclen) +static struct ccw1 *alloc_chan_prog(const char __user *ubuf, int rec_count, + int reclen) { - size_t num_ccws; struct ccw1 *cpa; + void *kbuf; int i; - TRACE("alloc_chan_prog(%p, %zu, %zu)\n", buf, count, reclen); + TRACE("alloc_chan_prog(%p, %i, %i)\n", ubuf, rec_count, reclen); /* * We chain a NOP onto the writes to force CE+DE together. * That means we allocate room for CCWs to cover count/reclen * records plus a NOP. */ - num_ccws = count / reclen + 1; - cpa = kmalloc(num_ccws * sizeof(struct ccw1), GFP_KERNEL | GFP_DMA); + cpa = kzalloc((rec_count + 1) * sizeof(struct ccw1), + GFP_KERNEL | GFP_DMA); if (!cpa) - return NULL; + return ERR_PTR(-ENOMEM); - for (i = 0; count; i++) { + for (i = 0; i < rec_count; i++) { cpa[i].cmd_code = WRITE_CCW_CMD; cpa[i].flags = CCW_FLAG_CC | CCW_FLAG_SLI; cpa[i].count = reclen; - cpa[i].cda = __pa(buf); - buf += reclen; - count -= reclen; + kbuf = kmalloc(reclen, GFP_KERNEL | GFP_DMA); + if (!kbuf) { + free_chan_prog(cpa); + return ERR_PTR(-ENOMEM); + } + cpa[i].cda = (u32)(addr_t) kbuf; + if (copy_from_user(kbuf, ubuf, reclen)) { + free_chan_prog(cpa); + return ERR_PTR(-EFAULT); + } + ubuf += reclen; } /* The following NOP CCW forces CE+DE to be presented together */ cpa[i].cmd_code = CCW_CMD_NOOP; - cpa[i].flags = 0; - cpa[i].count = 0; - cpa[i].cda = 0; - return cpa; } @@ -325,24 +338,11 @@ static ssize_t do_write(struct urdev *urd, const char __user *udata, size_t count, size_t reclen, loff_t *ppos) { struct ccw1 *cpa; - char *buf; int rc; - /* Data buffer must be under 2GB line for fmt1 CCWs: hence GFP_DMA */ - buf = kmalloc(count, GFP_KERNEL | GFP_DMA); - if (!buf) - return -ENOMEM; - - if (copy_from_user(buf, udata, count)) { - rc = -EFAULT; - goto fail_kfree_buf; - } - - cpa = alloc_chan_prog(buf, count, reclen); - if (!cpa) { - rc = -ENOMEM; - goto fail_kfree_buf; - } + cpa = alloc_chan_prog(udata, count / reclen, reclen); + if (IS_ERR(cpa)) + return PTR_ERR(cpa); rc = do_ur_io(urd, cpa); if (rc) @@ -354,10 +354,9 @@ static ssize_t do_write(struct urdev *urd, const char __user *udata, } *ppos += count; rc = count; + fail_kfree_cpa: - kfree(cpa); -fail_kfree_buf: - kfree(buf); + free_chan_prog(cpa); return rc; } -- cgit v1.2.3 From 278bc68c4bfcd1af97972f5c4458acf3b9b19c37 Mon Sep 17 00:00:00 2001 From: Heiko Carstens Date: Fri, 10 Aug 2007 14:32:31 +0200 Subject: [S390] vmur: use DECLARE_COMPLETION_ONSTACK to keep lockdep happy INFO: trying to register non-static key. the code is fine but needs lockdep annotation. turning off the locking correctness validator. 000000000ff9fb08 000000000ff9fb18 0000000000000002 0000000000000000 000000000ff9fbb8 000000000ff9fb30 000000000ff9fb30 0000000000104198 0000000000000000 0000000000000002 0000000000000000 0000000000000000 000000000ff9fb18 000000000000000c 000000000ff9fb18 000000000ff9fb88 0000000000448db0 0000000000104198 000000000ff9fb18 000000000ff9fb68 Call Trace: ([<00000000001040ea>] show_trace+0x12e/0x170) [<00000000001041f2>] show_stack+0xc6/0xf8 [<0000000000104252>] dump_stack+0x2e/0x3c [<0000000000155f9c>] __lock_acquire+0x460/0x1048 [<0000000000156c16>] lock_acquire+0x92/0xb8 [<000000000043f406>] _spin_lock_irqsave+0x62/0x80 [<0000000000121382>] complete+0x32/0x78 [<000000001082b468>] ur_int_handler+0xc8/0xec [vmur] [<0000000000313216>] ccw_device_call_handler+0xae/0xd4 [<0000000000310da4>] ccw_device_irq+0x5c/0x130 [<0000000000312c84>] io_subchannel_irq+0x8c/0x118 [<000000000030a88c>] do_IRQ+0x16c/0x194 [<0000000000111a62>] io_no_vtime+0x16/0x1c [<0000000080001394>] 0x80001394 Signed-off-by: Heiko Carstens Signed-off-by: Martin Schwidefsky --- drivers/s390/char/vmur.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/s390/char/vmur.c b/drivers/s390/char/vmur.c index 1b758b51d7e..27b8bf92741 100644 --- a/drivers/s390/char/vmur.c +++ b/drivers/s390/char/vmur.c @@ -202,7 +202,7 @@ static int do_ur_io(struct urdev *urd, struct ccw1 *cpa) { int rc; struct ccw_device *cdev = urd->cdev; - DECLARE_COMPLETION(event); + DECLARE_COMPLETION_ONSTACK(event); TRACE("do_ur_io: cpa=%p\n", cpa); -- cgit v1.2.3 From f2405598e0678e9c93dd780f2a12fc562ece3d13 Mon Sep 17 00:00:00 2001 From: Michael Holzheu Date: Fri, 10 Aug 2007 14:32:32 +0200 Subject: [S390] vmur: reject open on z/VM reader files with status HOLD If a reader file with HOLD status is at the top of the reader queue, currently all read requests will return data of the second file in the queue. But the semantics of vmur is that always the topmost file is read. With this fix -EPERM is returned on open, if the topmost reader file is in HOLD status. Signed-off-by: Michael Holzheu Signed-off-by: Martin Schwidefsky --- drivers/s390/char/vmur.c | 4 +++- drivers/s390/char/vmur.h | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/s390/char/vmur.c b/drivers/s390/char/vmur.c index 27b8bf92741..04395c0f99d 100644 --- a/drivers/s390/char/vmur.c +++ b/drivers/s390/char/vmur.c @@ -556,7 +556,9 @@ static int verify_device(struct urdev *urd) rc = diag_read_next_file_info(&fcb, 0); if (rc) return rc; - + /* if file is in hold status, we do not read it */ + if (fcb.file_stat & (FLG_SYSTEM_HOLD | FLG_USER_HOLD)) + return -EPERM; /* open file on virtual reader */ buf = kmalloc(PAGE_SIZE, GFP_KERNEL); if (!buf) diff --git a/drivers/s390/char/vmur.h b/drivers/s390/char/vmur.h index 16d0a4e38e4..522a9dfaa27 100644 --- a/drivers/s390/char/vmur.h +++ b/drivers/s390/char/vmur.h @@ -50,7 +50,9 @@ struct file_control_block { char rest[200]; } __attribute__ ((packed)); -#define FLG_CP_DUMP 0x10 +#define FLG_SYSTEM_HOLD 0x04 +#define FLG_CP_DUMP 0x10 +#define FLG_USER_HOLD 0x20 /* * A struct urdev is created for each ur device that is made available -- cgit v1.2.3 From 4eac34529bce2b4cca9be90a6903c965baa8193c Mon Sep 17 00:00:00 2001 From: Michael Holzheu Date: Fri, 10 Aug 2007 14:32:33 +0200 Subject: [S390] vmur: add "top of queue" sanity check for reader open If the z/VM reader is already open, it can happen that after opening the Linux reader device, not the topmost file is processed. According the semantics of the Linux z/VM unit record device driver, always the topmost file has to be processed. With this fix an error is returned if that is not the case. Signed-off-by: Michael Holzheu Signed-off-by: Martin Schwidefsky --- drivers/s390/char/vmur.c | 7 ++++++- drivers/s390/char/vmur.h | 1 + 2 files changed, 7 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/s390/char/vmur.c b/drivers/s390/char/vmur.c index 04395c0f99d..a9d58629e79 100644 --- a/drivers/s390/char/vmur.c +++ b/drivers/s390/char/vmur.c @@ -565,9 +565,14 @@ static int verify_device(struct urdev *urd) return -ENOMEM; rc = diag_read_file(urd->dev_id.devno, buf); kfree(buf); - if ((rc != 0) && (rc != -ENODATA)) /* EOF does not hurt */ return rc; + /* check if the file on top of the queue is open now */ + rc = diag_read_next_file_info(&fcb, 0); + if (rc) + return rc; + if (!(fcb.file_stat & FLG_IN_USE)) + return -EMFILE; return 0; default: return -ENOTSUPP; diff --git a/drivers/s390/char/vmur.h b/drivers/s390/char/vmur.h index 522a9dfaa27..2b3c564e047 100644 --- a/drivers/s390/char/vmur.h +++ b/drivers/s390/char/vmur.h @@ -53,6 +53,7 @@ struct file_control_block { #define FLG_SYSTEM_HOLD 0x04 #define FLG_CP_DUMP 0x10 #define FLG_USER_HOLD 0x20 +#define FLG_IN_USE 0x80 /* * A struct urdev is created for each ur device that is made available -- cgit v1.2.3 From 3eed13cc3beaa9ee07b126a662def88f7281394e Mon Sep 17 00:00:00 2001 From: Michael Holzheu Date: Fri, 10 Aug 2007 14:32:34 +0200 Subject: [S390] vmur: diag14 only works with buffers below 2GB If memory buffers above 2GB are used, diagnose 14 raises a specification exception. This fix ensures that buffer allocation is done below the 2GB boundary. Signed-off-by: Michael Holzheu Signed-off-by: Martin Schwidefsky --- drivers/s390/char/vmur.c | 106 +++++++++++++++++++++++++++++++---------------- 1 file changed, 70 insertions(+), 36 deletions(-) (limited to 'drivers') diff --git a/drivers/s390/char/vmur.c b/drivers/s390/char/vmur.c index a9d58629e79..04b19bdc09d 100644 --- a/drivers/s390/char/vmur.c +++ b/drivers/s390/char/vmur.c @@ -472,7 +472,7 @@ static ssize_t diag14_read(struct file *file, char __user *ubuf, size_t count, return rc; len = min((size_t) PAGE_SIZE, count); - buf = kmalloc(PAGE_SIZE, GFP_KERNEL); + buf = (char *) __get_free_page(GFP_KERNEL | GFP_DMA); if (!buf) return -ENOMEM; @@ -499,7 +499,7 @@ static ssize_t diag14_read(struct file *file, char __user *ubuf, size_t count, *offs += copied; rc = copied; fail: - kfree(buf); + free_page((unsigned long) buf); return rc; } @@ -542,63 +542,97 @@ static int diag_read_next_file_info(struct file_control_block *buf, int spid) } } -static int verify_device(struct urdev *urd) +static int verify_uri_device(struct urdev *urd) { - struct file_control_block fcb; + struct file_control_block *fcb; char *buf; int rc; + fcb = kmalloc(sizeof(*fcb), GFP_KERNEL | GFP_DMA); + if (!fcb) + return -ENOMEM; + + /* check for empty reader device (beginning of chain) */ + rc = diag_read_next_file_info(fcb, 0); + if (rc) + goto fail_free_fcb; + + /* if file is in hold status, we do not read it */ + if (fcb->file_stat & (FLG_SYSTEM_HOLD | FLG_USER_HOLD)) { + rc = -EPERM; + goto fail_free_fcb; + } + + /* open file on virtual reader */ + buf = (char *) __get_free_page(GFP_KERNEL | GFP_DMA); + if (!buf) { + rc = -ENOMEM; + goto fail_free_fcb; + } + rc = diag_read_file(urd->dev_id.devno, buf); + if ((rc != 0) && (rc != -ENODATA)) /* EOF does not hurt */ + goto fail_free_buf; + + /* check if the file on top of the queue is open now */ + rc = diag_read_next_file_info(fcb, 0); + if (rc) + goto fail_free_buf; + if (!(fcb->file_stat & FLG_IN_USE)) { + rc = -EMFILE; + goto fail_free_buf; + } + rc = 0; + +fail_free_buf: + free_page((unsigned long) buf); +fail_free_fcb: + kfree(fcb); + return rc; +} + +static int verify_device(struct urdev *urd) +{ switch (urd->class) { case DEV_CLASS_UR_O: return 0; /* no check needed here */ case DEV_CLASS_UR_I: - /* check for empty reader device (beginning of chain) */ - rc = diag_read_next_file_info(&fcb, 0); - if (rc) - return rc; - /* if file is in hold status, we do not read it */ - if (fcb.file_stat & (FLG_SYSTEM_HOLD | FLG_USER_HOLD)) - return -EPERM; - /* open file on virtual reader */ - buf = kmalloc(PAGE_SIZE, GFP_KERNEL); - if (!buf) - return -ENOMEM; - rc = diag_read_file(urd->dev_id.devno, buf); - kfree(buf); - if ((rc != 0) && (rc != -ENODATA)) /* EOF does not hurt */ - return rc; - /* check if the file on top of the queue is open now */ - rc = diag_read_next_file_info(&fcb, 0); - if (rc) - return rc; - if (!(fcb.file_stat & FLG_IN_USE)) - return -EMFILE; - return 0; + return verify_uri_device(urd); default: return -ENOTSUPP; } } -static int get_file_reclen(struct urdev *urd) +static int get_uri_file_reclen(struct urdev *urd) { - struct file_control_block fcb; + struct file_control_block *fcb; int rc; + fcb = kmalloc(sizeof(*fcb), GFP_KERNEL | GFP_DMA); + if (!fcb) + return -ENOMEM; + rc = diag_read_next_file_info(fcb, 0); + if (rc) + goto fail_free; + if (fcb->file_stat & FLG_CP_DUMP) + rc = 0; + else + rc = fcb->rec_len; + +fail_free: + kfree(fcb); + return rc; +} + +static int get_file_reclen(struct urdev *urd) +{ switch (urd->class) { case DEV_CLASS_UR_O: return 0; case DEV_CLASS_UR_I: - rc = diag_read_next_file_info(&fcb, 0); - if (rc) - return rc; - break; + return get_uri_file_reclen(urd); default: return -ENOTSUPP; } - if (fcb.file_stat & FLG_CP_DUMP) - return 0; - - return fcb.rec_len; } static int ur_open(struct inode *inode, struct file *file) -- cgit v1.2.3 From cbea66d9788a344e16e161f22a6e0c4deef2c0ed Mon Sep 17 00:00:00 2001 From: Melissa Howland Date: Fri, 10 Aug 2007 14:32:35 +0200 Subject: [S390] monwriter: Serialization bug for multithreaded applications. Locking added so that multithreaded applications can now do writes from different threads without the risk of storage corruption. Signed-off-by: Melissa Howland Signed-off-by: Martin Schwidefsky --- drivers/s390/char/monwriter.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'drivers') diff --git a/drivers/s390/char/monwriter.c b/drivers/s390/char/monwriter.c index 268598ef3ef..20442fbf934 100644 --- a/drivers/s390/char/monwriter.c +++ b/drivers/s390/char/monwriter.c @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -41,6 +42,7 @@ struct mon_private { size_t hdr_to_read; size_t data_to_read; struct mon_buf *current_buf; + struct mutex thread_mutex; }; /* @@ -179,6 +181,7 @@ static int monwrite_open(struct inode *inode, struct file *filp) return -ENOMEM; INIT_LIST_HEAD(&monpriv->list); monpriv->hdr_to_read = sizeof(monpriv->hdr); + mutex_init(&monpriv->thread_mutex); filp->private_data = monpriv; return nonseekable_open(inode, filp); } @@ -209,6 +212,7 @@ static ssize_t monwrite_write(struct file *filp, const char __user *data, void *to; int rc; + mutex_lock(&monpriv->thread_mutex); for (written = 0; written < count; ) { if (monpriv->hdr_to_read) { len = min(count - written, monpriv->hdr_to_read); @@ -247,11 +251,13 @@ static ssize_t monwrite_write(struct file *filp, const char __user *data, } monpriv->hdr_to_read = sizeof(monpriv->hdr); } + mutex_unlock(&monpriv->thread_mutex); return written; out_error: monpriv->data_to_read = 0; monpriv->hdr_to_read = sizeof(struct monwrite_hdr); + mutex_unlock(&monpriv->thread_mutex); return rc; } -- cgit v1.2.3