Whamcloud - gitweb
LU-7623 Update obd iocontrol methods with __user attribute
[fs/lustre-release.git] / lustre / obdecho / echo_client.c
index 944dddd..e435f5d 100644 (file)
@@ -27,7 +27,7 @@
  * Copyright (c) 2002, 2010, Oracle and/or its affiliates. All rights reserved.
  * Use is subject to license terms.
  *
- * Copyright (c) 2011, 2014, Intel Corporation.
+ * Copyright (c) 2011, 2015, Intel Corporation.
  */
 /*
  * This file is part of Lustre, http://www.lustre.org/
@@ -67,7 +67,7 @@ struct echo_device {
        struct echo_client_obd   *ed_ec;
 
        struct cl_site            ed_site_myself;
-       struct cl_site           *ed_site;
+       struct lu_site           *ed_site;
        struct lu_device         *ed_next;
        int                       ed_next_ismd;
        struct lu_client_seq     *ed_cl_seq;
@@ -230,7 +230,7 @@ struct echo_thread_info {
        struct md_attr          eti_ma;
        struct lu_name          eti_lname;
        /* per-thread values, can be re-used */
-       void                    *eti_big_lmm;
+       void                    *eti_big_lmm; /* may be vmalloc'd */
        int                     eti_big_lmmsize;
        char                    eti_name[20];
        struct lu_buf           eti_buf;
@@ -584,9 +584,6 @@ static struct lu_device_operations echo_device_lu_ops = {
 
 /** @} echo_lu_dev_ops */
 
-static struct cl_device_operations echo_device_cl_ops = {
-};
-
 /** \defgroup echo_init Setup and teardown
  *
  * Init and fini functions for echo client.
@@ -598,28 +595,30 @@ static int echo_site_init(const struct lu_env *env, struct echo_device *ed)
         struct cl_site *site = &ed->ed_site_myself;
         int rc;
 
-        /* initialize site */
+       /* initialize site */
         rc = cl_site_init(site, &ed->ed_cl);
         if (rc) {
-                CERROR("Cannot initilize site for echo client(%d)\n", rc);
+               CERROR("Cannot initialize site for echo client(%d)\n", rc);
                 return rc;
         }
 
-        rc = lu_site_init_finish(&site->cs_lu);
-        if (rc)
-                return rc;
+       rc = lu_site_init_finish(&site->cs_lu);
+       if (rc) {
+               cl_site_fini(site);
+               return rc;
+       }
 
-        ed->ed_site = site;
-        return 0;
+       ed->ed_site = &site->cs_lu;
+       return 0;
 }
 
 static void echo_site_fini(const struct lu_env *env, struct echo_device *ed)
 {
-        if (ed->ed_site) {
-                if (!ed->ed_next_ismd)
-                        cl_site_fini(ed->ed_site);
-                ed->ed_site = NULL;
-        }
+       if (ed->ed_site) {
+               if (!ed->ed_next_ismd)
+                       lu_site_fini(ed->ed_site);
+               ed->ed_site = NULL;
+       }
 }
 
 static void *echo_thread_key_init(const struct lu_context *ctx,
@@ -841,7 +840,6 @@ static struct lu_device *echo_device_alloc(const struct lu_env *env,
                 GOTO(out, rc);
 
         cd->cd_lu_dev.ld_ops = &echo_device_lu_ops;
-        cd->cd_ops = &echo_device_cl_ops;
 
         cleanup = 2;
         obd = class_name2obd(lustre_cfg_string(cfg, 0));
@@ -918,11 +916,10 @@ static struct lu_device *echo_device_alloc(const struct lu_env *env,
                         GOTO(out, rc = -EINVAL);
                 }
 
-                next = ld;
-                /* For MD echo client, it will use the site in MDS stack */
-                ed->ed_site_myself.cs_lu = *ls;
-                ed->ed_site = &ed->ed_site_myself;
-                ed->ed_cl.cd_lu_dev.ld_site = &ed->ed_site_myself.cs_lu;
+               next = ld;
+               /* For MD echo client, it will use the site in MDS stack */
+               ed->ed_site = ls;
+               ed->ed_cl.cd_lu_dev.ld_site = ls;
                rc = echo_fid_init(ed, obd->obd_name, lu_site2seq(ls));
                if (rc) {
                        CERROR("echo fid init error %d\n", rc);
@@ -955,7 +952,7 @@ static struct lu_device *echo_device_alloc(const struct lu_env *env,
                         if (next->ld_site != NULL)
                                 GOTO(out, rc = -EBUSY);
 
-                        next->ld_site = &ed->ed_site->cs_lu;
+                        next->ld_site = ed->ed_site;
                         rc = next->ld_type->ldt_ops->ldto_device_init(env, next,
                                                      next->ld_type->ldt_name,
                                                      NULL);
@@ -1028,7 +1025,7 @@ static struct lu_device *echo_device_free(const struct lu_env *env,
         CDEBUG(D_INFO, "echo device:%p is going to be freed, next = %p\n",
                ed, next);
 
-        lu_site_purge(env, &ed->ed_site->cs_lu, -1);
+       lu_site_purge(env, ed->ed_site, -1);
 
         /* check if there are objects still alive.
          * It shouldn't have any object because lu_site_purge would cleanup
@@ -1041,7 +1038,7 @@ static struct lu_device *echo_device_free(const struct lu_env *env,
        spin_unlock(&ec->ec_lock);
 
        /* purge again */
-       lu_site_purge(env, &ed->ed_site->cs_lu, -1);
+       lu_site_purge(env, ed->ed_site, -1);
 
        CDEBUG(D_INFO,
               "Waiting for the reference of echo object to be dropped\n");
@@ -1054,7 +1051,7 @@ static struct lu_device *echo_device_free(const struct lu_env *env,
                       "wait for 1 second\n");
                set_current_state(TASK_UNINTERRUPTIBLE);
                schedule_timeout(cfs_time_seconds(1));
-               lu_site_purge(env, &ed->ed_site->cs_lu, -1);
+               lu_site_purge(env, ed->ed_site, -1);
                spin_lock(&ec->ec_lock);
        }
        spin_unlock(&ec->ec_lock);
@@ -1071,7 +1068,7 @@ static struct lu_device *echo_device_free(const struct lu_env *env,
        while (next && !ed->ed_next_ismd)
                next = next->ld_type->ldt_ops->ldto_device_free(env, next);
 
-        LASSERT(ed->ed_site == lu2cl_site(d->ld_site));
+        LASSERT(ed->ed_site == d->ld_site);
         echo_site_fini(env, ed);
         cl_device_fini(&ed->ed_cl);
         OBD_FREE_PTR(ed);
@@ -1118,7 +1115,7 @@ cl_echo_object_find(struct echo_device *d, const struct ost_id *oi)
        struct cl_object *obj;
        struct lov_oinfo *oinfo = NULL;
        struct lu_fid *fid;
-       int refcheck;
+       __u16  refcheck;
        int rc;
        ENTRY;
 
@@ -1178,7 +1175,7 @@ static int cl_echo_object_put(struct echo_object *eco)
 {
         struct lu_env *env;
         struct cl_object *obj = echo_obj2cl(eco);
-        int refcheck;
+       __u16  refcheck;
         ENTRY;
 
         env = cl_env_get(&refcheck);
@@ -1298,9 +1295,9 @@ static int cl_echo_object_brw(struct echo_object *eco, int rw, u64 offset,
         struct cl_page          *clp;
         struct lustre_handle    lh = { 0 };
         int page_size = cl_page_size(obj);
-        int refcheck;
         int rc;
         int i;
+       __u16 refcheck;
         ENTRY;
 
        LASSERT((offset & ~PAGE_MASK) == 0);
@@ -2063,7 +2060,7 @@ static int echo_md_handler(struct echo_device *ed, int command,
        struct echo_thread_info *info;
         struct lu_device      *ld = ed->ed_next;
         struct lu_env         *env;
-        int                    refcheck;
+       __u16                  refcheck;
         struct lu_object      *parent;
         char                  *name = NULL;
         int                    namelen = data->ioc_plen2;
@@ -2175,7 +2172,7 @@ out_env:
 #endif /* HAVE_SERVER_SUPPORT */
 
 static int echo_create_object(const struct lu_env *env, struct echo_device *ed,
-                             struct obdo *oa, struct obd_trans_info *oti)
+                             struct obdo *oa)
 {
        struct echo_object      *eco;
        struct echo_client_obd  *ec = ed->ed_ec;
@@ -2193,7 +2190,7 @@ static int echo_create_object(const struct lu_env *env, struct echo_device *ed,
        if (ostid_id(&oa->o_oi) == 0)
                ostid_set_id(&oa->o_oi, ++last_object_id);
 
-       rc = obd_create(env, ec->ec_exp, oa, oti);
+       rc = obd_create(env, ec->ec_exp, oa);
        if (rc != 0) {
                CERROR("Cannot create objects: rc = %d\n", rc);
                GOTO(failed, rc);
@@ -2213,7 +2210,7 @@ static int echo_create_object(const struct lu_env *env, struct echo_device *ed,
 
 failed:
        if (created && rc != 0)
-               obd_destroy(env, ec->ec_exp, oa, oti);
+               obd_destroy(env, ec->ec_exp, oa);
 
        if (rc != 0)
                CERROR("create object failed with: rc = %d\n", rc);
@@ -2317,8 +2314,7 @@ echo_client_page_debug_check(struct page *page, u64 id, u64 offset, u64 count)
 
 static int echo_client_kbrw(struct echo_device *ed, int rw, struct obdo *oa,
                            struct echo_object *eco, u64 offset,
-                           u64 count, int async,
-                           struct obd_trans_info *oti)
+                           u64 count, int async)
 {
        size_t                  npages;
         struct brw_page        *pga;
@@ -2359,27 +2355,27 @@ static int echo_client_kbrw(struct echo_device *ed, int rw, struct obdo *oa,
                 RETURN(-ENOMEM);
         }
 
-        for (i = 0, pgp = pga, off = offset;
-             i < npages;
+       for (i = 0, pgp = pga, off = offset;
+            i < npages;
             i++, pgp++, off += PAGE_CACHE_SIZE) {
 
-                LASSERT (pgp->pg == NULL);      /* for cleanup */
+               LASSERT(pgp->pg == NULL);       /* for cleanup */
 
-                rc = -ENOMEM;
-                OBD_PAGE_ALLOC(pgp->pg, gfp_mask);
-                if (pgp->pg == NULL)
-                        goto out;
+               rc = -ENOMEM;
+               pgp->pg = alloc_page(gfp_mask);
+               if (pgp->pg == NULL)
+                       goto out;
 
-                pages[i] = pgp->pg;
+               pages[i] = pgp->pg;
                pgp->count = PAGE_CACHE_SIZE;
-                pgp->off = off;
-                pgp->flag = brw_flags;
+               pgp->off = off;
+               pgp->flag = brw_flags;
 
                if (verify)
                        echo_client_page_debug_setup(pgp->pg, rw,
                                                     ostid_id(&oa->o_oi), off,
                                                     pgp->count);
-        }
+       }
 
         /* brw mode can only be used at client */
         LASSERT(ed->ed_next != NULL);
@@ -2401,7 +2397,7 @@ static int echo_client_kbrw(struct echo_device *ed, int rw, struct obdo *oa,
                        if (vrc != 0 && rc == 0)
                                rc = vrc;
                }
-               OBD_PAGE_FREE(pgp->pg);
+               __free_page(pgp->pg);
         }
         OBD_FREE(pga, npages * sizeof(*pga));
         OBD_FREE(pages, npages * sizeof(*pages));
@@ -2412,12 +2408,11 @@ static int echo_client_prep_commit(const struct lu_env *env,
                                   struct obd_export *exp, int rw,
                                   struct obdo *oa, struct echo_object *eco,
                                   u64 offset, u64 count,
-                                  u64 batch, struct obd_trans_info *oti,
-                                  int async)
+                                  u64 batch, int async)
 {
        struct obd_ioobj         ioo;
        struct niobuf_local     *lnb;
-       struct niobuf_remote    *rnb;
+       struct niobuf_remote     rnb;
        u64                      off;
        u64                      npages, tot_pages;
        int i, ret = 0, brw_flags = 0;
@@ -2430,41 +2425,35 @@ static int echo_client_prep_commit(const struct lu_env *env,
        npages = batch >> PAGE_CACHE_SHIFT;
        tot_pages = count >> PAGE_CACHE_SHIFT;
 
-        OBD_ALLOC(lnb, npages * sizeof(struct niobuf_local));
-        OBD_ALLOC(rnb, npages * sizeof(struct niobuf_remote));
-
-        if (lnb == NULL || rnb == NULL)
-                GOTO(out, ret = -ENOMEM);
+       OBD_ALLOC(lnb, npages * sizeof(struct niobuf_local));
+       if (lnb == NULL)
+               GOTO(out, ret = -ENOMEM);
 
        if (rw == OBD_BRW_WRITE && async)
                brw_flags |= OBD_BRW_ASYNC;
 
-        obdo_to_ioobj(oa, &ioo);
+       obdo_to_ioobj(oa, &ioo);
 
-        off = offset;
+       off = offset;
 
-        for(; tot_pages; tot_pages -= npages) {
-                int lpages;
+       for (; tot_pages > 0; tot_pages -= npages) {
+               int lpages;
 
-                if (tot_pages < npages)
-                        npages = tot_pages;
-
-               for (i = 0; i < npages; i++, off += PAGE_CACHE_SIZE) {
-                       rnb[i].rnb_offset = off;
-                       rnb[i].rnb_len = PAGE_CACHE_SIZE;
-                       rnb[i].rnb_flags = brw_flags;
-                }
+               if (tot_pages < npages)
+                       npages = tot_pages;
 
-                ioo.ioo_bufcnt = npages;
+               rnb.rnb_offset = off;
+               rnb.rnb_len = npages * PAGE_CACHE_SIZE;
+               rnb.rnb_flags = brw_flags;
+               ioo.ioo_bufcnt = 1;
+               off += npages * PAGE_CACHE_SIZE;
 
                 lpages = npages;
-               ret = obd_preprw(env, rw, exp, oa, 1, &ioo, rnb, &lpages,
-                                lnb, oti);
+               ret = obd_preprw(env, rw, exp, oa, 1, &ioo, &rnb, &lpages, lnb);
                 if (ret != 0)
                         GOTO(out, ret);
-                LASSERT(lpages == npages);
 
-                for (i = 0; i < lpages; i++) {
+               for (i = 0; i < lpages; i++) {
                        struct page *page = lnb[i].lnb_page;
 
                        /* read past eof? */
@@ -2481,41 +2470,35 @@ static int echo_client_prep_commit(const struct lu_env *env,
 
                        if (rw == OBD_BRW_WRITE)
                                echo_client_page_debug_setup(page, rw,
-                                                           ostid_id(&oa->o_oi),
-                                                            rnb[i].rnb_offset,
-                                                            rnb[i].rnb_len);
+                                                       ostid_id(&oa->o_oi),
+                                                       lnb[i].lnb_file_offset,
+                                                       lnb[i].lnb_len);
                        else
                                echo_client_page_debug_check(page,
-                                                           ostid_id(&oa->o_oi),
-                                                            rnb[i].rnb_offset,
-                                                            rnb[i].rnb_len);
+                                                       ostid_id(&oa->o_oi),
+                                                       lnb[i].lnb_file_offset,
+                                                       lnb[i].lnb_len);
                }
 
-               ret = obd_commitrw(env, rw, exp, oa, 1, &ioo,
-                                  rnb, npages, lnb, oti, ret);
+               ret = obd_commitrw(env, rw, exp, oa, 1, &ioo, &rnb, npages, lnb,
+                                  ret);
                 if (ret != 0)
                         GOTO(out, ret);
 
-                /* Reset oti otherwise it would confuse ldiskfs. */
-                memset(oti, 0, sizeof(*oti));
-
                /* Reuse env context. */
                lu_context_exit((struct lu_context *)&env->le_ctx);
                lu_context_enter((struct lu_context *)&env->le_ctx);
-        }
+       }
 
 out:
-        if (lnb)
-                OBD_FREE(lnb, npages * sizeof(struct niobuf_local));
-        if (rnb)
-                OBD_FREE(rnb, npages * sizeof(struct niobuf_remote));
-        RETURN(ret);
+       if (lnb)
+               OBD_FREE(lnb, npages * sizeof(struct niobuf_local));
+       RETURN(ret);
 }
 
 static int echo_client_brw_ioctl(const struct lu_env *env, int rw,
                                 struct obd_export *exp,
-                                struct obd_ioctl_data *data,
-                                struct obd_trans_info *dummy_oti)
+                                struct obd_ioctl_data *data)
 {
         struct obd_device *obd = class_exp2obd(exp);
         struct echo_device *ed = obd2echo_dev(obd);
@@ -2553,26 +2536,26 @@ static int echo_client_brw_ioctl(const struct lu_env *env, int rw,
         case 1:
                 /* fall through */
         case 2:
-                rc = echo_client_kbrw(ed, rw, oa,
-                                      eco, data->ioc_offset,
-                                     data->ioc_count, async, dummy_oti);
-                break;
-        case 3:
-               rc = echo_client_prep_commit(env, ec->ec_exp, rw, oa,
-                                            eco, data->ioc_offset,
-                                            data->ioc_count, data->ioc_plen1,
-                                            dummy_oti, async);
-                break;
-        default:
-                rc = -EINVAL;
-        }
-        echo_put_object(eco);
-        RETURN(rc);
+               rc = echo_client_kbrw(ed, rw, oa, eco, data->ioc_offset,
+                                     data->ioc_count, async);
+               break;
+       case 3:
+               rc = echo_client_prep_commit(env, ec->ec_exp, rw, oa, eco,
+                                            data->ioc_offset, data->ioc_count,
+                                            data->ioc_plen1, async);
+               break;
+       default:
+               rc = -EINVAL;
+       }
+
+       echo_put_object(eco);
+
+       RETURN(rc);
 }
 
 static int
 echo_client_iocontrol(unsigned int cmd, struct obd_export *exp, int len,
-                      void *karg, void *uarg)
+                     void *karg, void __user *uarg)
 {
 #ifdef HAVE_SERVER_SUPPORT
        struct tgt_session_info *tsi;
@@ -2582,21 +2565,16 @@ echo_client_iocontrol(unsigned int cmd, struct obd_export *exp, int len,
         struct echo_client_obd *ec = ed->ed_ec;
         struct echo_object     *eco;
         struct obd_ioctl_data  *data = karg;
-        struct obd_trans_info   dummy_oti;
         struct lu_env          *env;
-        struct oti_req_ack_lock *ack_lock;
         struct obdo            *oa;
         struct lu_fid           fid;
         int                     rw = OBD_BRW_READ;
         int                     rc = 0;
-        int                     i;
 #ifdef HAVE_SERVER_SUPPORT
        struct lu_context        echo_session;
 #endif
         ENTRY;
 
-        memset(&dummy_oti, 0, sizeof(dummy_oti));
-
        oa = &data->ioc_obdo1;
        if (!(oa->o_valid & OBD_MD_FLGROUP)) {
                oa->o_valid |= OBD_MD_FLGROUP;
@@ -2632,7 +2610,7 @@ echo_client_iocontrol(unsigned int cmd, struct obd_export *exp, int len,
                 if (!cfs_capable(CFS_CAP_SYS_ADMIN))
                         GOTO (out, rc = -EPERM);
 
-               rc = echo_create_object(env, ed, oa, &dummy_oti);
+               rc = echo_create_object(env, ed, oa);
                 GOTO(out, rc);
 
 #ifdef HAVE_SERVER_SUPPORT
@@ -2666,7 +2644,7 @@ echo_client_iocontrol(unsigned int cmd, struct obd_export *exp, int len,
        }
         case OBD_IOC_ECHO_ALLOC_SEQ: {
                 struct lu_env   *cl_env;
-                int              refcheck;
+               __u16            refcheck;
                 __u64            seq;
                 int              max_count;
 
@@ -2708,7 +2686,7 @@ echo_client_iocontrol(unsigned int cmd, struct obd_export *exp, int len,
 
                 rc = echo_get_object(&eco, ed, oa);
                 if (rc == 0) {
-                       rc = obd_destroy(env, ec->ec_exp, oa, &dummy_oti);
+                       rc = obd_destroy(env, ec->ec_exp, oa);
                         if (rc == 0)
                                 eco->eo_deleted = 1;
                         echo_put_object(eco);
@@ -2718,11 +2696,7 @@ echo_client_iocontrol(unsigned int cmd, struct obd_export *exp, int len,
         case OBD_IOC_GETATTR:
                 rc = echo_get_object(&eco, ed, oa);
                 if (rc == 0) {
-                       struct obd_info oinfo = {
-                               .oi_oa = oa,
-                       };
-
-                        rc = obd_getattr(env, ec->ec_exp, &oinfo);
+                       rc = obd_getattr(env, ec->ec_exp, oa);
                         echo_put_object(eco);
                 }
                 GOTO(out, rc);
@@ -2733,11 +2707,7 @@ echo_client_iocontrol(unsigned int cmd, struct obd_export *exp, int len,
 
                 rc = echo_get_object(&eco, ed, oa);
                 if (rc == 0) {
-                       struct obd_info oinfo = {
-                               .oi_oa = oa,
-                       };
-
-                        rc = obd_setattr(env, ec->ec_exp, &oinfo, NULL);
+                       rc = obd_setattr(env, ec->ec_exp, oa);
                         echo_put_object(eco);
                 }
                 GOTO(out, rc);
@@ -2749,7 +2719,7 @@ echo_client_iocontrol(unsigned int cmd, struct obd_export *exp, int len,
                 rw = OBD_BRW_WRITE;
                 /* fall through */
         case OBD_IOC_BRW_READ:
-               rc = echo_client_brw_ioctl(env, rw, exp, data, &dummy_oti);
+               rc = echo_client_brw_ioctl(env, rw, exp, data);
                 GOTO(out, rc);
 
         default:
@@ -2768,14 +2738,6 @@ out_env:
 out_alloc:
         OBD_FREE_PTR(env);
 
-        /* XXX this should be in a helper also called by target_send_reply */
-        for (ack_lock = dummy_oti.oti_ack_locks, i = 0; i < 4;
-             i++, ack_lock++) {
-                if (!ack_lock->mode)
-                        break;
-                ldlm_lock_decref(&ack_lock->lock, ack_lock->mode);
-        }
-
         return rc;
 }
 
@@ -2926,27 +2888,6 @@ static struct obd_ops echo_client_obd_ops = {
         .o_disconnect  = echo_client_disconnect
 };
 
-static int echo_client_init(void)
-{
-        int rc;
-
-       rc = lu_kmem_init(echo_caches);
-       if (rc == 0) {
-               rc = class_register_type(&echo_client_obd_ops, NULL, true, NULL,
-                                        LUSTRE_ECHO_CLIENT_NAME,
-                                        &echo_device_type);
-               if (rc)
-                       lu_kmem_fini(echo_caches);
-       }
-       return rc;
-}
-
-static void echo_client_exit(void)
-{
-        class_unregister_type(LUSTRE_ECHO_CLIENT_NAME);
-        lu_kmem_fini(echo_caches);
-}
-
 static int __init obdecho_init(void)
 {
         int rc;
@@ -2967,7 +2908,14 @@ static int __init obdecho_init(void)
                goto failed_1;
 # endif
 
-        rc = echo_client_init();
+       rc = lu_kmem_init(echo_caches);
+       if (rc == 0) {
+               rc = class_register_type(&echo_client_obd_ops, NULL, true, NULL,
+                                        LUSTRE_ECHO_CLIENT_NAME,
+                                        &echo_device_type);
+               if (rc)
+                       lu_kmem_fini(echo_caches);
+       }
 
 # ifdef HAVE_SERVER_SUPPORT
         if (rc == 0)
@@ -2981,18 +2929,19 @@ failed_0:
         RETURN(rc);
 }
 
-static void /*__exit*/ obdecho_exit(void)
+static void __exit obdecho_exit(void)
 {
-        echo_client_exit();
+       class_unregister_type(LUSTRE_ECHO_CLIENT_NAME);
+       lu_kmem_fini(echo_caches);
 
-# ifdef HAVE_SERVER_SUPPORT
-        class_unregister_type(LUSTRE_ECHO_NAME);
-        echo_persistent_pages_fini();
-# endif
+#ifdef HAVE_SERVER_SUPPORT
+       class_unregister_type(LUSTRE_ECHO_NAME);
+       echo_persistent_pages_fini();
+#endif
 }
 
-MODULE_AUTHOR("Sun Microsystems, Inc. <http://www.lustre.org/>");
-MODULE_DESCRIPTION("Lustre Testing Echo OBD driver");
+MODULE_AUTHOR("OpenSFS, Inc. <http://www.lustre.org/>");
+MODULE_DESCRIPTION("Lustre Echo Client test driver");
 MODULE_VERSION(LUSTRE_VERSION_STRING);
 MODULE_LICENSE("GPL");